log

package
v1.34.0 Latest Latest
Warning

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

Go to latest
Published: Dec 9, 2025 License: MIT Imports: 4 Imported by: 0

README

Log Package

The log package acts as a helpful abstraction around the slog Logger that is built into the standard library in Go.

Engineers in GitLab must use this package in order to instantiate loggers for their application to ensure that there is consistency across all of our services in how we emit logs from our systems.

Usage

// returns a new JSON logger that outputs logs to the stdout
logger := log.New()

// A standard Info line
// Note: We strongly encourage that you use the *Context methods
// for observability purposes to ensure you'll be benefiting from
// field enrichment.
logger.InfoContext(ctx, "some info")
Logger Configuration
logger := log.New(
    // log.WithWriter - allows you to pass in a custom io.Writer
    // should you wish.
    log.WithWriter(os.Stderr),

    // allows you fine-grained control over how the
    // slog handler is configured.
    log.WithHandlerOptions(&slog.HandlerOptions{
        AddSource: true,
        Level: slog.LevelDebug,
    }),

    // allows you to set the output to text format
    log.WithTextFormat(),
)
Writing to Files

If you need to write to specific files, you can achieve this like so:

f, err := os.Create("test.out")
assert.Nil(t, err)
logger := log.New(log.WithWriter(f))
logger.Info("hello")

This would create a test.out file into which your log messages would be pushed.

Testing Your Observability

The logs that our systems output represent an often-neglected part of our API. Additional reporting systems and alerts are typically built on top of log lines and a lack of testing makes these setups rather fragile in nature.

It's strongly encouraged that all engineers bake in some form of assertions on the logs that they rely on for additional observability configuration within their tests.

// NewWithRecorder returns a logRecorder struct that
// captures all log lines emitted by the `logger`
logger, logRecorder := log.NewWithRecorder()

// We can then perform assertions on things like how many log lines
// have been emitted
assert.Len(t, recorder.Records, 1)

// As well as the shape of individual log lines
assert.Equal(t, "test message", recorder.Records[0].Message)
assert.Equal(t, tt.expectedLevel, recorder.Records[0].Level.String())
assert.Contains(t, recorder.Records[0].Attrs, slog.Attr{Key: "key", Value: slog.AnyValue("value")})

These log lines are captured in a Records field which is a slice of type testRecord:

// testRecord - a representation of a single log message
type testRecord struct {
    Level   slog.Level
    Message string
    Attrs   map[string]any
}

Context Logger

There is some important information we need to emit for every log message whenever a request is processed. This could be things such as the pipeline_id or the GitLab project ID.

You can store this information within the context and it will automatically enrich subsequent log emissions with these fields. This is super handy if you need to correlate log lines together by various fields for investigatory purposes.

ctx := log.WithFields(
    slog.String("project_id", "1234abc")
    slog.String("some_field", "abc123")
)

logger.InfoContext(ctx, "hello world")
// emitted log line will contain the `project_id` and `some_field` fields alongside
// "hello world"
Canonical Logger

Canonical logging is a technique that can be used to help reduce the volume of logs being emitted from your systems. You are effectively aggregating all of the fields that you care about through the lifecycle of a request and then logging a single line at the end of the lifecycle.

// pass this logger along the request lifecycle
ctx := context.Background()
ctx = log.WithFields(ctx, slog.String("step", "1"))

// at the point at which you need to emit the
// final log line which will then include all of the
// fields that have been collected within the
// context up until this point.
logger.InfoContext(ctx, "canonical_log")

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EnrichRecordWithMetaData

func EnrichRecordWithMetaData(ctx context.Context, r slog.Record) slog.Record

func Fields

func Fields(ctx context.Context) []slog.Attr

func New

func New(opts ...Option) *slog.Logger

New - a handy wrapper that configures the slog.Logger in a consistent fashion. Engineers should always default to using this constructor to ensure that they can take advantage of future global enhancements to our logging setup.

func NewContextHandler

func NewContextHandler(baseHandler slog.Handler) slog.Handler

func WithFields

func WithFields(ctx context.Context, attrs ...slog.Attr) context.Context

WithField - provides a way to inject more fields into a logger that is then persisted in the context. This is useful in situations where they need to employ canonical logging in order to limit the number of log lines that they emit.

Types

type Config

type Config struct {
	Writer        io.Writer
	HandlerOpts   *slog.HandlerOptions
	UseTextFormat bool
	BaseHandler   slog.Handler
}

Config holds the configuration for creating a new logger.

type ContextHandler

type ContextHandler struct {
	BaseHandler slog.Handler
}

func (*ContextHandler) Enabled

func (c *ContextHandler) Enabled(ctx context.Context, level slog.Level) bool

func (*ContextHandler) Handle

func (c *ContextHandler) Handle(ctx context.Context, r slog.Record) error

func (*ContextHandler) WithAttrs

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

func (*ContextHandler) WithGroup

func (c *ContextHandler) WithGroup(name string) slog.Handler

type Option

type Option func(*Config)

Option is a function that modifies the logger configuration.

func WithBaseHandler

func WithBaseHandler(handler slog.Handler) Option

WithBaseHandler allows providing a custom base handler This bypasses the default JSON/Text handler creation.

func WithHandlerOptions

func WithHandlerOptions(opts *slog.HandlerOptions) Option

WithHandlerOptions sets the handler options for the logger.

func WithTextFormat

func WithTextFormat() Option

WithTextFormat configures the logger to use text format instead of JSON.

func WithWriter

func WithWriter(w io.Writer) Option

WithWriter sets the output writer for the logger.

Jump to

Keyboard shortcuts

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