ecslog

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 12, 2026 License: MIT Imports: 11 Imported by: 0

README

ECSLog: Slog Handler for ECS logging

godoc license

This slog handler makes structured ECS logging easier with slog by accepting the dot notation (event.action) and producing corresponding nested JSON objects. This approach eliminates the need for nested grouping when constructing the log attributes.

For details see documentation.

Features:
  • deduplication of scalar attributes
  • downstream log instances can set attribute in "group" created upstream
  • it has comparable performance to slog.NewJSONHandler()
Performance

The implementation has very similar performance to slog.NewJSONHandler(), based on some local testing with zap benchmark.

Example

logger := slog.New(ecslog.NewHandler(os.Stderr)).
    With(
        slog.String("event.dataset", "testing"),
        slog.String("log.logger", "slog"),
    )
// ...
logger.Info("Test!",
    slog.String("event.action", "test"),
)
// {
//   "@timestamp": "2026-01-11T17:00:42.42648+01:00",
//   "message": "Test!",
//   "log": {
//     "logger": "slog",
//     "level": "INFO"
//   },
//   "event": {
//     "dataset": "testing",
//     "action": "test"
//   }
// }

Documentation

Overview

Package ecslog provides slog compatible handler which produces structured JSON.

Conceptually it is very similar with slog.JSONHandler, but provides one key functionality. It accepts dot delimited attribute keys and outputs records with appropriate nested JSON objects.

slog.String("event.action": "test") -> {"event":{"action": "test"}}

This approach can be convenient for Elastic Common Schema (ECS), which includes fair amount of nested JSON objects, and fields are usually described using dot notation ("event.action"). Compared to constructing log records with slog.Group, it may be more convenient, but crucially, it allows specifying nested object content in multiple assignments.

datasetLog := log.WithAttrs(slog.String("event.dataset", "audit"))
datasetLog.Info(slog.String("event.action", "test"))

Another advantage of this implementation is deduplication. Specifying single field multiple times will produce deduplicated JSON record, which contains "last" value.

datasetLog := log.WithAttrs(slog.String("event.action", "audit"))
datasetLog.Info(slog.String("event.action", "test"))
// {"event": {"action": "test"}, ...}

Limitations

The approach with dot notation, combined with aim to be as fast as slog.JSONHandler leads to some limitations. They are mainly related to the use of slog.Group. While the handler is compatible with this attribute kind and will handle it as expected most of the time, sometimes it may differ.

  • Specifying both `slog.Group("event", ...)` and `slog.String("event.log", ...)` will always ignore the attributes nested with the dot notation, outputing only the group.
  • Multiple slog.Group with same keys are not merged, the last[1] one is used.
  • Attribute slog.Group with empty key is not inlined. This rule is outlines by slog.Handler, but due to how groups are implemented, it is not possible without some sacrifices on the way.
Example
package main

import (
	"log/slog"
	"os"

	"github.com/oidq/ecslog"
)

func main() {
	logger := slog.New(ecslog.NewHandler(os.Stdout, ecslog.WithTimestamp(false))).
		With(
			slog.String("event.dataset", "testing"),
		)
	// ...
	logger.Info("Test!",
		slog.String("event.action", "test"),
	)
}
Output:
{"message":"Test!","log":{"level":"INFO"},"event":{"dataset":"testing","action":"test"}}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler is slog.Handler which produces JSON structured log output to given io.Writer.

See package documentation for details about record processing.

func NewHandler

func NewHandler(writer io.Writer, options ...Option) *Handler

NewHandler creates a new slog.Handler instance with given options.

func (*Handler) Enabled

func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool

Enabled controls log output. It is called by slog package.

func (*Handler) Handle

func (h *Handler) Handle(ctx context.Context, record slog.Record) error

Handle processes slog.Record and writes structured JSON line to given io.Writer. For details about the behavior regarding attributes see Handler.

It is called by slog package.

func (*Handler) WithAttrs

func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs creates new slog.Handler with given default attributes. It is called by slog package.

func (*Handler) WithGroup

func (h *Handler) WithGroup(name string) slog.Handler

WithGroup creates new slog.Handler with given default group (see slog.Handler interface) It is called by slog package.

type LogLevelFunc

type LogLevelFunc func(ctx context.Context, level slog.Level) bool

LogLevelFunc is used in WithLogLevelFunc to control which log entry is going to be logged. For basic control based on slog.Level use WithLogLevel().

type Option

type Option func(*handlerOptions)

func WithLogLevel

func WithLogLevel(minLevel slog.Level) Option

WithLogLevel options sets minimum log level to be outputted. Default value is slog.LevelInfo.

This option is exclusive with WithLogLevelFunc.

func WithLogLevelFunc

func WithLogLevelFunc(logLevelF LogLevelFunc) Option

WithLogLevelFunc options sets log level decision function. Given function will be called for each log entry and if it returns false, no log will be produced. Context passed to this function comes from slog.

This option is exclusive with WithLogLevel.

func WithSource

func WithSource(addSource bool) Option

WithSource option can be used to add log source information to logs.

Keys "log.origin.function", "log.origin.file" and "log.origin.line" will filled by information received from log/slog.

func WithTimestamp

func WithTimestamp(showTimestamp bool) Option

WithTimestamp option can be used to disable timestamp ("@timestamp" field) in generated logs.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL