-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
logger: add default logger and subsystem log handler
Adds ability to control logging via env vars.
- Loading branch information
Showing
2 changed files
with
143 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Package logger provides a slog.Logger that can be configured via environment variables. | ||
// NUNKI_LOG_LEVEL can be used to set the log level. | ||
// NUNKI_LOG_FORMAT can be used to set the log format. | ||
package logger | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log/slog" | ||
"os" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// Default returns a logger configured via environment variables. | ||
func Default() (*slog.Logger, error) { | ||
logLevel, err := getLogLevel() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return slog.New(logHandler()(os.Stderr, &slog.HandlerOptions{ | ||
Level: logLevel, | ||
})), nil | ||
} | ||
|
||
func getLogLevel() (slog.Level, error) { | ||
logLevel := os.Getenv("NUNKI_LOG_LEVEL") | ||
switch strings.ToLower(logLevel) { | ||
case "debug": | ||
return slog.LevelDebug, nil | ||
case "", "info": | ||
return slog.LevelInfo, nil | ||
case "warn": | ||
return slog.LevelWarn, nil | ||
case "error": | ||
return slog.LevelError, nil | ||
} | ||
|
||
numericLevel, err := strconv.Atoi(logLevel) | ||
if err != nil { | ||
return slog.Level(0), fmt.Errorf("invalid log level: %q", logLevel) | ||
} | ||
return slog.Level(numericLevel), nil | ||
} | ||
|
||
func logHandler() func(w io.Writer, opts *slog.HandlerOptions) slog.Handler { | ||
switch strings.ToLower(os.Getenv("NUNKI_LOG_FORMAT")) { | ||
case "json": | ||
return func(w io.Writer, opts *slog.HandlerOptions) slog.Handler { | ||
return slog.NewJSONHandler(w, opts) | ||
} | ||
} | ||
return func(w io.Writer, opts *slog.HandlerOptions) slog.Handler { | ||
return slog.NewTextHandler(w, opts) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
Copyright (c) Edgeless Systems GmbH | ||
SPDX-License-Identifier: AGPL-3.0-only | ||
*/ | ||
|
||
// Package subsystemlog provides a slog.Handler that can be used to enable | ||
// logging on a per-subsystem basis. | ||
// | ||
// NUNKI_LOG_SUBSYSTEMS can be used to enable logging for specific subsystems. | ||
// If NUNKI_LOG_SUBSYSTEMS has the special value "*", all subsystems are enabled. | ||
// Otherwise, a comma-separated list of subsystem names can be specified. | ||
package subsystemlog | ||
|
||
import ( | ||
"context" | ||
"log/slog" | ||
"os" | ||
"strings" | ||
) | ||
|
||
// Handler is a slog.Handler that can be used to enable logging on a per-subsystem basis. | ||
type Handler struct { | ||
inner slog.Handler | ||
subsystem string | ||
enabled bool | ||
} | ||
|
||
// NewHandler returns a new Handler. | ||
func NewHandler(inner slog.Handler, subsystem string) (*Handler, error) { | ||
if subsystem != "" { | ||
inner = inner.WithGroup(subsystem) | ||
} | ||
|
||
return &Handler{ | ||
inner: inner, | ||
subsystem: subsystem, | ||
enabled: subsystemEnvEnabled(subsystem), | ||
}, nil | ||
} | ||
|
||
// Enabled returns true if the given level is enabled. | ||
func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool { | ||
return h.enabled && h.inner.Enabled(ctx, level) | ||
} | ||
|
||
// Handle handles the given record. | ||
func (h *Handler) Handle(ctx context.Context, record slog.Record) error { | ||
return h.inner.Handle(ctx, record) | ||
} | ||
|
||
// WithAttrs returns a new Handler with the given attributes. | ||
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler { | ||
return &Handler{ | ||
inner: h.inner.WithAttrs(attrs), | ||
subsystem: h.subsystem, | ||
enabled: h.enabled, | ||
} | ||
} | ||
|
||
// WithGroup returns a new Handler with the given group. | ||
func (h *Handler) WithGroup(name string) slog.Handler { | ||
return &Handler{ | ||
inner: h.inner.WithGroup(name), | ||
subsystem: h.subsystem, | ||
enabled: h.enabled, | ||
} | ||
} | ||
|
||
func subsystemEnvEnabled(subsystem string) bool { | ||
allowList := os.Getenv("NUNKI_LOG_SUBSYSTEMS") | ||
|
||
return subsystemAllowListMatch(subsystem, allowList) | ||
} | ||
|
||
func subsystemAllowListMatch(subsystem string, allowList string) bool { | ||
if allowList == "*" { | ||
return true | ||
} | ||
for _, allow := range strings.Split(allowList, ",") { | ||
allow = strings.ToLower(strings.TrimSpace(allow)) | ||
if allow == subsystem { | ||
return true | ||
} | ||
} | ||
return false | ||
} |