Skip to content

Go library

Version

The Go library and SLO plugins are available in Sloth >= v0.15.0.

Introduction

Sloth has traditionally been used as a CLI application or a Kubernetes operator. However, some use cases require custom SLO generation, for example, when existing SLO plugins are not enough, or when the SLO workflow needs tight integration with other systems.

The Sloth Go library lets you use Sloth’s core logic directly in your Go applications. It includes everything that the main Sloth app supports, such as:

  • SLO and SLI plugins.
  • Multiple SLO specification formats.
  • Helpers to export results in different formats.

The library is designed to have a simple, high-level API, so you can leverage Sloth’s internal logic without dealing with complex or low-level details.

Components

The library has two main building blocks:

  • SLO Generator: Generates SLOs and returns a structured in-memory representation (domain model) result.
  • Storage Helpers: Take the generated domain objects and export them into different formats or backends.

This separation allows you to automate SLO generation, customize the pipeline between generation and storage, or even implement your own storage layer.

Go Docs

Full Go API reference is available at https://pkg.go.dev/github.com/slok/sloth/pkg/lib

Examples

Live Editor

A proof of concept showing Sloth running fully in the browser using WASM.
This example uses the Go library to generate SLOs live on the client side.

REST API

Example of exposing the Go library through a simple HTTP API to generate SLOs remotely.

package main

import (
    "fmt"
    "io"
    "net/http"

    sloth "github.com/slok/sloth/pkg/lib"
)

// This example shows a basic usage of sloth library by exposing sloth SLO generation functionality as a rest HTTP API.
// Check with `curl -XPOST http://127.0.0.1:8080/sloth/generate -d "$(cat ./examples/getting-started.yml)"`.
func main() {
    gen, err := sloth.NewPrometheusSLOGenerator(sloth.PrometheusSLOGeneratorConfig{})
    if err != nil {
        panic(fmt.Errorf("could not create SLO generator: %w", err))
    }

    mux := http.NewServeMux()
    mux.HandleFunc("POST /sloth/generate", func(w http.ResponseWriter, r *http.Request) {
        // Get body from request.
        body, err := io.ReadAll(r.Body)
        if err != nil {
            http.Error(w, "failed to read request body", http.StatusBadRequest)
            return
        }
        defer r.Body.Close()

        // Generate SLOs.
        result, err := gen.GenerateFromRaw(r.Context(), body)
        if err != nil {
            http.Error(w, fmt.Sprintf("could not generate SLOs: %v", err), http.StatusInternalServerError)
            return
        }
        w.WriteHeader(http.StatusOK)
        err = sloth.WriteResultAsPrometheusStd(r.Context(), *result, w)
        if err != nil {
            http.Error(w, fmt.Sprintf("could not write result: %v", err), http.StatusInternalServerError)
            return
        }
    })

    httpServer := &http.Server{Addr: ":8080", Handler: mux}

    fmt.Println("Starting server at :8080")

    err = httpServer.ListenAndServe()
    if err != nil {
        panic(err)
    }
}

Remote SLO plugins

Example showing how to load and use remote SLO plugins within your Go application.

package main

import (
    "context"
    "fmt"
    "io"
    "io/fs"
    "net/http"
    "os"
    "testing/fstest"

    sloth "github.com/slok/sloth/pkg/lib"
)

const (
    remotePluginURL = "https://gist.githubusercontent.com/slok/9408924d844049b675a8da9185422b40/raw/e36b7dd881f3465cb568db3edf5afb0d38b5427f/sloth-slo-plugin.go"
)

const sloSpec = `---
version: "prometheus/v1"
service: "myservice"
slo_plugins:
  chain:
  - id: "sloth-examples/add_description_to_info/v1"
slos:
  - name: "requests-availability"
    objective: 99.9
    description: "Common SLO based on availability for HTTP request responses."
    sli:
      events:
        error_query: sum(rate(http_request_duration_seconds_count{job="myservice",code=~"(5..|429)"}[{{.window}}]))
        total_query: sum(rate(http_request_duration_seconds_count{job="myservice"}[{{.window}}]))
    alerting:
      page_alert:
        disable: true
      ticket_alert:
        disable: true
`

func main() {
    ctx := context.Background()

    // Download the remote plugin.
    resp, err := http.Get(remotePluginURL)
    if err != nil {
        panic(err)
    }
    if resp.StatusCode != http.StatusOK {
        panic("failed to download remote plugin")
    }

    pluginB, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    // Load the downloaded plugin in an in-memory fs.
    f := fstest.MapFS{}
    f["remote-example/plugin.go"] = &fstest.MapFile{Data: pluginB}

    gen, err := sloth.NewPrometheusSLOGenerator(sloth.PrometheusSLOGeneratorConfig{PluginsFS: []fs.FS{f}})
    if err != nil {
        panic(fmt.Errorf("could not create SLO generator: %w", err))
    }

    // Generate SLOs.
    result, err := gen.GenerateFromRaw(ctx, []byte(sloSpec))
    if err != nil {
        panic(fmt.Sprintf("could not generate SLOs: %v", err))
    }
    err = sloth.WriteResultAsPrometheusStd(ctx, *result, os.Stdout)
    if err != nil {
        panic(fmt.Sprintf("could not write result: %v", err))
    }
}