### Multi-region Setup Example Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/failover.md Demonstrates setting up a failover handler for a multi-region deployment. Logs will attempt to go to the primary region first, then fall back to secondary and tertiary regions. ```go handler := slogmulti.Failover()( slog.NewJSONHandler(usEastConn, nil), // Primary slog.NewJSONHandler(usWestConn, nil), // Secondary slog.NewJSONHandler(euWestConn, nil), // Tertiary ) logger := slog.New(handler) logger.Info("Always attempts US East first, then falls back to US West, then EU West") ``` -------------------------------- ### Install slog-multi Source: https://github.com/samber/slog-multi/blob/main/README.md Install the slog-multi library using go get. Requires Go version 1.21 or later. ```sh go get github.com/samber/slog-multi ``` -------------------------------- ### Install Development Dependencies Source: https://github.com/samber/slog-multi/blob/main/README.md Installs necessary development tools using the 'make tools' command. ```bash make tools ``` -------------------------------- ### Complete Production Logging Setup with Slog Multi Source: https://github.com/samber/slog-multi/blob/main/_autodocs/patterns.md Combines recovery, privacy middleware, and failover handlers for a comprehensive production logging system. Ensure primaryConn and backupConn are properly initialized before use. ```go logger := slog.New( slogmulti.Pipe( // Recovery: catch handler errors slogmulti.RecoverHandlerError(func(ctx context.Context, r slog.Record, err error) { fmt.Printf("logging error: %v\n", err) }), // Privacy: redact PII slogmulti.NewHandleInlineMiddleware(func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { record.Attrs(func(attr slog.Attr) bool { if attr.Key == "password" || attr.Key == "token" { record.AddAttrs(slog.String(attr.Key, "***REDACTED***")) } return true }) return next(ctx, record) }), ).Handler( // Failover: primary and backup servers slogmulti.Failover()( slog.NewJSONHandler(primaryConn, nil), slog.NewJSONHandler(backupConn, nil), ), ), ) logger.Info("production-ready logging") ``` -------------------------------- ### Example: Using with Pipe Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/recover.md Demonstrates how to use the Recover handler within a pipeline of slog handlers to safely catch and log errors. ```APIDOC ## Example: Using with Pipe ```go package main import ( "context" "fmt" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { recovery := slogmulti.RecoverHandlerError( func(ctx context.Context, record slog.Record, err error) { fmt.Printf("Handler error: %v\n", err) }, ) sink := slog.NewJSONHandler(os.Stderr, nil) handler := slogmulti.Pipe(recovery).Handler(sink) logger := slog.New(handler) logger.Info("all errors are safely caught and logged") } ``` ``` -------------------------------- ### Example: Using RecoverHandlerError Middleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/recover.md Demonstrates how to use the RecoverHandlerError middleware to wrap a JSON handler. This setup ensures that any errors or panics during logging are caught and handled by the provided recovery function without crashing the application. ```go package main import ( "context" "fmt" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { recovery := slogmulti.RecoverHandlerError( func(ctx context.Context, record slog.Record, err error) { fmt.Printf("Logging error recovered: %v\n", err) }, ) riskyHandler := slog.NewJSONHandler(os.Stderr, nil) safeHandler := recovery(riskyHandler) logger := slog.New(safeHandler) logger.Info("message processed safely") } ``` -------------------------------- ### Route Logs with Business Logic Source: https://github.com/samber/slog-multi/blob/main/_autodocs/patterns.md Configure a router to direct log records to different handlers based on custom business logic. This example routes 'database' and 'api' component logs to specific Slack channels, with a console handler as a fallback. ```go handler := slogmulti.Router(). Add( databaseSlackChannel, func(ctx context.Context, r slog.Record) bool { // Route database logs found := false r.Attrs(func(attr slog.Attr) bool { if attr.Key == "component" && attr.Value.String() == "database" { found = true return false } return true }) return found }, ). Add( apiSlackChannel, func(ctx context.Context, r slog.Record) bool { // Route API logs found := false r.Attrs(func(attr slog.Attr) bool { if attr.Key == "component" && attr.Value.String() == "api" { found = true return false } return true }) return found }, ). Add(consoleHandler). // Fallback Handler() ``` -------------------------------- ### Create Inline Middleware with Custom Handlers Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/inline-middleware.md Use `NewInlineMiddleware` to create a middleware that intercepts and modifies log records. This example demonstrates passing through `Enabled`, `WithAttrs`, and `WithGroup` calls while adding a custom attribute in the `Handle` function. ```go package main import ( "context" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { middleware := slogmulti.NewInlineMiddleware( // Enabled: passthrough func(ctx context.Context, level slog.Level, next func(context.Context, slog.Level) bool) bool { return next(ctx, level) }, // Handle: add custom attribute func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { record.AddAttrs(slog.String("custom", "value")) return next(ctx, record) }, // WithAttrs: passthrough func(attrs []slog.Attr, next func([]slog.Attr) slog.Handler) slog.Handler { return next(attrs) }, // WithGroup: passthrough func(name string, next func(string) slog.Handler) slog.Handler { return next(name) }, ) handler := slogmulti.Pipe(middleware).Handler(slog.NewJSONHandler(os.Stderr, nil)) logger := slog.New(handler) logger.Info("message") } ``` -------------------------------- ### Create Pipe with Middleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/pipe.md Creates a new PipeBuilder with the provided middleware functions. Use this to start building a chain of log record transformations. ```go package main import ( "context" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { // Create middleware that adds a timestamp timestampMiddleware := slogmulti.NewHandleInlineMiddleware( func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { record.AddAttrs(slog.Time("logged_at", time.Now())) return next(ctx, record) }, ) handler := slogmulti.Pipe(timestampMiddleware). Handler(slog.NewJSONHandler(os.Stderr, nil)) logger := slog.New(handler) logger.Info("message with timestamp") } ``` -------------------------------- ### Custom Middleware Example: FilterDebugLogs Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/pipe.md An example of a custom middleware that filters out debug logs. It demonstrates how to use NewInlineMiddleware to create handlers that intercept and conditionally process log records. ```go // Custom middleware that filters debug logs func FilterDebugLogs(next slog.Handler) slog.Handler { return slogmulti.NewInlineMiddleware( func(ctx context.Context, level slog.Level, nextEnabled func(context.Context, slog.Level) bool) bool { if level == slog.LevelDebug { return false } return nextEnabled(ctx, level) }, func(ctx context.Context, record slog.Record, nextHandle func(context.Context, slog.Record) error) error { return nextHandle(ctx, record) }, func(attrs []slog.Attr, nextAttrs func([]slog.Attr) slog.Handler) slog.Handler { return nextAttrs(attrs) }, func(name string, nextGroup func(string) slog.Handler) slog.Handler { return nextGroup(name) }, )(next) } ``` -------------------------------- ### Go: Failover Handler with slogmulti.Failover Source: https://github.com/samber/slog-multi/blob/main/README.md Ensures log delivery reliability by trying multiple handlers in order until one succeeds. Ideal for high-availability logging setups where log loss is unacceptable. Requires handlers to be passed as variadic arguments to Failover(). ```go import ( "net" slogmulti "github.com/samber/slog-multi" "log/slog" "os" "time" ) func main() { // Create connections to multiple log servers // ncat -l 1000 -k // ncat -l 1001 -k // ncat -l 1002 -k // List AZs - use github.com/netbrain/goautosocket for auto-reconnect logstash1, _ := net.Dial("tcp", "logstash.eu-west-3a.internal:1000") logstash2, _ := net.Dial("tcp", "logstash.eu-west-3b.internal:1000") logstash3, _ := net.Dial("tcp", "logstash.eu-west-3c.internal:1000") logger := slog.New( slogmulti.Failover()( slog.HandlerOptions{}.NewJSONHandler(logstash1, nil), // Primary slog.HandlerOptions{}.NewJSONHandler(logstash2, nil), // Secondary slog.HandlerOptions{}.NewJSONHandler(logstash3, nil), // Tertiary ), ) logger. With( slog.Group("user", slog.String("id", "user-123"), slog.Time("created_at", time.Now()), ), ) .With("environment", "dev") .With("error", fmt.Errorf("an error")) .Error("A message") } ``` -------------------------------- ### Implement Attribute Filtering Middleware in Go Source: https://github.com/samber/slog-multi/blob/main/_autodocs/patterns.md Use `slogmulti.NewWithAttrsInlineMiddleware` to create a middleware that filters attributes. This example removes attributes with keys prefixed by '_internal_'. Ensure the `sinkHandler` is properly initialized. ```go complianceMiddleware := slogmulti.NewWithAttrsInlineMiddleware( func(attrs []slog.Attr, next func([]slog.Attr) slog.Handler) slog.Handler { filtered := make([]slog.Attr, 0, len(attrs)) for _, attr := range attrs { // Filter out non-compliant attributes if !strings.HasPrefix(attr.Key, "_internal_") { filtered = append(filtered, attr) } } return next(filtered) }, ) handler := slogmulti.Pipe(complianceMiddleware).Handler(sinkHandler) ``` -------------------------------- ### Conditional Logger Configuration for Dev vs. Production Source: https://github.com/samber/slog-multi/blob/main/_autodocs/patterns.md Creates a logger instance tailored for either development (simple text output) or production (comprehensive setup with recovery and failover). The 'isDev' boolean flag determines the configuration. Ensure errorLogger, primaryHandler, and backupHandler are defined elsewhere. ```go func NewLogger(isDev bool) *slog.Logger { if isDev { // Development: simple text output with all levels handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ Level: slog.LevelDebug, }) return slog.New(handler) } // Production: comprehensive setup with recovery and failover handler := slogmulti.Pipe( slogmulti.RecoverHandlerError(errorLogger), ).Handler( slogmulti.Failover()( primaryHandler, backupHandler, ), ) return slog.New(handler) } ``` -------------------------------- ### Create Handle Inline Middleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/inline-middleware.md Use NewHandleInlineMiddleware to create middleware that only intercepts the Handle method. This is useful for modifying log records just before they are processed by the next handler in the chain. The example adds a timestamp attribute to all log records. ```go middleware := slogmulti.NewHandleInlineMiddleware( func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { record.AddAttrs(slog.Time("logged_at", time.Now())) return next(ctx, record) }, ) handler := slogmulti.Pipe(middleware).Handler(sink) ``` -------------------------------- ### Create and Configure Router Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/router.md Creates a new router instance and adds handlers with level-based predicates. Logs are routed to all matching handlers. ```go package main import ( "context" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { consoleHandler := slog.NewTextHandler(os.Stderr, nil) fileHandler := slog.NewJSONHandler(os.Stdout, nil) handler := slogmulti.Router(). Add(consoleHandler, slogmulti.LevelIs(slog.LevelInfo)). Add(fileHandler, slogmulti.LevelIs(slog.LevelError)). Handler() logger := slog.New(handler) logger.Info("goes to console only") logger.Error("goes to file only") } ``` -------------------------------- ### Basic Fanout with slog-multi Source: https://github.com/samber/slog-multi/blob/main/_autodocs/README.md Distribute log records to multiple handlers simultaneously. Ensure all specified handlers are configured correctly. ```go handler := slogmulti.Fanout( slog.NewJSONHandler(file, nil), slog.NewTextHandler(os.Stderr, nil), ) logger := slog.New(handler) ``` -------------------------------- ### Get Router Handler Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/router.md Retrieves a slog.Handler that implements the routing logic configured for the router. ```go func (h *router) Handler() slog.Handler ``` -------------------------------- ### Create a First Match Router Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/firstmatch.md Creates a first-match router from routable handlers. Use this when you need logs to be processed by only the first matching handler. ```go package main import ( "context" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { queryHandler := slog.NewJSONHandler(os.Stdout, nil) requestHandler := slog.NewJSONHandler(os.Stdout, nil) fallbackHandler := slog.NewJSONHandler(os.Stderr, nil) handler := slogmulti.Router(). Add(queryHandler, slogmulti.AttrKindIs("query", slog.KindString)). Add(requestHandler, slogmulti.AttrKindIs("method", slog.KindString)). Add(fallbackHandler). FirstMatch(). Handler() logger := slog.New(handler) // Goes to queryHandler only (stops at first match) logger.Debug("Executing SQL query", "query", "SELECT * FROM users WHERE id = ?") // Goes to requestHandler only (queryHandler didn't match) logger.Error("Incoming request failed", "method", "POST") // Goes to fallbackHandler (no other handlers matched) logger.Error("An unexpected error occurred") } ``` -------------------------------- ### Inline Middleware Hooking Handle() Method Source: https://github.com/samber/slog-multi/blob/main/README.md Implement middleware that intercepts the Handle() method of a slog.Handler. This example adds a timestamp to all log records before they are processed. ```go middleware := slogmulti.NewHandleInlineMiddleware(func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { // Add timestamp to all logs record.AddAttrs(slog.Time("logged_at", time.Now())) return next(ctx, record) }) ``` -------------------------------- ### Run Tests Source: https://github.com/samber/slog-multi/blob/main/README.md Executes the test suite for the project. Use 'make watch-test' for continuous testing during development. ```bash make test ``` ```bash make watch-test ``` -------------------------------- ### Middleware Pattern Implementation Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/index.md Illustrates the common pattern for implementing middleware. A middleware function takes the next handler in the chain and returns a new handler that wraps the next one. ```go middleware := func(next slog.Handler) slog.Handler { return newWrappedHandler(next) } ``` -------------------------------- ### Create WithAttrs Inline Middleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/inline-middleware.md Use NewWithAttrsInlineMiddleware to create middleware that only intercepts the WithAttrs method. This allows filtering or modifying attributes before they are passed to the next handler. The example filters out sensitive attributes like 'password' and 'token'. ```go middleware := slogmulti.NewWithAttrsInlineMiddleware( func(attrs []slog.Attr, next func([]slog.Attr) slog.Handler) slog.Handler { filtered := make([]slog.Attr, 0, len(attrs)) for _, attr := range attrs { if attr.Key != "password" && attr.Key != "token" { filtered = append(filtered, attr) } } return next(filtered) }, ) handler := slogmulti.Pipe(middleware).Handler(sink) ``` -------------------------------- ### Route Logs by Environment with slog-multi Source: https://github.com/samber/slog-multi/blob/main/_autodocs/patterns.md Configure a logger to route logs to different handlers based on the environment (e.g., development vs. production). Requires importing slog and slogmulti. ```go package main import ( "context" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func setupLogger(env string) *slog.Logger { debugHandler := slog.NewTextHandler(os.Stderr, nil) prodHandler := slog.NewJSONHandler(os.Stdout, nil) isProduction := func(ctx context.Context, r slog.Record) bool { return env == "prod" } isDevelopment := func(ctx context.Context, r slog.Record) bool { return env != "prod" } handler := slogmulti.Router(). Add(prodHandler, isProduction). Add(debugHandler, isDevelopment). Handler() return slog.New(handler) } ``` -------------------------------- ### Import slog-multi Package Source: https://github.com/samber/slog-multi/blob/main/_autodocs/overview.md Shows the import path for the slog-multi library, necessary for using its advanced routing and composition features. ```go import slogmulti "github.com/samber/slog-multi" ``` -------------------------------- ### Implement Complete Inline Middleware Source: https://github.com/samber/slog-multi/blob/main/README.md Construct a comprehensive inline middleware by combining custom logic for level checking, record processing, attribute handling, and group management. Use this for complex, multi-stage logging transformations. ```go mdw := slogmulti.NewInlineMiddleware( func(ctx context.Context, level slog.Level, next func(context.Context, slog.Level) bool) bool{ // Custom logic here // [...] return next(ctx, level) }, func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error{ // Custom logic here // [...] return next(ctx, record) }, func(attrs []slog.Attr, next func([]slog.Attr) slog.Handler) slog.Handler{ // Custom logic here // [...] return next(attrs) }, func(name string, next func(string) slog.Handler) slog.Handler{ // Custom logic here // [...] return next(name) }, ) ``` -------------------------------- ### Using RecoverHandlerError with Pipe Go Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/recover.md Demonstrates how to use RecoverHandlerError with slog-multi's Pipe to create a handler that safely catches and logs errors from underlying handlers. This setup ensures that errors during log processing do not crash the application. ```go package main import ( "context" "fmt" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { recovery := slogmulti.RecoverHandlerError( func(ctx context.Context, record slog.Record, err error) { fmt.Printf("Handler error: %v\n", err) }, ) sink := slog.NewJSONHandler(os.Stderr, nil) handler := slogmulti.Pipe(recovery).Handler(sink) logger := slog.New(handler) logger.Info("all errors are safely caught and logged") } ``` -------------------------------- ### Production Logging Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Configure a logger for production environments using a pipe that recovers from handler errors and a failover strategy for primary and backup handlers. ```go logger := slog.New( slogmulti.Pipe( slogmulti.RecoverHandlerError(errorLogger), ).Handler( slogmulti.Failover()( primaryHandler, backupHandler, ), ), ) ``` -------------------------------- ### Development Logging Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Set up logger for development with a router that directs debug and info level logs to the console and error logs to a specific error handler. ```go logger := slog.New( slogmulti.Router(). Add(consoleHandler, slogmulti.LevelIs(slog.LevelDebug, slog.LevelInfo)). Add(errorHandler, slogmulti.LevelIs(slog.LevelError)). Handler(), ) ``` -------------------------------- ### Inline Handler/Middleware Implementations Source: https://github.com/samber/slog-multi/blob/main/_autodocs/MANIFEST.md Documentation for various inline handler and middleware implementations. ```APIDOC ## Inline Handler/Middleware Implementations (All inline handler/middleware implementations are documented) ``` -------------------------------- ### Pipelining Logs with Middleware Source: https://github.com/samber/slog-multi/blob/main/README.md Demonstrates chaining multiple middleware functions (error formatting and GDPR compliance) before a final handler. Use this to apply sequential transformations and filtering to log records. ```go import ( "context" slogmulti "github.com/samber/slog-multi" "log/slog" "os" "time" ) func main() { // First middleware: format Go `error` type into an structured object {error: "*myCustomErrorType", message: "could not reach https://a.b/c"} errorFormattingMiddleware := slogmulti.NewHandleInlineMiddleware(func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { record.Attrs(func(attr slog.Attr) bool { if attr.Key == "error" && attr.Value.Kind() == slog.KindAny { if err, ok := attr.Value.Any().(error); ok { record.AddAttrs( slog.String("error_type", "error"), slog.String("error_message", err.Error()), ) } } return true }) return next(ctx, record) }) // Second middleware: remove PII gdprMiddleware := slogmulti.NewHandleInlineMiddleware(func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { record.Attrs(func(attr slog.Attr) bool { if attr.Key == "email" || attr.Key == "phone" || attr.Key == "created_at" { record.AddAttrs(slog.String(attr.Key, "*********")) } return true }) return next(ctx, record) }) // Final handler sink := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{}) logger := slog.New( slogmulti. Pipe(errorFormattingMiddleware). Pipe(gdprMiddleware). // ... Handler(sink), ) logger. With( slog.Group("user", slog.String("id", "user-123"), slog.String("email", "user-123"), slog.Time("created_at", time.Now()), ), ). With("environment", "dev"). Error("A message", slog.String("foo", "bar"), slog.Any("error", fmt.Errorf("an error")), ) } ``` -------------------------------- ### Add Performance Monitoring Middleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/patterns.md Integrate middleware to measure and log the duration of log record handling. Logs exceeding 100 milliseconds will have their duration recorded. ```go performanceMiddleware := slogmulti.NewHandleInlineMiddleware( func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { start := time.Now() err := next(ctx, record) duration := time.Since(start) if duration > 100*time.Millisecond { record.AddAttrs(slog.Duration("handler_duration", duration)) } return err }, ) handler := slogmulti.Pipe(performanceMiddleware).Handler(sinkHandler) ``` -------------------------------- ### Complete Inline Middleware Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Combine multiple inline middleware functions for comprehensive log processing. This provides a flexible way to build complex middleware chains. ```go middleware := slogmulti.NewInlineMiddleware( enabledFunc, handleFunc, withAttrsFunc, withGroupFunc, ) ``` -------------------------------- ### Implement failover logging Source: https://github.com/samber/slog-multi/blob/main/_autodocs/00-START-HERE.md Use the Failover handler to ensure logs are sent to a primary handler, with a backup handler used if the primary fails. This provides resilience for critical logs. ```go handler := slogmulti.Failover()( primaryHandler, backupHandler, ) ``` -------------------------------- ### Middleware Transformation Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Implement middleware transformations by piping multiple middleware handlers before the final sink handler. ```go logger := slog.New( slogmulti.Pipe( gdprMiddleware, errorFormattingMiddleware, timestampMiddleware, ).Handler(sinkHandler), ) ``` -------------------------------- ### Pipe / Middleware Chain Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Build a chain of middleware handlers that process log records before a final sink handler. Each middleware is added using the 'Pipe' method. ```go handler := slogmulti.Pipe(middleware1, middleware2).Handler(finalHandler) ``` -------------------------------- ### Inline Implementations Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/index.md Concrete implementations of inline handlers and middleware. ```APIDOC ## Inline Implementations - **InlineHandler** — Custom handler with functions - **HandleInlineHandler** — Handle-only handler - **InlineMiddleware** — Complete middleware - **EnabledInlineMiddleware** — Enabled-only middleware - **HandleInlineMiddleware** — Handle-only middleware - **WithAttrsInlineMiddleware** — WithAttrs-only middleware - **WithGroupInlineMiddleware** — WithGroup-only middleware ``` -------------------------------- ### Handle Inline Middleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Configure middleware to handle log records with custom logic. Use this for transforming or processing log data before it's passed on. ```go middleware := slogmulti.NewHandleInlineMiddleware(func(ctx, record, next) error { // Custom logic return next(ctx, record) }) ``` -------------------------------- ### Router Signature Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/router.md Defines the signature for creating a router instance. ```go func (h *router) FirstMatch() *router ``` -------------------------------- ### Fanout Handler Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Use Fanout to send all log records to multiple handlers. No configuration options are available. ```go handler := slogmulti.Fanout(handler1, handler2, handler3) ``` -------------------------------- ### slog-multi File Organization Structure Source: https://github.com/samber/slog-multi/blob/main/_autodocs/README.md Illustrates the directory and file structure of the slog-multi project. This helps in understanding where different components and documentation files are located. ```plaintext output/ ├── README.md (this file) ├── overview.md (project overview) ├── types.md (type definitions) ├── configuration.md (configuration options) ├── patterns.md (16 common patterns) └── api-reference/ ├── index.md (API index) ├── fanout.md (FanoutHandler) ├── router.md (Router) ├── router-predicates.md (predicates) ├── pipe.md (middleware chaining) ├── pool.md (PoolHandler) ├── failover.md (FailoverHandler) ├── firstmatch.md (FirstMatchHandler) ├── recover.md (error recovery) ├── inline-handlers.md (inline handlers) └── inline-middleware.md (inline middleware) ``` -------------------------------- ### High-Volume Logging Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Configure a logger for high-volume scenarios using a pool to distribute logs across multiple handlers concurrently. ```go logger := slog.New( slogmulti.Pool()( handler1, handler2, handler3, ), ) ``` -------------------------------- ### Enable First-Match Routing Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/router.md Enables first-match routing mode. Logs are routed to the first handler that matches the predicates. Use this when you want a log to be processed by only one handler. ```go handler := slogmulti.Router(). Add(queryHandler, slogmulti.AttrKindIs("query", slog.KindString)). Add(requestHandler, slogmulti.AttrKindIs("method", slog.KindString)). Add(fallbackHandler). FirstMatch(). Handler() // Each log is routed to exactly one handler (the first that matches) ``` -------------------------------- ### Pipe Methods Source: https://github.com/samber/slog-multi/blob/main/_autodocs/overview.md Methods for building a chain of middleware handlers. ```APIDOC ## Pipe Methods ### Pipe - **Purpose**: Add a middleware to the chain. - **Signature**: `Pipe(Middleware)` ### Handler - **Purpose**: Finalize the middleware chain with a sink handler. - **Signature**: `Handler(slog.Handler)` ``` -------------------------------- ### Failover Handler Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Use Failover to try handlers in order until one succeeds in processing the log record. No configuration options are available. ```go handler := slogmulti.Failover()(handler1, handler2, handler3) ``` -------------------------------- ### Create a FanoutHandler Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/fanout.md Use the `Fanout` function to create a new FanoutHandler. This function takes a variable number of `slog.Handler` instances and returns a single `slog.Handler` that distributes records to all of them. If only one handler is provided, it is returned directly. If a FanoutHandler is passed as an argument, its handlers are flattened. ```go package main import ( "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { handler := slogmulti.Fanout( slog.NewJSONHandler(os.Stdout, nil), slog.NewTextHandler(os.Stderr, nil), ) logger := slog.New(handler) logger.Info("message distributed to both handlers") } ``` -------------------------------- ### NewInlineMiddleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/inline-middleware.md Creates a middleware that implements all handler methods. It takes functions that hook into Enabled, Handle, WithAttrs, and WithGroup methods. ```APIDOC ## Function: NewInlineMiddleware Creates a middleware that implements all handler methods. ### Signature ```go func NewInlineMiddleware( enabledFunc func(ctx context.Context, level slog.Level, next func(context.Context, slog.Level) bool) bool, handleFunc func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error, withAttrsFunc func(attrs []slog.Attr, next func([]slog.Attr) slog.Handler) slog.Handler, withGroupFunc func(name string, next func(string) slog.Handler) slog.Handler, ) Middleware ``` ### Parameters | Parameter | Type | Description | |-----------|------|-------------| | enabledFunc | Function that hooks into Enabled method | Determines if the handler is enabled for a log level | | handleFunc | Function that hooks into Handle method | Processes log records | | withAttrsFunc | Function that hooks into WithAttrs method | Handles attribute propagation | | withGroupFunc | Function that hooks into WithGroup method | Handles group propagation | ### Return Returns a `Middleware` function that can be used with `Pipe()`. ### Panics - If any function parameter is nil ### Example ```go package main import ( "context" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { middleware := slogmulti.NewInlineMiddleware( // Enabled: passthrough func(ctx context.Context, level slog.Level, next func(context.Context, slog.Level) bool) bool { return next(ctx, level) }, // Handle: add custom attribute func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error { record.AddAttrs(slog.String("custom", "value")) return next(ctx, record) }, // WithAttrs: passthrough func(attrs []slog.Attr, next func([]slog.Attr) slog.Handler) slog.Handler { return next(attrs) }, // WithGroup: passthrough func(name string, next func(string) slog.Handler) slog.Handler { return next(name) }, ) handler := slogmulti.Pipe(middleware).Handler(slog.NewJSONHandler(os.Stderr, nil)) logger := slog.New(handler) logger.Info("message") } ``` ### Source `github.com/samber/slog-multi/middleware_inline.go:10` ``` -------------------------------- ### Main Functions Source: https://github.com/samber/slog-multi/blob/main/_autodocs/overview.md Core functions for creating different types of log handlers and middleware chains. ```APIDOC ## Main Functions ### Fanout - **Purpose**: Create a fanout handler that duplicates logs to multiple handlers. - **Signature**: `Fanout(slog.Handler, ...slog.Handler)` ### Router - **Purpose**: Create a router for conditional routing of log records. - **Signature**: `Router() slog.Handler ### Failover - **Purpose**: Create a failover handler factory, allowing a fallback handler if the primary fails. - **Signature**: `Failover() slog.HandlerFactory ### Pool - **Purpose**: Create a pool handler factory for managing a pool of handlers. - **Signature**: `Pool() slog.HandlerFactory ### Pipe - **Purpose**: Create a middleware chain builder. - **Signature**: `Pipe(...Middleware)` ### RecoverHandlerError - **Purpose**: Create error recovery middleware. - **Signature**: `RecoverHandlerError(RecoveryFunc)` ``` -------------------------------- ### InlineMiddleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/types.md InlineMiddleware allows implementing middleware using provided function implementations for various slog.Handler hooks. ```APIDOC ## Type: InlineMiddleware Implements middleware using provided function implementations. ### Definition ```go type InlineMiddleware struct { next slog.Handler enabledFunc func(ctx context.Context, level slog.Level, next func(context.Context, slog.Level) bool) bool handleFunc func(ctx context.Context, record slog.Record, next func(context.Context, slog.Record) error) error withAttrsFunc func(attrs []slog.Attr, next func([]slog.Attr) slog.Handler) slog.Handler withGroupFunc func(name string, next func(string) slog.Handler) slog.Handler } ``` ### Fields | Field | Type | Description | |-------|------|-------------| | next | `slog.Handler` | The next handler in the chain | | enabledFunc | Function | Implementation of Enabled hook | | handleFunc | Function | Implementation of Handle hook | | withAttrsFunc | Function | Implementation of WithAttrs hook | | withGroupFunc | Function | Implementation of WithGroup hook | ### Implements - `slog.Handler` interface ### Created by - `NewInlineMiddleware(func(...), func(...), func(...), func(...)) Middleware` — Creates complete middleware ### Source `github.com/samber/slog-multi/middleware_inline.go:45` ``` -------------------------------- ### FirstMatch() Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/router.md Enables first-match routing mode. In this mode, logs are routed to the first handler that matches the criteria, and processing stops for that log. ```APIDOC ## Method: FirstMatch Enables first-match routing mode where logs are routed to the first matching handler only. ### Signature ```go func (h *router) FirstMatch() *router ``` ### Return Returns a `*router` instance with first-match mode enabled. ### Example ```go handler := slogmulti.Router(). Add(queryHandler, slogmulti.AttrKindIs("query", slog.KindString)). Add(requestHandler, slogmulti.AttrKindIs("method", slog.KindString)). Add(fallbackHandler). FirstMatch(). Handler() // Each log is routed to exactly one handler (the first that matches) ``` ``` -------------------------------- ### Exported Functions Source: https://github.com/samber/slog-multi/blob/main/_autodocs/MANIFEST.md These are the main entry points and factories provided by the slog-multi library for building logging configurations. ```APIDOC ## Exported Functions ### Fanout() Main fanout entry point. ### Router() Router builder. ### Failover() Failover factory. ### Pool() Pool factory. ### Pipe() Middleware chain builder. ### FirstMatch() First-match router. ### RecoverHandlerError() Error recovery middleware. ### LevelIs() Level predicate. ### LevelIsNot() Inverted level predicate. ### MessageIs() Message predicate. ### MessageIsNot() Inverted message predicate. ### MessageContains() Substring predicate. ### MessageNotContains() Inverted substring predicate. ### AttrValueIs() Attribute value predicate. ### AttrKindIs() Attribute kind predicate. ### NewInlineHandler() Inline handler factory. ### NewHandleInlineHandler() Handle-only handler factory. ### NewInlineMiddleware() Complete middleware factory. ### NewEnabledInlineMiddleware() Enabled-only middleware factory. ### NewHandleInlineMiddleware() Handle-only middleware factory. ### NewWithAttrsInlineMiddleware() WithAttrs-only middleware factory. ### NewWithGroupInlineMiddleware() WithGroup-only middleware factory. ``` -------------------------------- ### Add Prefix to Group Names with WithGroup Middleware Source: https://github.com/samber/slog-multi/blob/main/README.md Create middleware to automatically add a prefix, such as 'app.', to log group names. This helps in organizing logs from different application modules. ```go mdw := slogmulti.NewWithGroupInlineMiddleware(func(name string, next func(string) slog.Handler) slog.Handler{ // Add prefix to group names prefixedName := "app." + name return next(name) }) ``` -------------------------------- ### Handle Method Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/firstmatch.md Distributes a log record to the first matching handler. Returns the error from the first matching handler, or nil if no handlers matched. ```APIDOC ## Method: Handle ### Description Distributes a log record to the first matching handler. It iterates through handlers, checks predicates, and forwards the record to the first enabled and matching handler. ### Signature ```go func (h *FirstMatchHandler) Handle(ctx context.Context, r slog.Record) error ``` ### Parameters #### Path Parameters - ctx (context.Context) - The context for the logging operation - r (slog.Record) - The log record to process ### Return Returns the error from the first matching handler, or nil if no handlers matched. ### Details 1. Iterates through each child handler in order. 2. Checks if the handler's predicates match the record. 3. If a match is found and the handler is enabled, forwards the record and returns. 4. Returns immediately after the first match (stops processing further handlers). 5. Returns nil if no handlers match. ``` -------------------------------- ### Load Balancing Logs with slogmulti.Pool() Source: https://github.com/samber/slog-multi/blob/main/README.md Distributes logging load across multiple handlers using round-robin with randomization. Useful for high-throughput logging and distributed logging infrastructure. ```go import ( "net" slogmulti "github.com/samber/slog-multi" "log/slog" "os" "time" ) func main() { // Create multiple log servers // ncat -l 1000 -k // ncat -l 1001 -k // ncat -l 1002 -k // List AZs - use github.com/netbrain/goautosocket for auto-reconnect logstash1, _ := net.Dial("tcp", "logstash.eu-west-3a.internal:1000") logstash2, _ := net.Dial("tcp", "logstash.eu-west-3b.internal:1000") logstash3, _ := net.Dial("tcp", "logstash.eu-west-3c.internal:1000") logger := slog.New( slogmulti.Pool()( // A random handler will be picked for each log slog.HandlerOptions{}.NewJSONHandler(logstash1, nil), slog.HandlerOptions{}.NewJSONHandler(logstash2, nil), slog.HandlerOptions{}.NewJSONHandler(logstash3, nil), ), ) // High-volume logging for i := 0; i < 1000; i++ { logger. With( slog.Group("user", slog.String("id", "user-123"), slog.Time("created_at", time.Now()), ), ). With("environment", "dev"). With("error", fmt.Errorf("an error")), Error("A message") } } ``` -------------------------------- ### Router Handler Configuration Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Configure a Router handler to conditionally route log records to different handlers based on predicates. Enable 'FirstMatch' to stop routing after the first successful match. ```go handler := slogmulti.Router(). Add(handler1, predicate1). Add(handler2, predicate2). Handler() ``` -------------------------------- ### Enable Inline Middleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Configure middleware to enable or disable logging based on custom logic. This is useful for conditional logging. ```go middleware := slogmulti.NewEnabledInlineMiddleware(func(ctx, level, next) bool { // Custom logic return next(ctx, level) }) ``` -------------------------------- ### FirstMatch Function Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/firstmatch.md Creates a first-match router from a variable number of routable handlers. The router will send logs to only the first handler whose predicates match. ```APIDOC ## Function: FirstMatch Creates a first-match router from routable handlers. ### Signature ```go func FirstMatch(handlers ...*RoutableHandler) *FirstMatchHandler ``` ### Parameters | Parameter | Type | Description | |-----------|------|-------------| | handlers | `...*RoutableHandler` | Variable number of RoutableHandler instances | ### Return Returns a `*FirstMatchHandler` that routes to only the first matching handler. ### Example ```go package main import ( "context" "log/slog" "os" slogmulti "github.com/samber/slog-multi" ) func main() { queryHandler := slog.NewJSONHandler(os.Stdout, nil) requestHandler := slog.NewJSONHandler(os.Stdout, nil) fallbackHandler := slog.NewJSONHandler(os.Stderr, nil) handler := slogmulti.Router(). Add(queryHandler, slogmulti.AttrKindIs("query", slog.KindString)). Add(requestHandler, slogmulti.AttrKindIs("method", slog.KindString)). Add(fallbackHandler). FirstMatch(). Handler() logger := slog.New(handler) // Goes to queryHandler only (stops at first match) logger.Debug("Executing SQL query", "query", "SELECT * FROM users WHERE id = ?") // Goes to requestHandler only (queryHandler didn't match) logger.Error("Incoming request failed", "method", "POST") // Goes to fallbackHandler (no other handlers matched) logger.Error("An unexpected error occurred") } ``` ``` -------------------------------- ### Create Inline Handler with Custom Logic Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/inline-handlers.md Use `NewInlineHandler` to create a `slog.Handler` by providing functions for enabling log levels and handling log records. This is useful for custom logging behavior or quick testing. ```go package main import ( "context" "fmt" "log/slog" slogmulti "github.com/samber/slog-multi" ) func main() { handler := slogmulti.NewInlineHandler( // Enabled function: enable all levels func(ctx context.Context, groups []string, attrs []slog.Attr, level slog.Level) bool { return true }, // Handle function: print to stdout func(ctx context.Context, groups []string, attrs []slog.Attr, record slog.Record) error { fmt.Printf("[%s] %s\n", record.Level, record.Message) return nil }, ) logger := slog.New(handler) logger.Info("custom handler works") } ``` -------------------------------- ### Distribute Log Record to First Matching Handler Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/firstmatch.md Distributes a log record to the first handler whose predicates match the record and is enabled. Returns the error from the first matching handler, or nil if no handlers matched. ```go func (h *FirstMatchHandler) Handle(ctx context.Context, r slog.Record) error { // Implementation omitted for brevity } ``` -------------------------------- ### Priority-Based Routing with Router Source: https://github.com/samber/slog-multi/blob/main/_autodocs/patterns.md Implement priority-based routing using the Router handler to direct logs to the first handler that matches specific criteria. This is useful for segregating logs based on severity or other attributes. ```go handler := slogmulti.Router(). Add(criticalHandler, slogmulti.AttrValueIs("severity", "critical")), Add(warningHandler, slogmulti.AttrValueIs("severity", "warning")), Add(infoHandler, slogmulti.AttrValueIs("severity", "info")), Add(debugHandler). // Catch-all FirstMatch(). Handler() logger := slog.New(handler) // Logs go to exactly one handler (the first that matches) ``` -------------------------------- ### Create Failover Handler Factory Source: https://github.com/samber/slog-multi/blob/main/_autodocs/api-reference/failover.md Creates a factory function for FailoverHandler instances. Use this to initialize the failover mechanism with multiple log destinations. ```go package main import ( "log/slog" "net" slogmulti "github.com/samber/slog-multi" ) func main() { primary, _ := net.Dial("tcp", "primary.logs:4242") secondary, _ := net.Dial("tcp", "secondary.logs:4242") backup, _ := net.Dial("tcp", "backup.logs:4242") handler := slogmulti.Failover()( slog.NewJSONHandler(primary, nil), slog.NewJSONHandler(secondary, nil), slog.NewJSONHandler(backup, nil), ) logger := slog.New(handler) logger.Info("message", "description", "Logs to primary, fails over to secondary if primary fails") } ``` -------------------------------- ### Custom Inline Handler with Enabled and Handle Source: https://github.com/samber/slog-multi/blob/main/README.md A more complete inline handler implementation that allows customization of both the Enabled and Handle methods. Useful for fine-grained control over log processing. ```go mdw := slogmulti.NewInlineHandler( // simulate "Enabled()" method func(ctx context.Context, groups []string, attrs []slog.Attr, level slog.Level) bool { // Custom logic here // [...] return true }, // simulate "Handle()" method func(ctx context.Context, groups []string, attrs []slog.Attr, record slog.Record) error { // Custom logic here // [...] return nil }, ) ``` -------------------------------- ### High-Availability Logging with slog-multi Failover Source: https://github.com/samber/slog-multi/blob/main/_autodocs/README.md Ensure log delivery by attempting to send records to a primary handler and falling back to a backup handler if the primary fails. This is useful for critical logging scenarios. ```go handler := slogmulti.Failover()( primaryHandler, backupHandler, ) logger := slog.New(handler) ``` -------------------------------- ### Go: First Match Routing with slogmulti.Router Source: https://github.com/samber/slog-multi/blob/main/README.md Routes logs to the first matching handler only. Use when exactly one handler should process each log based on defined attribute predicates. Ensure all necessary handlers and predicates are configured before enabling FirstMatch(). ```go import ( slogmulti "github.com/samber/slog-multi" slogslack "github.com/samber/slog-slack" "log/slog" ) func main() { queryChannel := slogslack.Option{Level: slog.LevelDebug, WebhookURL: "xxx", Channel: "db-queries"}.NewSlackHandler() requestChannel := slogslack.Option{Level: slog.LevelError, WebhookURL: "xxx", Channel: "service-requests"}.NewSlackHandler() influxdbChannel := slogslack.Option{Level: slog.LevelInfo, WebhookURL: "xxx", Channel: "influxdb-metrics"}.NewSlackHandler() fallbackChannel := slogslack.Option{Level: slog.LevelError, WebhookURL: "xxx", Channel: "logs"}.NewSlackHandler() logger := slog.New( slogmulti.Router(). Add(queryChannel, slogmulti.AttrKindIs("query", slog.KindString, "args", slog.KindAny)). Add(requestChannel, slogmulti.AttrKindIs("method", slog.KindString, "body", slog.KindAny)). Add(influxdbChannel, slogmulti.AttrValueIs("scope", "influx")), Add(fallbackChannel). // Catch-all for everything else FirstMatch(). // ← Enable first-match routing Handler(), ) // Goes to queryChannel only (stops at first match) logger.Debug("Executing SQL query", "query", "SELECT * FROM users WHERE id = ?", "args", []int{1}) // Goes to requestChannel only (stops at first match) logger.Error("Incoming request failed", "method", "POST", "body", "{'name':'test'}") // Goes to fallbackChannel (no other handlers matched) logger.Error("An unexpected error occurred") } ``` -------------------------------- ### WithGroup Inline Middleware Source: https://github.com/samber/slog-multi/blob/main/_autodocs/configuration.md Configure middleware to manage log record grouping. This is useful for organizing logs by a specific name or category. ```go middleware := slogmulti.NewWithGroupInlineMiddleware(func(name, next) slog.Handler { // Custom logic return next(name) }) ```