Add comprehensive godoc documentation to all packages
- Add package-level documentation with usage examples and architecture details - Document all public types, functions, and methods following godoc conventions - Remove unused logger.Error type and NewError function - Apply consistent documentation style across all packages Packages updated: - apitls: TLS certificate management with automatic renewal - config: Environment-based configuration system - config/depenv: Deployment environment handling - ekko: Enhanced Echo web framework wrapper - kafka: Kafka client wrapper with TLS support - logger: Structured logging with OpenTelemetry integration - tracing: OpenTelemetry distributed tracing setup - types: Shared data structures for NTP Pool project - xff/fastlyxff: Fastly CDN IP range management All tests pass after documentation changes.
This commit is contained in:
108
logger/logger.go
108
logger/logger.go
@@ -1,3 +1,25 @@
|
||||
// Package logger provides structured logging with OpenTelemetry trace integration.
|
||||
//
|
||||
// This package offers multiple logging configurations for different deployment scenarios:
|
||||
// - Text logging to stderr with optional timestamp removal for systemd
|
||||
// - OTLP (OpenTelemetry Protocol) logging for observability pipelines
|
||||
// - Multi-logger setup that outputs to both text and OTLP simultaneously
|
||||
// - Context-aware logging with trace ID correlation
|
||||
//
|
||||
// The package automatically detects systemd environments and adjusts timestamp handling
|
||||
// accordingly. It supports debug level configuration via environment variables and
|
||||
// provides compatibility bridges for legacy logging interfaces.
|
||||
//
|
||||
// Key features:
|
||||
// - Automatic OpenTelemetry trace and span ID inclusion in log entries
|
||||
// - Configurable log levels via DEBUG environment variable (with optional prefix)
|
||||
// - Systemd-compatible output (no timestamps when INVOCATION_ID is present)
|
||||
// - Thread-safe logger setup with sync.Once protection
|
||||
// - Context propagation for request-scoped logging
|
||||
//
|
||||
// Environment variables:
|
||||
// - DEBUG: Enable debug level logging (configurable prefix via ConfigPrefix)
|
||||
// - INVOCATION_ID: Systemd detection for timestamp handling
|
||||
package logger
|
||||
|
||||
import (
|
||||
@@ -13,6 +35,9 @@ import (
|
||||
"go.opentelemetry.io/contrib/bridges/otelslog"
|
||||
)
|
||||
|
||||
// ConfigPrefix allows customizing the environment variable prefix for configuration.
|
||||
// When set, environment variables like DEBUG become {ConfigPrefix}_DEBUG.
|
||||
// This enables multiple services to have independent logging configuration.
|
||||
var ConfigPrefix = ""
|
||||
|
||||
var (
|
||||
@@ -67,10 +92,15 @@ func setupOtlpLogger() *slog.Logger {
|
||||
return otlpLogger
|
||||
}
|
||||
|
||||
// SetupMultiLogger will setup and make default a logger that
|
||||
// logs as described in Setup() as well as an OLTP logger.
|
||||
// The "multi logger" is made the default the first time
|
||||
// this function is called
|
||||
// SetupMultiLogger creates a logger that outputs to both text (stderr) and OTLP simultaneously.
|
||||
// This is useful for services that need both human-readable logs and structured observability data.
|
||||
//
|
||||
// The multi-logger combines:
|
||||
// - Text handler: Stderr output with OpenTelemetry trace integration
|
||||
// - OTLP handler: Structured logs sent via OpenTelemetry Protocol
|
||||
//
|
||||
// On first call, this logger becomes the default logger returned by Setup().
|
||||
// The function is thread-safe and uses sync.Once to ensure single initialization.
|
||||
func SetupMultiLogger() *slog.Logger {
|
||||
setupMulti.Do(func() {
|
||||
textHandler := Setup().Handler()
|
||||
@@ -89,28 +119,38 @@ func SetupMultiLogger() *slog.Logger {
|
||||
return multiLogger
|
||||
}
|
||||
|
||||
// SetupOLTP configures and returns a logger sending logs
|
||||
// via OpenTelemetry (configured via the tracing package).
|
||||
// SetupOLTP creates a logger that sends structured logs via OpenTelemetry Protocol.
|
||||
// This logger is designed for observability pipelines and log aggregation systems.
|
||||
//
|
||||
// This was made to work with Loki + Grafana that makes it
|
||||
// hard to view the log attributes in the UI, so the log
|
||||
// message is formatted similarly to the text logger. The
|
||||
// attributes are duplicated as OLTP attributes in the
|
||||
// log messages. https://github.com/grafana/loki/issues/14788
|
||||
// The OTLP logger formats log messages similarly to the text logger for better
|
||||
// compatibility with Loki + Grafana, while still providing structured attributes.
|
||||
// Log attributes are available both in the message format and as OTLP attributes.
|
||||
//
|
||||
// This logger does not become the default logger and must be used explicitly.
|
||||
// It requires OpenTelemetry tracing configuration to be set up via the tracing package.
|
||||
//
|
||||
// See: https://github.com/grafana/loki/issues/14788 for formatting rationale.
|
||||
func SetupOLTP() *slog.Logger {
|
||||
return setupOtlpLogger()
|
||||
}
|
||||
|
||||
// Setup returns an slog.Logger configured for text formatting
|
||||
// to stderr.
|
||||
// OpenTelemetry trace_id and span_id's are logged as attributes
|
||||
// when available.
|
||||
// When the application is running under systemd timestamps are
|
||||
// omitted. On first call the slog default logger is set to this
|
||||
// logger as well.
|
||||
// Setup creates and returns the standard text logger for the application.
|
||||
// This is the primary logging function that most applications should use.
|
||||
//
|
||||
// If SetupMultiLogger has been called Setup() will return
|
||||
// the "multi logger"
|
||||
// Features:
|
||||
// - Text formatting to stderr with human-readable output
|
||||
// - Automatic OpenTelemetry trace_id and span_id inclusion when available
|
||||
// - Systemd compatibility: omits timestamps when INVOCATION_ID environment variable is present
|
||||
// - Debug level support via DEBUG environment variable (respects ConfigPrefix)
|
||||
// - Thread-safe initialization with sync.Once
|
||||
//
|
||||
// On first call, this logger becomes the slog default logger. If SetupMultiLogger()
|
||||
// has been called previously, Setup() returns the multi-logger instead of the text logger.
|
||||
//
|
||||
// The logger automatically detects execution context:
|
||||
// - Systemd: Removes timestamps (systemd adds its own)
|
||||
// - Debug mode: Enables debug level logging based on environment variables
|
||||
// - OpenTelemetry: Includes trace correlation when tracing is active
|
||||
func Setup() *slog.Logger {
|
||||
setupText.Do(func() {
|
||||
h := setupStdErrHandler()
|
||||
@@ -129,15 +169,33 @@ func Setup() *slog.Logger {
|
||||
|
||||
type loggerKey struct{}
|
||||
|
||||
// NewContext adds the logger to the context. Use this
|
||||
// to for example make a request specific logger available
|
||||
// to other functions through the context
|
||||
// NewContext stores a logger in the context for request-scoped logging.
|
||||
// This enables passing request-specific loggers (e.g., with request IDs,
|
||||
// user context, or other correlation data) through the call stack.
|
||||
//
|
||||
// Use this to create context-aware logging where different parts of the
|
||||
// application can access the same enriched logger instance.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// logger := slog.With("request_id", requestID)
|
||||
// ctx := logger.NewContext(ctx, logger)
|
||||
// // Pass ctx to downstream functions
|
||||
func NewContext(ctx context.Context, l *slog.Logger) context.Context {
|
||||
return context.WithValue(ctx, loggerKey{}, l)
|
||||
}
|
||||
|
||||
// FromContext retrieves a logger from the context. If there is none,
|
||||
// it returns the default logger
|
||||
// FromContext retrieves a logger from the context.
|
||||
// If no logger is stored in the context, it returns the default logger from Setup().
|
||||
//
|
||||
// This function provides a safe way to access context-scoped loggers without
|
||||
// needing to check for nil values. It ensures that logging is always available,
|
||||
// falling back to the application's standard logger configuration.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// log := logger.FromContext(ctx)
|
||||
// log.Info("processing request") // Uses context logger or default
|
||||
func FromContext(ctx context.Context) *slog.Logger {
|
||||
if l, ok := ctx.Value(loggerKey{}).(*slog.Logger); ok {
|
||||
return l
|
||||
|
@@ -5,12 +5,24 @@ import (
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// stdLoggerish provides a bridge between legacy log interfaces and slog.
|
||||
// It implements common logging methods (Println, Printf, Fatalf) that
|
||||
// delegate to structured logging with a consistent key prefix.
|
||||
type stdLoggerish struct {
|
||||
key string
|
||||
log *slog.Logger
|
||||
f func(string, ...any)
|
||||
key string // Prefix key for all log messages
|
||||
log *slog.Logger // Underlying structured logger
|
||||
f func(string, ...any) // Log function (Info or Debug level)
|
||||
}
|
||||
|
||||
// NewStdLog creates a legacy-compatible logger that bridges to structured logging.
|
||||
// This is useful for third-party libraries that expect a standard log.Logger interface.
|
||||
//
|
||||
// Parameters:
|
||||
// - key: Prefix added to all log messages for identification
|
||||
// - debug: If true, logs at debug level; otherwise logs at info level
|
||||
// - log: Underlying slog.Logger (uses Setup() if nil)
|
||||
//
|
||||
// The returned logger implements Println, Printf, and Fatalf methods.
|
||||
func NewStdLog(key string, debug bool, log *slog.Logger) *stdLoggerish {
|
||||
if log == nil {
|
||||
log = Setup()
|
||||
@@ -27,14 +39,18 @@ func NewStdLog(key string, debug bool, log *slog.Logger) *stdLoggerish {
|
||||
return sl
|
||||
}
|
||||
|
||||
// Println logs the arguments using the configured log level with the instance key.
|
||||
func (l stdLoggerish) Println(msg ...any) {
|
||||
l.f(l.key, "msg", msg)
|
||||
}
|
||||
|
||||
// Printf logs a formatted message using the configured log level with the instance key.
|
||||
func (l stdLoggerish) Printf(msg string, args ...any) {
|
||||
l.f(l.key, "msg", fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
// Fatalf logs a formatted error message and panics.
|
||||
// Note: This implementation panics instead of calling os.Exit for testability.
|
||||
func (l stdLoggerish) Fatalf(msg string, args ...any) {
|
||||
l.log.Error(l.key, "msg", fmt.Sprintf(msg, args...))
|
||||
panic("fatal error") // todo: does this make sense at all?
|
||||
|
@@ -1,17 +0,0 @@
|
||||
package logger
|
||||
|
||||
type Error struct {
|
||||
Msg string
|
||||
Data []any
|
||||
}
|
||||
|
||||
func NewError(msg string, data ...any) *Error {
|
||||
return &Error{
|
||||
Msg: msg,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return "not implemented"
|
||||
}
|
Reference in New Issue
Block a user