Skip to content

Repository files navigation

slogx-go

Go Report Card GoDoc License Release

Description

Package slogx is a collection of extensions to Go’s structured logging package, slog.

Features

The slogx package provides the following features:

  • ContextHandler allows you to add slog attributes (slog.Attr instances) to a context.Context. These attributes are added to log records when the *Context function variants (InfoContext, ErrorContext, etc) on the logger are used.
  • LoggerBuilder provides a simple way to build slog.Logger instances.
  • LevelManager provides a way to manage slog.LevelVar instances from environment variables or provided LevelFunc (useful with config modules like koanf, viper, etc.).
  • Multiple loggers can be created with different log levels and formats. See internal/examples for more examples.

Installation

go get -u github.com/Evernorth/slogx-go

Usage

Simple logging

The following example demonstrates how to create a logger and log a message.

package main

import (
	"github.com/Evernorth/slogx-go/slogx"
	"log/slog"
	"os"
)

// This gets us a slog.Logger with context support that logs in JSON format to stdout.
var (
	logger, _ = slogx.NewLoggerBuilder().
		WithWriter(os.Stdout).
		WithFormat(slogx.FormatJSON).
		WithLevel(slog.LevelInfo).
		Build()
)

func main() {
	logger.Info("Hello, World!")
}

Simple log example

{"time":"2024-10-21T12:03:41.103566-04:00","level":"INFO","msg":"Hello, World!"}

Setting Log Level With String

The following example demonstrates how to create a logger using a string based log level.

package main

import (
	"github.com/Evernorth/slogx-go/slogx"
	"log/slog"
	"os"
)

// This gets us a slog.Logger with context support that logs in JSON format to stdout.
var (
	logLevel := os.Getenv("LOG_LEVEL")
	logger, _ = slogx.NewLoggerBuilder().
		WithWriter(os.Stdout).
		WithFormat(slogx.FormatJSON).
		WithLevelString(logLevel).
		Build()
)

func main() {
	logger.Info("Hello, World!")
}

This will produce the same behavior as the above example, but provides a convenient alternative if you want to staticly set your log level from a string. If the string is not a valid slog log level (debug, info, warn or error, case-insensitive, it will default to info)

Setting Timestamp Format

The following example shows how to configure the timestamp format for your logger. You must use a valid format provided by the time standard library's constants.

package main

import (
	"os"
	"time"

	"github.com/Evernorth/slogx-go/slogx"
)

func main() {

	logger, _ := slogx.NewLoggerBuilder().
		WithWriter(os.Stdout).
		WithFormat(slogx.FormatJSON).
		WithLevelString("debug").
		WithTimestampFormat(time.TimeOnly).
		Build()

	logger.Debug("A")
	logger.Info("B")
	logger.Warn("C")
	logger.Error("D")
}

Timestamp Example Output

{"time":"00:24:48","level":"DEBUG","msg":"A"}
{"time":"00:24:48","level":"INFO","msg":"B"}
{"time":"00:24:48","level":"WARN","msg":"C"}
{"time":"00:24:48","level":"ERROR","msg":"D"}

Managing log levels

The following examples demonstrate how to create a logger with a log level that can be changed at runtime.

Environment Variables example

package main

import (
	"github.com/Evernorth/slogx-go/slogx"
	"log/slog"
	"os"
)

// Environment variables
// These can be set to change the log level at runtime.
// The log level can be set to one of the following values: DEBUG, INFO, WARN, ERROR, FATAL, PANIC
const (
	logger1LevelEnvVar = "LOGGER1_LOG_LEVEL"
	logger2LevelEnvVar = "LOGGER2_LOG_LEVEL"
)

// Loggers
// This gets us a slog.Logger with context support that logs in JSON format to stdout.
var (
	logger1, levelVar1 = slogx.NewLoggerBuilder().
				WithWriter(os.Stdout).
				WithFormat(slogx.FormatJSON).
				WithLevel(slog.LevelInfo).
				Build()

	logger2, levelVar2 = slogx.NewLoggerBuilder().
				WithWriter(os.Stdout).
				WithFormat(slogx.FormatJSON).
				WithLevel(slog.LevelDebug).
				Build()
)

