Documentation
¶
Overview ¶
Package logger provides namespace-based debug logging with zero overhead when disabled.
The logger package implements a lightweight debug logging system inspired by the debug npm package. It uses the DEBUG environment variable for selective log enabling with pattern matching and namespace coloring. When logging is disabled for a namespace, log calls have zero overhead (not even string formatting).
Basic Usage ¶
var log = logger.New("cli:compile")
func CompileWorkflow(path string) error {
log.Printf("Compiling workflow: %s", path)
// Only executed if DEBUG matches "cli:compile" or "cli:*"
if log.Enabled() {
// Expensive operation only when logging is enabled
log.Printf("Details: %+v", expensiveDebugInfo())
}
return nil
}
Environment Variables ¶
DEBUG - Controls which namespaces are enabled:
DEBUG=* # Enable all namespaces DEBUG=cli:* # Enable all cli namespaces DEBUG=workflow:* # Enable all workflow namespaces DEBUG=cli:*,parser:* # Enable multiple patterns DEBUG=*,-test:* # Enable all except test namespaces
DEBUG_COLORS - Controls color output (default: enabled in terminals):
DEBUG_COLORS=0 # Disable colors (auto-disabled when piping)
Namespace Convention ¶
Follow the pattern: pkg:filename or pkg:component
logger.New("cli:compile_command") # Command-specific
logger.New("workflow:compiler") # Core component
logger.New("parser:frontmatter") # Subcomponent
logger.New("mcp:gateway") # Feature-specific
Use consistent naming across the codebase for easy filtering.
Features ¶
Zero Overhead: Log calls are no-ops when the namespace is disabled. No string formatting or function calls occur.
Time Deltas: Each log shows time elapsed since the previous log in that namespace (e.g., +50ms, +2.5s, +1m30s).
Auto-Colors: Each namespace gets a consistent color in terminals. Colors are generated deterministically from the namespace string.
Pattern Matching: Supports wildcards (*) and exclusions (-pattern) for flexible namespace filtering.
Performance ¶
// No overhead - neither Printf nor Enabled() called when disabled
log.Printf("Debug info: %s", expensiveFunction())
// Check first for expensive operations
if log.Enabled() {
result := expensiveFunction()
log.Printf("Result: %+v", result)
}
Use Cases ¶
Development Debugging: Enable specific namespaces during development to trace execution flow without adding/removing print statements.
Performance Analysis: Time deltas help identify slow operations and bottlenecks in the compilation pipeline.
Production Diagnostics: Users can enable logging to diagnose issues by setting DEBUG environment variable before running commands.
Integration Testing: Tests can enable logging selectively to verify behavior without affecting test output.
Output Format ¶
workflow:compiler Parsing workflow file +0ms workflow:compiler Generating YAML +125ms cli:compile Compilation complete +2.5s
Each line shows: namespace (colored), message, time delta
Best Practices ¶
Use logger.New at package level with consistent namespace naming.
Log significant operations, not every line - focus on key decision points.
Check log.Enabled() before expensive debug operations like JSON marshaling.
Use descriptive messages that make sense without source code context.
Prefer structured data in messages for easy parsing if needed later.
Related Packages ¶
pkg/console - User-facing output formatting (errors, success, warnings)
pkg/timeutil - Time formatting utilities used for delta calculations
pkg/tty - Terminal detection for color support
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExtractErrorMessage ¶ added in v0.32.0
ExtractErrorMessage extracts a clean error message from a log line. It removes timestamps, log level prefixes, and other common noise. If the message is longer than 200 characters, it will be truncated.
func NewSlogLogger ¶ added in v0.33.0
NewSlogLogger creates a new slog.Logger that uses gh-aw's logger package This allows integration with libraries that expect slog.Logger
func NewSlogLoggerWithHandler ¶ added in v0.33.0
NewSlogLoggerWithHandler creates a new slog.Logger using an existing Logger instance
Types ¶
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
Logger represents a debug logger for a specific namespace.
func New ¶
New creates a new Logger for the given namespace. The enabled state is computed at construction time based on the DEBUG environment variable. DEBUG syntax follows https://www.npmjs.com/package/debug patterns:
DEBUG=* - enables all loggers DEBUG=namespace:* - enables all loggers in a namespace DEBUG=ns1,ns2 - enables specific namespaces DEBUG=ns:*,-ns:skip - enables namespace but excludes specific patterns
Colors are automatically assigned to each namespace if DEBUG_COLORS != "0" and stderr is a TTY.
Example ¶
package main
import (
"fmt"
"os"
"github.com/githubnext/gh-aw/pkg/logger"
)
func main() {
// Set DEBUG environment variable to enable loggers
os.Setenv("DEBUG", "app:*")
defer os.Unsetenv("DEBUG")
// Create a logger for a specific namespace
log := logger.New("app:feature")
// Check if logger is enabled
if log.Enabled() {
fmt.Println("Logger is enabled")
}
}
Output: Logger is enabled
Example (Patterns) ¶
package main
import (
"os"
)
func main() {
// Example patterns for DEBUG environment variable
// Enable all loggers
os.Setenv("DEBUG", "*")
// Enable all loggers in workflow namespace
os.Setenv("DEBUG", "workflow:*")
// Enable multiple namespaces
os.Setenv("DEBUG", "workflow:*,cli:*")
// Enable all except specific patterns
os.Setenv("DEBUG", "*,-workflow:test")
// Enable namespace but exclude specific loggers
os.Setenv("DEBUG", "workflow:*,-workflow:cache")
defer os.Unsetenv("DEBUG")
}
func (*Logger) Print ¶
Print prints a message if the logger is enabled. A newline is always added at the end. Time diff since last log is displayed like the debug npm package.
Example ¶
package main
import (
"os"
"github.com/githubnext/gh-aw/pkg/logger"
)
func main() {
// Enable all loggers
os.Setenv("DEBUG", "*")
defer os.Unsetenv("DEBUG")
log := logger.New("app:feature")
// Print concatenates arguments like fmt.Sprint
log.Print("Processing", " ", "items")
// Output to stderr: app:feature Processing items +0ns
}
func (*Logger) Printf ¶
Printf prints a formatted message if the logger is enabled. A newline is always added at the end. Time diff since last log is displayed like the debug npm package.
Example ¶
package main
import (
"os"
"github.com/githubnext/gh-aw/pkg/logger"
)
func main() {
// Enable all loggers
os.Setenv("DEBUG", "*")
defer os.Unsetenv("DEBUG")
log := logger.New("app:feature")
// Printf uses standard fmt.Printf formatting
log.Printf("Processing %d items", 42)
// Output to stderr: app:feature Processing 42 items
}
type SlogHandler ¶ added in v0.33.0
type SlogHandler struct {
// contains filtered or unexported fields
}
SlogHandler implements slog.Handler by wrapping a gh-aw Logger This allows integration with libraries that expect slog.Logger
func NewSlogHandler ¶ added in v0.33.0
func NewSlogHandler(logger *Logger) *SlogHandler
NewSlogHandler creates a new slog.Handler that wraps a gh-aw Logger
func (*SlogHandler) Enabled ¶ added in v0.33.0
Enabled reports whether the handler handles records at the given level. We enable all levels when our logger is enabled.
func (*SlogHandler) Handle ¶ added in v0.33.0
Handle handles the Record. It will only be called when Enabled returns true.