Documentation ¶
Overview ¶
Example ¶
package main import ( "fmt" "io" "log" "os" "path/filepath" "runtime" "golang.org/x/exp/slog" "gopkg.in/natefinch/lumberjack.v2" "slogmw" ) type config struct { ConsoleDisable bool MinimumLevel slog.Level ConsoleFormat string AddSource bool AddFullSource bool FileEnable bool FilePath string FileFormat string RotateEnable bool RotateSize int RotateKeep int RotateAge int } const ( FormatText = "TEXT" FormatJSON = "JSON" ) func main() { appConfig := config{ ConsoleDisable: false, MinimumLevel: slog.LevelInfo, ConsoleFormat: FormatJSON, AddSource: true, AddFullSource: false, FileEnable: true, FilePath: "./slog.log", FileFormat: FormatText, RotateEnable: false, RotateSize: 0, RotateKeep: 0, RotateAge: 0, } logger, err := newMultiSlogger(appConfig, "SlogTest") if err != nil { panic(fmt.Errorf("setup logging: %w", err)) } logFunc(logger, "Logging initialized...") logFunc(logger.With(slog.Int("With", lineNumber())), "1st With Call") for i := 0; i <= 10; i++ { logFunc(logger.With(slog.Int("Iteration", i)), "Looping") } logFunc(logger.WithGroup("Grouping"), "Group", slog.String("SubKey", "SubValue")) } func lineNumber() int { // notice that we're using 1, so it will actually log the where // the error happened, 0 = this function, we don't want that. _, _, line, _ := runtime.Caller(1) return line } func logFunc(log *slog.Logger, msg string, args ...any) { log.Info(msg, args...) } func removeTime(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey && len(groups) == 0 { a.Key = "" } return a } func newMultiSlogger(conf config, appName string) (*slog.Logger, error) { handler := slog.HandlerOptions{ AddSource: conf.AddSource, ReplaceAttr: removeTime, } if !conf.AddFullSource { handler.ReplaceAttr = func(groups []string, a slog.Attr) slog.Attr { // Remove the directory from the source's filename. if a.Key == slog.SourceKey { a.Value = slog.StringValue(filepath.Base(a.Value.String())) } // Remove Timestamp for testing if a.Key == slog.TimeKey && len(groups) == 0 { a.Key = "" } return a } } logWriter, err := slogSetupMultiWriter(conf) if err != nil { return nil, fmt.Errorf("setup Slog MultiWriter: %w", err) } return slog.New( handler.NewJSONHandler(logWriter). WithAttrs([]slog.Attr{ slog.String("name", appName), }), ), nil } func outputFormatWriter(format string, writer io.Writer) io.Writer { switch format { case FormatJSON: log.Println("Utilizing JSON") return slogmw.NewJSON(writer) default: log.Println("Utilizing LOGFMT") return slogmw.NewLOGFMT(writer) } } func slogSetupMultiWriter(conf config) (io.Writer, error) { var writers []io.Writer if !conf.ConsoleDisable { log.Println("Enabling Console Logger") writers = append(writers, outputFormatWriter(conf.ConsoleFormat, os.Stdout)) } if conf.FileEnable { log.Println("Enabling File Logger") var output io.Writer if conf.RotateEnable { output = &lumberjack.Logger{ Filename: conf.FilePath, MaxSize: conf.RotateSize, // megabytes MaxBackups: conf.RotateKeep, MaxAge: conf.RotateAge, // days } } else { logFile, err := os.OpenFile( conf.FilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644, //nolint:gomnd ) if err != nil { return nil, fmt.Errorf("could not open log file [%s] Error: %w", conf.FilePath, err) } output = logFile } writers = append(writers, outputFormatWriter(conf.FileFormat, output)) } return io.MultiWriter(writers...), nil }
Output: {"level":"INFO","source":"example_test.go:91","msg":"Logging initialized...","name":"SlogTest"} {"level":"INFO","source":"example_test.go:91","msg":"1st With Call","name":"SlogTest","With":58} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":0} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":1} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":2} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":3} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":4} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":5} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":6} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":7} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":8} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":9} {"level":"INFO","source":"example_test.go:91","msg":"Looping","name":"SlogTest","Iteration":10} {"level":"INFO","source":"example_test.go:91","msg":"Group","name":"SlogTest","Grouping":{"SubKey":"SubValue"}}
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var VerboseErrors bool
VerboseErrors will indicate that we should print to console any errors as they are normally swallowed by Slog.
Functions ¶
This section is empty.
Types ¶
type JSON ¶
JSON stores the destination writer and passes through the Slog JSON. In the future, we may perform additional logic on the given JSON.
Click to show internal directories.
Click to hide internal directories.