// manageLevelFromEnv Manage the log level from an environment variable with LevelManager.ManageLevelFromEnv.
func manageLevelFromEnv(logLevel string, levelVar *slog.LevelVar) {
	// Log the log level
	slog.Default().Info("", slog.String(logLevel, os.Getenv(logLevel)))

	// Set the log level.
	err := slogx.GetLevelManager().ManageLevelFromEnv(levelVar, logLevel)
	if err != nil {
		panic(err)
	}

}

// setup Set the default level manager
func setup() {

	// Enroll the levelVars with the LevelManager
	manageLevelFromEnv(logger1LevelEnvVar, levelVar1)
	manageLevelFromEnv(logger2LevelEnvVar, levelVar2)

	// Tell the LevelManager to update the levels
	slogx.GetLevelManager().UpdateLevels()

	// Log that the logger has been initialized

	slog.Info("Logger initialized", slog.String(logger1LevelEnvVar, levelVar1.Level().String()))
	slog.Info("Logger initialized", slog.String(logger2LevelEnvVar, levelVar2.Level().String()))

}

// main This function demonstrates how to use the slogx package to create a logger to manage log levels.
// It also demonstrates how to manage the log level from an environment variable.
// It also demonstrates how to use multiple loggers with log levels.
// The logger is configured to log in JSON format to stdout with a default log level of INFO.
func main() {

	setup()

	// Log some test messages

	// This message will not be logged because the log level is INFO.
	// This can be changed by setting the environment variable LOGGER1_LOG_LEVEL to DEBUG
	logger1.Debug("logger1 debug message.")

	logger1.Info("logger1 info message.")
	logger1.Warn("logger1 warn message.")
	logger1.Error("logger1 error message.")

	// Log some test messages using the 2nd logger

	// This message will be logged because the log level is DEBUG.
	// This can be changed by setting the environment variable LOGGER2_LOG_LEVEL to INFO
	logger2.Debug("logger2 debug message.")
	logger2.Info("logger2 info message.")
	logger2.Warn("logger2 warn message.")
	logger2.Error("logger2 error message.")

}

LevelFunc example

Although very similar to the above, this example demonstrates how a LevelFunc can be used to customize the source of log level values.

package main

import (
	"github.com/Evernorth/slogx-go/slogx"
	"github.com/knadh/koanf/v2"
	"log/slog"
	"os"
) 

// Property keys
// These can be set to change the log level at runtime.
// The log level can be set to one of the following values: DEBUG, INFO, WARN, ERROR, FATAL, PANIC
const (
    logger1LevelKey = "logger1.log.level"
    logger2LevelKey = "logger2.log.level"
)

// Loggers
// This gets us a slog.Logger with context support that logs in JSON format to stdout.
var (
    logger1, levelVar1 = slogx.NewLoggerBuilder().
      WithWriter(os.Stdout).
      WithFormat(slogx.FormatJSON).
      WithLevel(slog.LevelInfo).
      Build()
  
    logger2, levelVar2 = slogx.NewLoggerBuilder().
      WithWriter(os.Stdout).
      WithFormat(slogx.FormatJSON).
      WithLevel(slog.LevelDebug).
      Build()
    
    // create a new koanf instance to provide the configuration
    k = koanf.New(".")
)

// manageLevelFromFunc Manage the log level from a function with LevelManager.ManageLevelFromFunc.
func manageLevelFromFunc(logLevel string, levelVar *slog.LevelVar) {
    // Log the log level
    slog.Default().Info("", slog.String(logLevel, k.String(logLevel)))
    
    // Set the log level, using Koanf as the LevelFunc for obtaining updated log levels.
    err := slogx.GetLevelManager().ManageLevelFromFunc(levelVar, logLevel, k.String)
    if err != nil {
        panic(err)
    }

}

// setup Set the default level manager
func setup() {

    // Load Koanf config from the Providers of choice (environment, file, etc.).
    loadConfig(k)
    
    // Enroll the level keys with the LevelManager
    manageLevelFromFunc(logger1LevelKey, levelVar1)
    manageLevelFromFunc(logger2LevelKey, levelVar2)
    
    // Tell the LevelManager to update the levels
    slogx.GetLevelManager().UpdateLevels()
    
    // Log that the logger has been initialized
    
    slog.Info("Logger initialized", slog.String(logger1LevelKey, levelVar1.Level().String()))
    slog.Info("Logger initialized", slog.String(logger2LevelKey, levelVar2.Level().String()))
}

