Documentation
¶
Overview ¶
Package zlog implements JSON-structured logging for slog.Handler interface. It provides the ability to print stacktrace, a development mode that renders logs in a user-friendly output like a zap library, flexible custom time formatters and other features, and almost fully compatible with slog.Handler rules, while maintaining high performance.
zlog also provides a Logger for better use the formatted logging methods (printf-style).
Getting Started ¶
Create a JSONHandler with default Config.
handler := zlog.NewJSONHandler(nil)
Use slog.Logger with zlog.JSONHandler.
log := slog.New(handler) log.Info("hello world", slog.String("foo", "bar")) log.WithGroup("request"). With(slog.String("method", "GET")). Info("received request", "params", params)
Use zlog.Logger with zlog.JSONHandler.
log := zlog.New(handler) log.Info("hello world", slog.String("foo", "bar")) // ... log.Errorf("Read file %s failed: %s", filePath, err)
Use default zlog.Logger
zlog.Warn("hello world", slog.String("foo", "bar")) // set the logger you created as the default. log := zlog.New(handler) zlog.SetDefault(log) zlog.Warn("hello world", slog.String("foo", "bar"))
Custom Configuration ¶
Create a JSONHandler with custom Config.
h := zlog.NewJSONHandler(&zlog.Config{ HandlerOptions: slog.HandlerOptions{ // enable AddSource AddSource: true, // set the level Level: slog.LevelDebug, } // enable development mode Development: true, }) // use WithOptions to override the handler Config. h = h.WithOptions(zlog.WithStacktraceEnabled(true)) log:= slog.New(h)
TraceContext ¶
The Context may contain some values that you want to print in each log. You need to implement the ContextExtractor function, which extracts the value you want and returns []slog.Attr.
The TraceContextExtractor function extracts trace span information from the Context.
func TraceContextExtractor(ctx context.Context) []slog.Attr { spanContext := trace.SpanContextFromContext(ctx) if spanContext.IsValid() { return []slog.Attr{ slog.Group("trace", slog.String("traceID", spanContext.TraceID().String()), slog.String("spanID", spanContext.SpanID().String()), ), } } return nil }
Index ¶
- Constants
- func Debug(msg string, args ...any)
- func DebugContext(ctx context.Context, msg string, args ...any)
- func DebugContextf(ctx context.Context, format string, args ...any)
- func Debugf(format string, args ...any)
- func Error(msg string, args ...any)
- func ErrorContext(ctx context.Context, msg string, args ...any)
- func ErrorContextf(ctx context.Context, format string, args ...any)
- func Errorf(format string, args ...any)
- func Info(msg string, args ...any)
- func InfoContext(ctx context.Context, msg string, args ...any)
- func InfoContextf(ctx context.Context, format string, args ...any)
- func Infof(format string, args ...any)
- func Log(ctx context.Context, level slog.Level, msg string, args ...any)
- func LogAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr)
- func NewNopHandler() slog.Handler
- func NewNopSlogger() *slog.Logger
- func SetDefault(l *Logger)
- func Warn(msg string, args ...any)
- func WarnContext(ctx context.Context, msg string, args ...any)
- func WarnContextf(ctx context.Context, format string, args ...any)
- func Warnf(format string, args ...any)
- type AppendTimeFunc
- type Config
- type ContextExtractor
- type JSONHandler
- func (h *JSONHandler) CapturePC(level slog.Level) bool
- func (h *JSONHandler) Enabled(_ context.Context, level slog.Level) bool
- func (h *JSONHandler) Handle(ctx context.Context, r slog.Record) error
- func (h *JSONHandler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *JSONHandler) WithGroup(name string) slog.Handler
- func (h *JSONHandler) WithOptions(opts ...Option) *JSONHandler
- type Logger
- func (l *Logger) Debug(msg string, args ...any)
- func (l *Logger) DebugContext(ctx context.Context, msg string, args ...any)
- func (l *Logger) DebugContextf(ctx context.Context, format string, args ...any)
- func (l *Logger) Debugf(format string, args ...any)
- func (l *Logger) Enabled(ctx context.Context, level slog.Level) bool
- func (l *Logger) Error(msg string, args ...any)
- func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any)
- func (l *Logger) ErrorContextf(ctx context.Context, format string, args ...any)
- func (l *Logger) Errorf(format string, args ...any)
- func (l *Logger) Handler() *JSONHandler
- func (l *Logger) Info(msg string, args ...any)
- func (l *Logger) InfoContext(ctx context.Context, msg string, args ...any)
- func (l *Logger) InfoContextf(ctx context.Context, format string, args ...any)
- func (l *Logger) Infof(format string, args ...any)
- func (l *Logger) Log(ctx context.Context, level slog.Level, msg string, args ...any)
- func (l *Logger) LogAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr)
- func (l *Logger) Warn(msg string, args ...any)
- func (l *Logger) WarnContext(ctx context.Context, msg string, args ...any)
- func (l *Logger) WarnContextf(ctx context.Context, format string, args ...any)
- func (l *Logger) Warnf(format string, args ...any)
- func (l *Logger) With(args ...any) *Logger
- func (l *Logger) WithCallerSkip(skip int) *Logger
- func (l *Logger) WithGroup(name string) *Logger
- func (l *Logger) WithOptions(opts ...Option) *Logger
- type Option
- func WithAddSource(addSource bool) Option
- func WithContextExtractor(extractors ...ContextExtractor) Option
- func WithDevelopment(development bool) Option
- func WithLevel(level slog.Leveler) Option
- func WithReplaceAttr(replaceAttr func(groups []string, a slog.Attr) slog.Attr) Option
- func WithStacktraceEnabled(enabled bool) Option
- func WithStacktraceKey(key string) Option
- func WithStacktraceLevel(level slog.Leveler) Option
- func WithTimeFormatter(formatter func([]byte, time.Time) []byte) Option
- func WithWriter(w io.Writer) Option
Examples ¶
Constants ¶
const RFC3339Milli = "2006-01-02T15:04:05.999Z07:00"
RFC3339Milli define the time format as RFC3339 with millisecond precision.
Variables ¶
This section is empty.
Functions ¶
func DebugContext ¶
DebugContext calls Logger.DebugContext on the default logger.
func DebugContextf ¶
DebugContextf calls Logger.DebugContextf on the default logger.
func ErrorContext ¶
ErrorContext calls Logger.ErrorContext on the default logger.
func ErrorContextf ¶
ErrorContextf calls Logger.ErrorContextf on the default logger.
func InfoContext ¶
InfoContext calls Logger.InfoContext on the default logger.
func InfoContextf ¶
InfoContextf calls Logger.InfoContextf on the default logger.
func NewNopHandler ¶
NewNopHandler creates a slog handler that discards all log messages.
func NewNopSlogger ¶
NewNopSlogger creates a slog logger that discards all log messages.
func WarnContext ¶
WarnContext calls Logger.WarnContext on the default logger.
func WarnContextf ¶
WarnContextf calls Logger.WarnContextf on the default logger.
Types ¶
type AppendTimeFunc ¶ added in v0.3.0
AppendTimeFunc append the formatted value to buf and returns the extended buffer.
type Config ¶
type Config struct { // If nil, a default handler is used. slog.HandlerOptions // Development enables development mode like zap development Logger, // that will write logs in human-friendly format, // also level will be colored if output is a terminal. Development bool // Writer is the writer to use. If nil, os.Stderr is used. Writer io.Writer // TimeFormatter is the time formatter to use for buildin attribute time value. If nil, use format RFC3339Milli as default. TimeFormatter AppendTimeFunc // built-in attribute keys, use slog's default if not set. // https://pkgo.dev/log/slog#pkg-constants TimeKey string LevelKey string MessageKey string SourceKey string // StacktraceEnabled enables stack trace for slog.Record. StacktraceEnabled bool // StacktraceLevel means which slog.Level from we should enable stack trace. // Default is slog.LevelError. StacktraceLevel slog.Leveler // StacktraceKey is the key for stacktrace field, default is "stacktrace". StacktraceKey string // ContextExtractors will be used in Handler.Handle ContextExtractors []ContextExtractor // If a group has no Attrs (even if it has a non-empty key), ignore it. IgnoreEmptyGroup bool }
Config the configuration for the JSONHandler.
type ContextExtractor ¶
ContextExtractor get attributes from context, that can be used in slog.Handler.
Example (UserContext) ¶
package main import ( "context" "log/slog" "net/http" "time" "github.com/icefed/zlog" ) func AuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // ... // Pretend that we read and parsed the token, and the user authentication succeeded ctx := context.WithValue(context.Background(), userKey{}, user{ Name: "[email protected]", Id: "a2067a0a-6b0b-4ee5-a049-16bdb8ed6ff5", }) next.ServeHTTP(w, r.WithContext(ctx)) }) } func LogMiddleware(log *zlog.Logger, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() next.ServeHTTP(w, r) duration := time.Since(start) log.InfoContext(r.Context(), "Received request", slog.String("method", r.Method), slog.String("path", r.URL.Path), slog.String("duration", duration.String()), ) }) } func hello(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("Hello, World!")) } func userContextExtractor(ctx context.Context) []slog.Attr { user, ok := ctx.Value(userKey{}).(user) if ok { return []slog.Attr{ slog.Group("user", slog.String("name", user.Name), slog.String("id", user.Id)), } } return nil } type userKey struct{} type user struct { Name string Id string } // The following is an example of printing a user request in http server. The log // contains user information and can be used as an audit log. func main() { h := zlog.NewJSONHandler(&zlog.Config{ HandlerOptions: slog.HandlerOptions{ Level: slog.LevelDebug, }, }) h = h.WithOptions(zlog.WithContextExtractor(userContextExtractor)) log := zlog.New(h) httpHandler := http.HandlerFunc(hello) // set auth middleware handler := AuthMiddleware(httpHandler) // set log middleware handler = LogMiddleware(log, handler) log.Info("starting server, listening on port 8080") http.ListenAndServe(":8080", handler) // Send a request using curl. // $ curl http://localhost:8080/api/v1/products // Hello, World! // Output like: // {"time":"2023-09-09T19:51:55.683+08:00","level":"INFO","msg":"starting server, listening on port 8080"} // {"time":"2023-09-09T19:52:04.228+08:00","level":"INFO","msg":"Received request","user":{"name":"[email protected]","id":"a2067a0a-6b0b-4ee5-a049-16bdb8ed6ff5"},"method":"GET","path":"/api/v1/products","duration":"6.221µs"} }
Output:
type JSONHandler ¶
type JSONHandler struct {
// contains filtered or unexported fields
}
JSONHandler implements the slog.Handler interface, transforming r.Record into the JSON format, and follows therules set by slog.Handler.
Additionally, it provides support for the development mode, akin to zap, which allows built-in attributes to be output in a human-friendly format.
func NewJSONHandler ¶
func NewJSONHandler(config *Config) *JSONHandler
NewJSONHandler creates a slog handler that writes log messages as JSON. If config is nil, a default configuration is used.
func (*JSONHandler) CapturePC ¶ added in v0.4.3
func (h *JSONHandler) CapturePC(level slog.Level) bool
CapturePC returns true if the handler has AddSource option enabled or the stacktrace is enabled at the given level. Logger should set PC in the slog.Record if this function returns true.
func (*JSONHandler) Enabled ¶
Enabled reports whether the handler handles records at the given level. The handler ignores records whose level is lower. https://pkgo.dev/log/slog#Handler
func (*JSONHandler) Handle ¶
Handle formats its argument Record as a JSON object on a single line. https://pkgo.dev/log/slog#Handler
func (*JSONHandler) WithAttrs ¶
func (h *JSONHandler) WithAttrs(attrs []slog.Attr) slog.Handler
WithAttrs implements the slog.Handler WithAttrs method. https://pkgo.dev/log/slog#Handler
func (*JSONHandler) WithGroup ¶
func (h *JSONHandler) WithGroup(name string) slog.Handler
WithGroup implements the slog.Handler WithGroup method. https://pkgo.dev/log/slog#Handler
func (*JSONHandler) WithOptions ¶
func (h *JSONHandler) WithOptions(opts ...Option) *JSONHandler
WithOptions return a new handler with the given options. Options will override the hander's config.
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
func New ¶
func New(h *JSONHandler) *Logger
New creates a new Logger. NewJSONHandler(nil) will be used if h is nil.
func NewNopLogger ¶
func NewNopLogger() *Logger
NewNopLogger creates a nop logger that discards all log messages.
func (*Logger) DebugContext ¶
DebugContext prints log message at the debug level with context.
func (*Logger) DebugContextf ¶
DebugContextf prints log message at the debug level with context, fmt.Sprintf is used to format.
func (*Logger) Debugf ¶
Debugf prints log message at the debug level, fmt.Sprintf is used to format.
func (*Logger) ErrorContext ¶
ErrorContext prints log message at the error level with context.
func (*Logger) ErrorContextf ¶
ErrorContextf prints log message at the error level with context, fmt.Sprintf is used to format.
func (*Logger) Errorf ¶
Errorf prints log message at the error level, fmt.Sprintf is used to format.
func (*Logger) InfoContext ¶
InfoContext prints log message at the info level with context.
func (*Logger) InfoContextf ¶
InfoContextf prints log message at the info level with context, fmt.Sprintf is used to format.
func (*Logger) Log ¶
Log prints log as a JSON object on a single line with the given level and message. Log follows the rules of slog.Logger.Log, args can be slog.Attr or will be converted to slog.Attr in pairs.
func (*Logger) LogAttrs ¶
Logf prints log as a JSON object on a single line with the given level, message and attrs.
func (*Logger) WarnContext ¶
WarnContext prints log message at the warn level with context.
func (*Logger) WarnContextf ¶
WarnContextf prints log message at the warn level with context, fmt.Sprintf is used to format.
func (*Logger) WithCallerSkip ¶
WithCallerSkip returns a new logger with the given caller skip. argument 'skip' will be added to the caller skip in the logger, which is passed as the first parameter 'skip' when calling runtime.Callers to get the source's pc. If 'skip' is 1, then skip a caller frame, if you have set callerskip, 'skip' can also be negative to subtract callerskip.
func (*Logger) WithOptions ¶
WithContext returns a new logger with the given handler options.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option is the option for JSONHandler.
Example (WithOptions) ¶
WithOptions can be used in handler and logger.
package main import ( "github.com/icefed/zlog" ) func main() { // use WithOptions in handler // first, create a default handler h := zlog.NewJSONHandler(nil) // set options for handler h = h.WithOptions( zlog.WithAddSource(true), zlog.WithStacktraceEnabled(true), zlog.WithDevelopment(true), ) // use WithOptions in logger log := zlog.New(h) // set options for logger log = log.WithOptions( zlog.WithAddSource(false), ) // ... }
Output:
func WithAddSource ¶
WithAddSource enables add source field.
func WithContextExtractor ¶
func WithContextExtractor(extractors ...ContextExtractor) Option
WithContextExtractor adds context extractors.
func WithDevelopment ¶
WithDevelopment enables development mode like zap development Logger.
func WithReplaceAttr ¶
WithReplaceAttr sets the replaceAttr.
func WithStacktraceEnabled ¶
WithStacktraceEnabled enables stacktrace for slog.Record.
func WithStacktraceKey ¶
WithStacktraceKey sets the key for stacktrace field.
func WithStacktraceLevel ¶
WithStacktraceLevel sets the level for stacktrace.
func WithTimeFormatter ¶
WithTimeFormatter sets the time formatter.