slogbuffer

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2024 License: MIT Imports: 6 Imported by: 0

README

slogbuffer

slogbuffer implements slog.Handler that buffers log records until real handler is available.

Use cases

This handler is useful in multiple situations. Some of them are:

  • Application wants to dynamically create handler based on config file (e.g. config file defines where logs should go, which level should be logged, etc). However, in order to load config file some code has to be executed and it might produce logs. These logs can be buffered using slogbuffer until real handler is known.
  • Application might want to remove some attributes or change keys for attributes using ReplaceAttrs option for stdlib handlers, but it might want to do it dynamically, based on external documentation. Until that config is known, logs can be buffered.
  • CLI applications might allow logging config via command line flags. Parsing flags might produce log lines, so buffering those might be useful.
  • Application might want to produce logs to file or stderr only in case of error or panic. However, once panic happens not all context might be available. Using slogbuffer allows logging normally and flushing all accumulated logs (or only subset of latest log records) once condition is met.
  • Of course, this is useful in any subset of program, not just entire application. E.g. HTTP handler can accept logger and log stuff, but level above (e.g. middleware) might choose to flush those records only if non-200 status code is returned.
  • Streaming logs over network in case when log sink still not ready is use case when buffering can se be useful with flushing all records once sink becomes available.

Usage

In order to use this handler, just create new slog.Logger using slog.New and provide instance of slogbuffer.BufferLogHandler. BufferLogHandler can be created in two ways, using:

  • NewBufferLogHandler(slog.Level) - creates unbound buffer, all log records will be stored. This is useful, but in case where a lot of lot messages can be produces can consume too much memory.
  • NewBoundBufferLogHandler(slog.Level, maxRecords int) creates bound buffer. It can store at most maxRecords of log records. When new ones are created, oldest ones added are removed.

After real handler is known and created, SetRealHandler(context.Context, slog.Handler) method should be called. At this point, all buffered log records are flushed to provided real logger and from that point on BufferLogHandler behaves as simple proxy to real handler, which means that any logger that already has instance of BufferLogHandler will continue working as if real handler was used from the start.

Contribution

While this was created to scratch personal itch (CLI application that allows user to configure logging), contributions are welcome via PRs.

Author(s)

Documentation

Overview

Package slogbuffer provides slog.Handler implementation that is capable of buffering log records until controlling applications flushes them to real handler.

This can be helpful in situations like:

  • configuration of real handler is not known until later (e.g. CLI applications that provide user with flags to control log output)
  • logs are streamed over network and sink is still not ready to accept log records
  • logs should be flushed only if some condition is met (e.g. panic)

This handler handles those cases but buffering log records and re-emits them on command by implementing slog.Handler that stores logs in memory until it receives Handler to wrap, after which it is just a simple wrapping handler.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BufferLogHandler

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

BufferLogHandler is pkg/log/slog.Handler that buffers records in memory until real log handler is provided, at which point it drains memory buffer and uses real handler from that point. Zero value is useful.

Example
package main

import (
	"context"
	"github.com/delicb/slogbuffer"
	"log/slog"
	"os"
)

func main() {
	// buffer log message up to 256 messages, drop oldest one is more messages are logged
	h := slogbuffer.NewBoundBufferLogHandler(slog.LevelInfo, 256)
	log := slog.New(h)

	// use logger normally
	log.Info("some message", slog.Int("key", 11))
	log.Debug("discarded message")
	log.Warn("warn message")

	// read config, config file, whatever to figure out how real handler looks like
	realHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
		ReplaceAttr: func(groups []string, attr slog.Attr) slog.Attr {
			if attr.Key == slog.TimeKey { // remove time to have constant output
				return slog.Attr{}
			}
			return attr
		},
	})

	// after setup is complete, we know the config of real handler
	err := h.SetRealHandler(context.Background(), realHandler)
	if err != nil {
		panic("setting real handler")
	}

	// from this point on, log records are not buffered, they are just passed to real handler
	log.Info("direct logging")

}
Output:

level=INFO msg="some message" key=11
level=WARN msg="warn message"
level=INFO msg="direct logging"

func NewBoundBufferLogHandler

func NewBoundBufferLogHandler(level slog.Level, maxRecords int) *BufferLogHandler

NewBoundBufferLogHandler creates instance of log handler that stores log records with upper limit on number of records, thus providing some level of memory consumption control.

func NewBufferLogHandler

func NewBufferLogHandler(level slog.Level) *BufferLogHandler

NewBufferLogHandler returns unbound instance of log handler that stores log records until such time when SetRealHandler is called, at which point messages get flushed and all subsequent calls are just proxy calls to real handler.

func (*BufferLogHandler) Discard

func (h *BufferLogHandler) Discard()

Discard removers all stored records.

func (*BufferLogHandler) Enabled

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

func (*BufferLogHandler) Handle

func (h *BufferLogHandler) Handle(ctx context.Context, r slog.Record) error

func (*BufferLogHandler) SetRealHandler

func (h *BufferLogHandler) SetRealHandler(ctx context.Context, real slog.Handler) error

SetRealHandler set real slog.Handler for this buffer handler. This will cause all buffered log records to be emitted to provided handler. Also, from this point on, current handler behaves as simple wrapper and all handling is passed to real handler (thus this instance is still usable, in case reference to it is held somewhere).

func (*BufferLogHandler) WithAttrs

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

func (*BufferLogHandler) WithGroup

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

Jump to

Keyboard shortcuts

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