// main This function demonstrates how to use the slogx package to create a logger to manage log levels.
// It also demonstrates how to manage the log level from a configuration module (Koanf, other).
// It also demonstrates how to use multiple loggers with log levels.
// The logger is configured to log in JSON format to stdout with a default log level of INFO.
func main() {

    setup()
    
    // Log some test messages
    
    // This message will not be logged because the log level is INFO.
    // This can be changed by setting the environment variable LOGGER1_LOG_LEVEL to DEBUG
    logger1.Debug("logger1 debug message.")
    
    logger1.Info("logger1 info message.")
    logger1.Warn("logger1 warn message.")
    logger1.Error("logger1 error message.")
    
    // Log some test messages using the 2nd logger
    
    // This message will be logged because the log level is DEBUG.
    // This can be changed by setting the environment variable LOGGER2_LOG_LEVEL to INFO
    logger2.Debug("logger2 debug message.")
    logger2.Info("logger2 info message.")
    logger2.Warn("logger2 warn message.")
    logger2.Error("logger2 error message.")

}

Log example

2024/10/21 12:05:40 INFO Logger initialized LOGGER1_LOG_LEVEL=INFO
2024/10/21 12:05:40 INFO Logger initialized LOGGER2_LOG_LEVEL=DEBUG
{"time":"2024-10-21T12:05:40.937543-04:00","level":"INFO","msg":"logger1 info message."}
{"time":"2024-10-21T12:05:40.937545-04:00","level":"WARN","msg":"logger1 warn message."}
{"time":"2024-10-21T12:05:40.937547-04:00","level":"ERROR","msg":"logger1 error message."}
{"time":"2024-10-21T12:05:40.937548-04:00","level":"DEBUG","msg":"logger2 debug message."}
{"time":"2024-10-21T12:05:40.93755-04:00","level":"INFO","msg":"logger2 info message."}
{"time":"2024-10-21T12:05:40.937551-04:00","level":"ERROR","msg":"logger2 error message."}

Context-aware logging

Create a context-aware slog.Logger, then use the logger making sure to use a Context variant function.

package main

import (
	"context"
	"github.com/Evernorth/slogx-go/slogx"
	"log/slog"
	"os"
)

// This gets us a slog.Logger with context support that logs in JSON format to stdout.
var (
	logger, _ = slogx.NewLoggerBuilder().
		WithWriter(os.Stdout).
		WithFormat(slogx.FormatJSON).
		WithLevel(slog.LevelInfo).
		WithContextHandler().
		Build()
)

// main This example demonstrates how to create a logger with context support that logs in JSON format to stdout.
// It also demonstrates how to log with context and attributes.
// The logger is configured to log in JSON format to stdout with a default log level of INFO.
func main() {
	ctx := context.Background()
	logger.InfoContext(ctx, "Hello, World! Logging with Context.")

	// Create a context with some attributes
	ctx = slogx.ContextWithAttrs(ctx, slog.String("test1", "val1"),
		slog.String("test2", "val2"))
	logger.InfoContext(ctx, "Context and some attributes")

	// Create a context with some more attributes
	ctx = slogx.ContextWithAttrs(ctx, slog.String("test3", "val3"))
	logger.InfoContext(ctx, "Context and some more attributes")

	// Create a context with updated attributes
	ctx = slogx.ContextWithAttrs(ctx, slog.String("test1", "new-val1"))
	logger.InfoContext(ctx, "Context and update attributes")

}

Context-aware log example

{"time":"2024-10-21T12:09:44.301872-04:00","level":"INFO","msg":"Hello, World! Logging with Context."}
{"time":"2024-10-21T12:09:44.302091-04:00","level":"INFO","msg":"Context and some attributes","test1":"val1","test2":"val2"}
{"time":"2024-10-21T12:09:44.302095-04:00","level":"INFO","msg":"Context and some more attributes","test1":"val1","test2":"val2","test3":"val3"}
{"time":"2024-10-21T12:09:44.302098-04:00","level":"INFO","msg":"Context and update attributes","test1":"new-val1","test2":"val2","test3":"val3"}

Dependencies

See the go.mod file.

Support

If you have questions, concerns, bug reports, etc. See CONTRIBUTING.

License

slogx is open source software released under the Apache 2.0 license.

Original Contributors

  • Steve Sefton, Evernorth
  • Shellee Stewart, Evernorth
  • Neil Powell, Evernorth

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages