package
0.0.2
Repository: https://github.com/osbuild/logging.git
Documentation: pkg.go.dev

# README

strc

A simple tracing library. When OpenTelemetry is a bit too much. Features:

  • Simple code instrumentation.
  • Serialization into log/slog (structured logging library).
  • Handler with multiple sub-handlers for further processing.
  • Simple exporting slog handler for callback-based exporters.
  • Adding "trace_id" root field to all logs for easy correlation between logs and traces.

Instrumentation

Add the following code to function you want to trace:

span, ctx := strc.Start(ctx, "span name")
defer span.End()

Optionally, additional "save points" can be inserted:

span.Event("an event")

Result

All results are stored in log/slog records. Each span creates one record with group named span with the following data:

  • span.name: span name
  • span.id: span ID
  • span.parent: parent ID or 0000000 when there is no parent
  • span.trace: trace ID
  • span.event: event name (only on event)
  • span.at: duration within a span (only on event)
  • span.duration: trace duration (only when span ends)
  • span.time: log time (can be enabled in exporter)

Spans end up in log sink too, for better readability, the following fields are added to the root namespace:

  • msg: message in the form span XXX started or event XXX
  • trace_id - trace ID (disable by setting strc.TraceIDFieldKey to empty string)
  • build_id - build Git sha (disable by setting strc.BuildIDFieldKey to empty string)

Propagation

A simple HTTP header-based propagation API is available. Note this is not meant to be used directly, there is HTTP middleware and client wrapper available:

// create new trace id
id := strc.NewTraceID()

// create a new context with trace id value
ctx := strc.WithContext(context.Background(), id)

// fetch the id from the context
id := strc.TraceIDFromContext(ctx)

// add the id from context to a HTTP request
strc.AddTraceIDHeader(ctx, request)

// fetch the id from a request
id := TraceIDFromRequest(request)

Middleware

A standard library HTTP middleware is provided for trace propagation

middleware := strc.NewMiddleware(logger)
mux := http.NewServeMux()
mux.Handle("/", middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	// ...
})))

The library does not provide native Echo middleware, but a standard middleware can be easily used from Echo:

middleware := strc.NewMiddleware(logger)
e.Use(echo.WrapMiddleware(middleware))

The strc.Middeware can be optionally configured to log detailed debug information like request or reply HTTP headers or even full body. This is turned off by default, see strc.MiddlewareConfig for more info.

HTTP client

A TracingDoer type can be used to decorate HTTP clients adding necessary propagation automatically as long as tracing information is in the request context:

r, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://home.zapletalovi.com/", nil)
doer := strc.NewTracingDoer(http.DefaultClient)
doer.Do(r)

The TracingDoer can be optionally configured to log detailed debug information like request or reply HTTP headers or even full body. This is turned off by default, see strc.TracingDoerConfig for more info.

Example headers generated or parsed by HTTP client & middleware code:

X-Strc-Trace-ID: LOlIxiHprrrvHqD
X-Strc-Span-ID: VIPEcES.yuufaHI

Full example

package main

import (
	"context"
	"log/slog"

	"github.com/osbuild/logging/pkg/strc"
)

func subProcess(ctx context.Context) {
	span, ctx := strc.Start(ctx, "subProcess")
	defer span.End()

	span.Event("an event")
}

func process(ctx context.Context) {
	span, ctx := strc.Start(ctx, "process")
	defer span.End()

	subProcess(ctx)
}

func main() {
	span, ctx := strc.Start(context.Background(), "main")
	defer span.End()

	process(ctx)
}

Note the above example will print nothing as tracing is disabled by default, two things must be done. First, a slog destination logger must be set:

strc.SetLogger(slog.Default())

Second, the destination logger must have debug level handing enabled. The default logging level can be increased if needed:

strc.Level = slog.LevelInfo

Run the example with the following command:

go run github.com/osbuild/logging/internal/example_print/

Which prints something like (removed time and log level for readability):

span main started span.name=main span.id=pEnFDti span.parent=0000000 span.trace=SVyjVloJYogpNPq span.source=main.go:28
span process started span.name=process span.id=fRfWksO span.parent=pEnFDti span.trace=SVyjVloJYogpNPq span.source=main.go:18
span subProcess started span.name=subProcess span.id=gSouhiv span.parent=fRfWksO span.trace=SVyjVloJYogpNPq span.source=main.go:11
span subProcess event an event span.name=subProcess span.id=gSouhiv span.parent=fRfWksO span.trace=SVyjVloJYogpNPq span.event="an event" span.at=21.644µs span.source=main.go:14
span subProcess finished in 47.355µs span.name=subProcess span.id=gSouhiv span.parent=fRfWksO span.trace=SVyjVloJYogpNPq span.dur=47.355µs span.source=main.go:15
span process finished in 94.405µs span.name=process span.id=fRfWksO span.parent=pEnFDti span.trace=SVyjVloJYogpNPq span.dur=94.405µs span.source=main.go:22
span main finished in 285.246µs span.name=main span.id=pEnFDti span.parent=0000000 span.trace=SVyjVloJYogpNPq span.dur=285.246µs span.source=main.go:32

Exporting data

While the main goal of this library is just instrumenting and sending data into slog, a simple function callback exporter handler is provided by the package for quick collecting or exporting capabilities as well as multi-handler for chaining handlers which is useful to keep sending standard logging data to logging systems. It uses slog.Attr type as the data carrier:

package main

import (
	"context"
	"log/slog"
	"os"

	"github.com/osbuild/logging/pkg/strc"
)

