logging

package
v1.0.4 Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2024 License: Apache-2.0 Imports: 15 Imported by: 58

Documentation

Overview

Package logging is an opinionated structured logging library based on log/slog.

This package also aliases most top-level functions in log/slog to reduce the need to manage the additional import.

Index

Examples

Constants

View Source
const (
	FormatJSON = Format("JSON")
	FormatText = Format("TEXT")
)
View Source
const (
	LevelDebug     = slog.Level(-4)
	LevelInfo      = slog.Level(0)
	LevelNotice    = slog.Level(2)
	LevelWarning   = slog.Level(4)
	LevelError     = slog.Level(8)
	LevelEmergency = slog.Level(12)
)

Variables

This section is empty.

Functions

func DefaultLogger added in v0.7.0

func DefaultLogger() *slog.Logger

DefaultLogger creates a default logger.

Example
package main

import (
	"log/slog"

	"github.com/abcxyz/pkg/logging"
)

var logger *slog.Logger

func main() {
	logger = logging.DefaultLogger()
}
Output:

func FormatNames added in v0.7.0

func FormatNames() []string

FormatNames returns the list of all log format names.

func FromContext

func FromContext(ctx context.Context) *slog.Logger

FromContext returns the logger stored in the context. If no such logger exists, a default logger is returned.

Example
package main

import (
	"context"
	"log/slog"
	"os"

	"github.com/abcxyz/pkg/logging"
)

var logger *slog.Logger

func main() {
	ctx := context.Background()

	logger = logging.New(os.Stdout, slog.LevelDebug, logging.FormatText, true)
	ctx = logging.WithLogger(ctx, logger)

	logger = logging.FromContext(ctx) // same logger
}
Output:

func GRPCStreamingInterceptor added in v0.1.2

func GRPCStreamingInterceptor(inLogger *slog.Logger, projectID string) grpc.StreamClientInterceptor

GRPCStreamingInterceptor returns client-side a gRPC streaming interceptor that populates a logger with trace data in the context.

func GRPCUnaryInterceptor added in v0.1.2

func GRPCUnaryInterceptor(inLogger *slog.Logger, projectID string) grpc.UnaryServerInterceptor

GRPCUnaryInterceptor returns a client-side gRPC unary interceptor that populates a logger with trace data in the context.

func HTTPInterceptor added in v0.1.2

func HTTPInterceptor(inLogger *slog.Logger, projectID string) func(http.Handler) http.Handler

HTTPInterceptor returns an HTTP middleware that populates a logger with trace data onto the incoming and outgoing http.Request context.

func LevelNames added in v0.7.0

func LevelNames() []string

LevelNames returns the list of all log level names.

func LevelSlogValue added in v0.7.0

func LevelSlogValue(l slog.Level) slog.Value

LevelSlogValue returns the slog.Value representation of the level.

func LevelString added in v0.7.0

func LevelString(l slog.Level) string

LevelString returns the string representation of the given level. Note this is different from calling String() on the Level, which uses the slog implementation.

func LookupLevel added in v0.7.0

func LookupLevel(name string) (slog.Level, error)

LookupLevel attempts to get the level that corresponds to the given name. If no such level exists, it returns an error. If the empty string is given, it returns Info level.

func New added in v0.7.0

func New(w io.Writer, level slog.Level, format Format, debug bool) *slog.Logger

New creates a new logger in the specified format and writes to the provided writer at the provided level. Use the returned leveler to dynamically change the level to a different value after creation.

If debug is true, the logging level is set to the lowest possible value (meaning all messages will be printed), and the output will include source information. This is very expensive, and you should not enable it unless actively debugging.

It returns the configured logger and a leveler which can be used to change the logger's level dynamically. The leveler does not require locking to change the level.

Example
package main

import (
	"bytes"
	"log/slog"

	"github.com/abcxyz/pkg/logging"
)

var logger *slog.Logger

func main() {
	// Write to a buffer instead of stdout
	var b bytes.Buffer
	logger = logging.New(&b, slog.LevelInfo, logging.FormatJSON, false)
}
Output:

func NewFromEnv

func NewFromEnv(envPrefix string) *slog.Logger

NewFromEnv is a convenience function for creating a logger that is configured from the environment. It sources the following environment variables, optionally prefixed with the given prefix:

  • LOG_LEVEL: string representation of the log level. It panics if no such log level exists.
  • LOG_FORMAT: format in which to output logs (e.g. json, text). It panics if no such format exists.
  • LOG_DEBUG: enable the most detailed debug logging. It panics iff the given value is not a valid boolean.
Example
package main

import (
	"log/slog"

	"github.com/abcxyz/pkg/logging"
)

var logger *slog.Logger

func main() {
	logger = logging.NewFromEnv("MY_APP_")
}
Output:

Example (SetLevel)
package main

import (
	"log/slog"

	"github.com/abcxyz/pkg/logging"
)

var logger *slog.Logger

func main() {
	logger = logging.SetLevel(logging.NewFromEnv("MY_APP_"), slog.LevelWarn)
}
Output:

func SetLevel added in v0.7.0

func SetLevel(logger *slog.Logger, level slog.Level) *slog.Logger

SetLevel adjusts the level on the provided logger. The handler on the given logger must be a LevelableHandler or else this function panics. If you created a logger through this package, it will automatically satisfy that interface.

This function is safe for concurrent use.

It returns the provided logger for convenience and easier chaining.

Example
package main

import (
	"log/slog"

	"github.com/abcxyz/pkg/logging"
)

var myLogger = logging.DefaultLogger()

func main() {
	logging.SetLevel(myLogger, slog.LevelDebug) // level is now debug
}
Output:

Example (Safe)
package main

import (
	"log/slog"

	"github.com/abcxyz/pkg/logging"
)

var myLogger = logging.DefaultLogger()

func main() {
	// This example demonstrates the totally safe way to set a level, assuming you
	// don't know if the logger is capable of changing levels dynamically.
	typ, ok := myLogger.Handler().(logging.LevelableHandler)
	if !ok {
		// not capable of setting levels
	}
	typ.SetLevel(slog.LevelDebug) // level is now debug
}
Output:

func TestLogger

func TestLogger(tb testing.TB) *slog.Logger

TestLogger creates a new logger for use in tests. It will only log messages when tests fail and the tests were run with verbose (-v).

Example
_ = func(t *testing.T) { // func TestMyThing(t *testing.T)
	logger = logging.TestLogger(t)
}
Output:

Example (Context)
package main

import (
	"context"
	"testing"

	"github.com/abcxyz/pkg/logging"
)

func main() {
	_ = func(t *testing.T) { // func TestMyThing(t *testing.T)
		// Most tests rely on the logger in the context, so here's a fast way to
		// inject a test logger into the context.
		ctx := logging.WithLogger(context.Background(), logging.TestLogger(t))

		// Use ctx in tests. Anything that extracts a logger from the context will
		// get the test logger now.
		_ = ctx
	}
}
Output:

func WithLogger

func WithLogger(ctx context.Context, logger *slog.Logger) context.Context

WithLogger creates a new context with the provided logger attached.

Example
package main

import (
	"context"
	"log/slog"
	"os"

	"github.com/abcxyz/pkg/logging"
)

var logger *slog.Logger

func main() {
	ctx := context.Background()

	logger = logging.New(os.Stdout, slog.LevelDebug, logging.FormatText, true)
	ctx = logging.WithLogger(ctx, logger)

	logger = logging.FromContext(ctx) // same logger
}
Output:

Types

type Format added in v0.7.0

type Format string

Format represents the logging format.

func LookupFormat added in v0.7.0

func LookupFormat(name string) (Format, error)

LookupFormat attempts to get the formatter that corresponds to the given name. If no such formatter exists, it returns an error. If the empty string is given, it returns the JSON formatter.

type LevelHandler added in v0.7.0

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

LevelHandler is a wrapper around a LevelHandler that gives us the ability to configure level at runtime without users needing to manage a separate LevelVar.

func NewLevelHandler added in v0.7.0

func NewLevelHandler(leveler slog.Leveler, h slog.Handler) *LevelHandler

NewLevelHandler creates a new handler that is capable of dynamically setting a level in a concurrency-safe way.

func (*LevelHandler) Enabled added in v0.7.0

func (h *LevelHandler) Enabled(_ context.Context, level slog.Level) bool

Enabled implements Handler.Enabled by reporting whether level is at least as large as h's level.

func (*LevelHandler) Handle added in v0.7.0

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

Handle implements Handler.Handle.

func (*LevelHandler) Handler added in v0.7.0

func (h *LevelHandler) Handler() slog.Handler

Handler returns the Handler wrapped by h.

func (*LevelHandler) SetLevel added in v0.7.0

func (h *LevelHandler) SetLevel(level slog.Level)

SetLevel implements the levelable interface. It adjusts the level of the logger. It is safe for concurrent use.

func (*LevelHandler) WithAttrs added in v0.7.0

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

WithAttrs implements Handler.WithAttrs.

func (*LevelHandler) WithGroup added in v0.7.0

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

WithGroup implements Handler.WithGroup.

type LevelableHandler added in v0.7.0

type LevelableHandler interface {
	// SetLevel dynamically sets the level on the handler.
	SetLevel(level slog.Level)
}

LevelableHandler is an interface which defines a handler that is able to dynamically set its level.

Jump to

Keyboard shortcuts

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