Logger Package Implementation Requirements
Overview
Implement a flexible, extensible logging package for the Mage project that wraps Go's slog library with enhanced features including colored output, artifact logging, GitHub Actions integration, and per-devtool configurability.
Core Requirements
1. Colored Output (Label-Only Coloring)
- Requirement: Color only the log level labels (DEBUG, INFO, WARN, ERROR), not the entire message
- Rationale: Maintains readability while providing visual distinction between log levels
- Implementation: Use
slog.HandlerOptions.ReplaceAttr to intercept and colorize level labels
- Color Scheme:
DEBUG: Gray + Bold
INFO: Blue + Bold
WARN: Yellow + Bold
ERROR: Red + Bold
- Auto-detection: Automatically detect TTY and disable colors for non-terminal outputs
- Override: Support explicit enable/disable via configuration
2. Artifact Logging
- Requirement: Write logs to both console and artifact file simultaneously
- Use Case: CI/CD pipelines need persistent log files for debugging and archival
- Behavior:
- Multi-writer support (console + file)
- Disable colors in artifact files (plain text only)
- Append mode for artifact files
- Optional path configuration via
LOG_ARTIFACT_PATH environment variable
- File Management: Handle file creation, permissions (0644), and proper opening
3. GitHub Actions Integration
- Requirement: Support GitHub Actions-specific logging formats
- Features Needed:
- Workflow Commands: Emit
::error::, ::warning::, ::notice::, ::debug:: commands
- Step Summary: Write markdown summaries to
$GITHUB_STEP_SUMMARY
- Log Grouping: Support collapsible log groups with
::group:: and ::endgroup::
- Annotations: File/line annotations for errors and warnings
- Detection: Auto-detect GitHub Actions environment via
GITHUB_ACTIONS=true
- Format Example:
::error file=app.go,line=10,col=5::Connection failed
::group::Building Go modules
... nested logs ...
::endgroup::
4. Per-Devtool Configurability
- Requirement: Different devtools need different logging configurations
- Configuration Dimensions:
- Log Level: Each devtool may have different verbosity needs
- Output Format: Some tools output JSON, others text
- Artifact Paths: Tool-specific artifact files (e.g.,
golangci-lint.log, prettier.log)
- Source Location: File/line information (expensive, only when needed)
- Output Parsing: Some tools need their output parsed and reformatted
- Configuration Sources (priority order):
- Explicit code configuration (highest priority)
- Environment variables (e.g.,
LOG_LEVEL_GOLANG=debug)
- Configuration file (future:
.mage/logging.yaml)
- Global defaults (lowest priority)
- Example Configuration:
// Global default
logger.SetGlobalLevel(slog.LevelInfo)
// Per-devtool override
golangLogger := logger.New(
logger.WithDevTool("golang"),
logger.WithLevel(slog.LevelDebug),
logger.WithArtifact("artifacts/golang.log"),
logger.WithSource(true),
)
5. Structured Logging with Context
- Requirement: Support key-value pairs and context propagation
- Features:
- Structured key-value logging:
log.Info("message", "key", value)
- Context variants:
InfoContext(ctx, "message", "key", value)
- Logger chaining with attributes:
log.With("package", "golang").Info("message")
- Attribute inheritance through context
- Use Cases for CLI Dev Tooling:
- Track which devtool is running (e.g.,
tool=golangci-lint)
- Track operation/command being executed (e.g.,
operation=lint, command=check)
- Track file paths or directories being processed (e.g.,
directory=/repo/cmd)
- Track GitHub Actions job/step context (e.g.,
job=build, step=3)
- Correlate logs across multiple tool invocations in a pipeline
- Track performance metrics (e.g.,
duration=2.3s, files_processed=42)
6. Environment Variable Configuration
- Required Variables:
LOG_LEVEL: Global log level (debug, info, warn, error)
LOG_ARTIFACT_PATH: Path to artifact file
LOG_FORMAT: Output format (text, json, github) [future]
LOG_COLOR: Force enable/disable colors (true, false, auto)
LOG_LEVEL_<DEVTOOL>: Per-devtool level overrides (e.g., LOG_LEVEL_GOLANG=debug)
- Behavior: Environment variables should be read once at initialization, not on every log call
7. Output Format Support
- Current: Text format via
slog.TextHandler
- Future Requirements:
- JSON Format: Machine-parseable structured logs
- GitHub Format: GitHub Actions workflow commands
- Custom Formats: Pluggable handler interface
- Format Selection: Via
LOG_FORMAT env var or WithFormat() option
Technical Constraints
1. Use Standard Library slog
- Rationale: Go 1.21+ standard library, no external dependencies
- Approach: Wrap
slog rather than replace it
- Benefits: Familiar API, battle-tested, forward-compatible
2. Zero Breaking Changes
- Requirement: Logger API must be stable from day one
- Strategy: Use options pattern for all configuration
- Versioning: Consider this v1.0 API - no breaking changes allowed
3. Performance Considerations
- Requirement: Minimal overhead, especially when logs are disabled
- Constraints:
- Level checks should be fast (slog handles this)
- Color detection done once at initialization
- No string formatting when log level disabled
- File I/O should not block logging calls
Integration Points
1. Global vs Local Loggers
- Global Default:
logger.Default() for simple cases
- Package Loggers:
logger.Default().With("package", "golang") for package-level
- Operation Loggers:
logger.New(opts...) for specific operations
- Context Loggers: Pass loggers through context when needed
File Structure
internal/logger/
├── logger.go
├── options.go
├── colors.go
├── github.go
├── formats.go
└── logger_test.go
Open Questions & Trade-offs
1. File Lifecycle Management
Question: How should artifact files be closed?
- Option A: Keep file handle open for duration of program (simpler)
- Option B: Add
Close() method, require manual cleanup (cleaner)
- Option C: Use
sync.Pool for file handles (complex)
- Recommendation: Start with Option A, add Option B if needed
2. Log Level Granularity
Question: Should we support more granular levels than debug/info/warn/error?
- Option A: Stick with standard slog levels (simpler)
- Option B: Add custom levels like TRACE, FATAL (more flexible)
- Recommendation: Start with Option A, slog's four levels are sufficient
3. Configuration File Support
Question: Should we support a configuration file (e.g., .mage/logging.yaml)?
- Pros: Centralized configuration, version controlled
- Cons: Additional complexity, file parsing overhead
- Recommendation: Environment variables
4. Performance vs. Features
Note: Synchronous logging features might block or introduce small perf impact.
I don't think this is a big deal now. Just putting it down as a note.
5. GitHub Actions Auto-Detection
Question: Should GitHub format be auto-enabled when GITHUB_ACTIONS=true?
- Option A: Auto-enable (convenience, less configuration)
- Option B: Require explicit opt-in (explicit, user control)
- Recommendation: Auto-enable with ability to override
Logger Package Implementation Requirements
Overview
Implement a flexible, extensible logging package for the Mage project that wraps Go's
sloglibrary with enhanced features including colored output, artifact logging, GitHub Actions integration, and per-devtool configurability.Core Requirements
1. Colored Output (Label-Only Coloring)
slog.HandlerOptions.ReplaceAttrto intercept and colorize level labelsDEBUG: Gray + BoldINFO: Blue + BoldWARN: Yellow + BoldERROR: Red + Bold2. Artifact Logging
LOG_ARTIFACT_PATHenvironment variable3. GitHub Actions Integration
::error::,::warning::,::notice::,::debug::commands$GITHUB_STEP_SUMMARY::group::and::endgroup::GITHUB_ACTIONS=true4. Per-Devtool Configurability
golangci-lint.log,prettier.log)LOG_LEVEL_GOLANG=debug).mage/logging.yaml)5. Structured Logging with Context
log.Info("message", "key", value)InfoContext(ctx, "message", "key", value)log.With("package", "golang").Info("message")tool=golangci-lint)operation=lint,command=check)directory=/repo/cmd)job=build,step=3)duration=2.3s,files_processed=42)6. Environment Variable Configuration
LOG_LEVEL: Global log level (debug, info, warn, error)LOG_ARTIFACT_PATH: Path to artifact fileLOG_FORMAT: Output format (text, json, github) [future]LOG_COLOR: Force enable/disable colors (true, false, auto)LOG_LEVEL_<DEVTOOL>: Per-devtool level overrides (e.g.,LOG_LEVEL_GOLANG=debug)7. Output Format Support
slog.TextHandlerLOG_FORMATenv var orWithFormat()optionTechnical Constraints
1. Use Standard Library
slogslograther than replace it2. Zero Breaking Changes
3. Performance Considerations
Integration Points
1. Global vs Local Loggers
logger.Default()for simple caseslogger.Default().With("package", "golang")for package-levellogger.New(opts...)for specific operationsFile Structure
Open Questions & Trade-offs
1. File Lifecycle Management
Question: How should artifact files be closed?
Close()method, require manual cleanup (cleaner)sync.Poolfor file handles (complex)2. Log Level Granularity
Question: Should we support more granular levels than debug/info/warn/error?
3. Configuration File Support
Question: Should we support a configuration file (e.g.,
.mage/logging.yaml)?4. Performance vs. Features
Note: Synchronous logging features might block or introduce small perf impact.
I don't think this is a big deal now. Just putting it down as a note.
5. GitHub Actions Auto-Detection
Question: Should GitHub format be auto-enabled when
GITHUB_ACTIONS=true?