Skip to content

desertwitch/slog-seq

 
 

Repository files navigation

Note

This is a fork of sokkalf/slog-seq with my changes and patches.
Feel free to use it, or the upstream, whichever suits your use case best.
It is maintained for my personal needs and not to compete with upstream.

Release Go Version Go Reference Go Report License
Codecov Lint Tests

slog-seq

slog-seq is a library for sending logs to a Seq server, as a handler for Go's structured logging slog.

Installation

go get github.com/desertwitch/slog-seq

For OpenTelemetry trace correlation and span forwarding:

go get github.com/desertwitch/slog-seq/seqotel

Quick Start

Handlers can be constructed through NewSeqHandler.

Once all work is done, handler.Close() must be called on the returned handler.

import (
	slogseq "github.com/desertwitch/slog-seq"
)

handler := slogseq.NewSeqHandler("http://your-seq-server/ingest/clef",
	slogseq.WithAPIKey("your-api-key"),
	slogseq.WithBatchSize(50),
	slogseq.WithFlushInterval(2*time.Second),
)
defer handler.Close()

slog.SetDefault(slog.New(handler))
slog.Info("Hello, world!")

You can also define your own slog.HandlerOptions struct:

opts := &slog.HandlerOptions{
	Level:     slog.LevelInfo,  // Minimum log level
	AddSource: true,            // Show source file, line and function in log
	ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
		if a.Key == "password" {
			// Mask passwords
			a.Value = slog.StringValue("*****")
		}
		if a.Key == slog.SourceKey {
			// Replace the full path with just the file name
			s := a.Value.Any().(*slog.Source)
			s.File = path.Base(s.File)
		}
		return a
	},
}

and pass it to the NewSeqHandler function with slogseq.WithHandlerOptions(opts).

See the package documentation for all available options.

HTTP Client

If you need to disable TLS certificate verification, you can do so by using the option slogseq.WithInsecure().

Alternatively, you can provide your own HTTP client by using the option slogseq.WithHTTPClient(client).

Multiple Workers

You can set the number of workers that send logs to the Seq server by using the option slogseq.WithWorkers(n).

This can be useful if you have a high enough volume of logs to cause dropped messages.

OpenTelemetry

The seqotel sub-package provides OpenTelemetry trace correlation and span forwarding. It has its own Go module, so the dependency is only pulled into your project if you use it.

seqotel.NewSeqOTelHandler works like slogseq.NewSeqHandler but automatically enriches log events with trace and span IDs from the active span context, if one is provided.

seqotel.NewLoggingSpanProcessor constructs a seqotel.LoggingSpanProcessor implementing trace.SpanProcessor and trace.SpanExporter to forward completed spans to Seq as CLEF events.

import (
	slogseq "github.com/desertwitch/slog-seq"
	"github.com/desertwitch/slog-seq/seqotel"
)

handler := seqotel.NewSeqOTelHandler("http://your-seq-server/ingest/clef",
	slogseq.WithAPIKey("your-api-key"),
	slogseq.WithBatchSize(50),
	slogseq.WithFlushInterval(2*time.Second),
)
defer handler.Close()

slog.SetDefault(slog.New(handler))

processor := seqotel.NewLoggingSpanProcessor(handler)
tp := trace.NewTracerProvider(
	trace.WithSpanProcessor(processor),
	trace.WithSampler(trace.AlwaysSample()),
)

tracer := tp.Tracer("example-tracer")

ctx, span := tracer.Start(context.Background(), "operation")
span.AddEvent("Starting work")
time.Sleep(500 * time.Millisecond)

slog.InfoContext(ctx, "This is a span log message", "key", "value")

_, subSpan := tracer.Start(ctx, "sub operation")
subSpan.AddEvent("Sub operation started")
time.Sleep(500 * time.Millisecond)
subSpan.AddEvent("Sub operation completed",
	tr.WithAttributes(attribute.String("key", "value")),
)
subSpan.End()

span.AddEvent("Work done")
slog.InfoContext(ctx, "All done!")
span.End()

Seq with traces

See the package documentation for more information.

Custom Integrations

SeqHandler.HandleCLEFEvent accepts pre-built CLEF events directly, bypassing slog entirely. This lets you use the handler's batching, retry, and worker machinery from any event source - syslog forwarders, custom protocols, queue consumers, or anything that can produce a CLEFEvent.

import (
	slogseq "github.com/desertwitch/slog-seq"
)

handler := slogseq.NewSeqHandler("http://your-seq-server/ingest/clef",
    slogseq.WithAPIKey("your-api-key"),
    slogseq.WithBatchSize(50),
)
defer handler.Close()

handler.HandleCLEFEvent(slogseq.CLEFEvent{
    Timestamp: time.Now(),
    Message:   "forwarded from syslog",
    Level:     slogseq.CLEFLevelInformation,
    Properties: map[string]any{
        "facility": "kern",
        "hostname": "router-01",
    },
})

Benchmarks

Benchmarks measure the hot path (log call through channel send).
HTTP delivery is asynchronous and batched, so it does not block the caller.

Measured using: Go 1.26, Intel Core i5-12600K, 8-core VM.

Benchmark ns/op B/op allocs/op
Handle 90 162 2
Handle (parallel) 189 447 6
Handle + WithAttrs 386 775 12
Handle + WithGroups 447 1021 10
Handle + AddSource 426 996 9
Handle + ReplaceAttr 348 720 10
HandleCLEFEvent1 30 35 0

1: Raw event dispatch after Handle preprocessing or directly by OTel.

Run make benchmark for full results.

License

MIT

About

slog-seq is a slog handler for sending logs to Seq, with optional OpenTelemetry tracing support.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Go 99.1%
  • Makefile 0.9%