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.
slog-seq is a library for sending logs to a Seq server, as a handler for Go's structured logging slog.
- Installation
- Quick Start
- HTTP Client
- Multiple Workers
- OpenTelemetry
- Custom Integrations
- Benchmarks
- License
go get github.com/desertwitch/slog-seqFor OpenTelemetry trace correlation and span forwarding:
go get github.com/desertwitch/slog-seq/seqotelHandlers 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.
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).
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.
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()See the package documentation for more information.
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 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.
MIT