func exportFunc(ctx context.Context, attrs []slog.Attr) {
	for _, attr := range attrs {
		println("exporting trace data", attr.Key, attr.Value.String())
	}
}

func main() {
	textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})
	exportHandler := strc.NewExportHandler(exportFunc)
	multiHandler := strc.NewMultiHandler(textHandler, exportHandler)

	logger := slog.New(multiHandler)
	slog.SetDefault(logger)
	strc.SetLogger(logger)

	span, _ := strc.Start(context.Background(), "main")
	defer span.End()
}

There is additional NewMultiHandlerCustom which allows adding custom attributes from context via a callback function. This is useful when additional correlation id (e.g. background job UUID) needs to be added to every single regular log record. The multi-handler creates the following new keys in the root element:

  • trace_id - trace ID (disable by setting strc.TraceIDFieldKey to empty string)
  • build_id - build Git sha (disable by setting strc.BuildIDFieldKey to empty string)

Run the example with the following command:

go run github.com/osbuild/logging/internal/example_export/

For the best performance, we a dedicated exporting handler should be written customized to the output format. For more info, see writing an slog handler.

# Packages

Simple tracing graph processor Package stgraph provides a simple tracing graph processor that can be used to generate flamegraphs from structured logs.

# Functions

Accept returns a filter that accepts requests that pass the given filter.
AcceptHost returns a filter that accepts requests with the given hosts.
AcceptHostContains returns a filter that accepts requests with hosts that contain the given regular expression.
AcceptMethod returns a filter that accepts requests with the given methods.
AcceptPath returns a filter that accepts requests with the given paths.
AcceptPathContains returns a filter that accepts requests with paths that contain the given parts.
AcceptPathMatch returns a filter that accepts requests with paths that match the given regular expressions.
AcceptPathPrefix returns a filter that accepts requests with paths that have the given prefixes.
AcceptPathSuffix returns a filter that accepts requests with paths that have the given suffixes.
AcceptStatus returns a filter that accepts requests with the given status codes.
AddSpanIDHeader adds span ID from context to a request header.
AddTraceIDHeader adds trace ID from context to a request header.
No description provided by the author
HeadfieldPairCallback is a slog callback that extracts values from the context and adds them to the attributes.
HeadfieldPairMiddleware is a middleware that extracts values from headers and stores them in the context.
Ignore returns a filter that ignores requests that pass the given filter.
IgnoreHost returns a filter that ignores requests with the given hosts.
IgnoreHostContains returns a filter that ignores requests with hosts that contain the given regular expression.
IgnoreMethod returns a filter that ignores requests with the given methods.
IgnorePath returns a filter that ignores requests with the given paths.
IgnorePathContains returns a filter that ignores requests with paths that contain the given parts.
IgnorePathMatch returns a filter that ignores requests with paths that match the given regular expressions.
IgnorePathPrefix returns a filter that ignores requests with paths that have the given prefixes.
IgnorePathSuffix returns a filter that ignores requests with paths that have the given suffixes.
IgnoreStatus returns a filter that ignores requests with the given status codes.
IncludeTime is an ExporterOption that includes the time in the exported attributes.
No description provided by the author
NewExportHandler creates a new Exporter with the given callback function and options.
NewMiddleware returns a `func(http.Handler) http.Handler` (middleware) that logs requests using slog.
NewMiddlewareWithConfig returns a `func(http.Handler) http.Handler` (middleware) that logs requests using slog.
NewMiddlewareWithFilters returns a `func(http.Handler) http.Handler` (middleware) that logs requests using slog.
NewMultiHandler distributes records to multiple slog.Handler.
NewMultiHandlerCustom distributes records to multiple slog.Handler with custom attributes and callback.
NewSpanID generates a new span ID.
NewTraceID generates a new random trace ID.
NewTracingDoer returns a new TracingDoer.
No description provided by the author
RecoverPanicMiddleware is a middleware that recovers from panics and logs them using slog as errors with status code 500.
No description provided by the author
No description provided by the author
Start returns span ID from a context.
SpanIDFromRequest returns span ID from a request.
No description provided by the author
Start returns trace ID from a context.
TraceIDFromRequest returns trace ID from a request.
No description provided by the author
Start returns a new context with span ID.
Start returns a new context with trace ID.

# Constants

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Variables

BuildIDFieldKey is the key used to store the git commit in the log record by MultiHandler.
No description provided by the author
No description provided by the author
No description provided by the author
ParentIDName is the key name used for trace ID.
64KB.
64KB.
SkipSource is a flag that disables source logging.
SpanGroupName is the group name used for span attributes.
No description provided by the author
SpanIDName is the key name used for trace ID.
TraceIDFieldKey is the key used to store the trace ID in the log record by MultiHandler.
No description provided by the author
TraceIDName is the key name used for trace ID.

# Structs

DoerErr is a simple wrapped error without any message.
ExportHandler is an slog.Handler which provides a callback function that allows for exporting attributes (slog.Attr) to a different system.
HeadfieldPair is a pair of header name and field name.
No description provided by the author
MultiHandler distributes records to multiple slog.Handler.
NoopHandler does nothing.
No description provided by the author
TracingDoer is a http client doer that adds tracing to the request and response.
No description provided by the author

# Interfaces

No description provided by the author
No description provided by the author

# Type aliases

No description provided by the author
Filter is a function that determines whether a request should be logged or not.
No description provided by the author
SpanID is a unique identifier for a trace.
TraceID is a unique identifier for a trace.