- Notifications
You must be signed in to change notification settings - Fork3.2k
Add logging stack with slog adapter#1574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Changes fromall commits
4938a50e10ff3ecfda0359af271ad0e8fd97cc9b04cfdf37767c932788e323aFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,29 @@ | ||||||
| package log | ||||||
| // Level represents the log level, from debug to fatal | ||||||
| type Level struct { | ||||||
| level string | ||||||
| } | ||||||
| var ( | ||||||
| // DebugLevel causes all logs to be logged | ||||||
| DebugLevel = Level{"debug"} | ||||||
| // InfoLevel causes all logs of level info or more severe to be logged | ||||||
| InfoLevel = Level{"info"} | ||||||
| // WarnLevel causes all logs of level warn or more severe to be logged | ||||||
| WarnLevel = Level{"warn"} | ||||||
| // ErrorLevel causes all logs of level error or more severe to be logged | ||||||
| ErrorLevel = Level{"error"} | ||||||
| // FatalLevel causes only logs of level fatal to be logged | ||||||
CopilotAI | ||||||
| // FatalLevel causesonly logs of level fatal to be logged | |
| // FatalLevel causesall logs of level fatal to be logged |
CopilotAIDec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The example code in the comment contains a syntax error. It showskvp.String("loglevel", log.DebugLevel.String()) but should likely be demonstrating a valid function call.
| // trace.SetLogLevel(kvp.String("loglevel", log.DebugLevel.String()) | |
| // trace.SetLogLevel(kvp.String("loglevel", log.DebugLevel.String())) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package log | ||
| import ( | ||
| "context" | ||
| "log/slog" | ||
| ) | ||
| type Logger interface { | ||
| Log(ctx context.Context, level Level, msg string, fields ...slog.Attr) | ||
| Debug(msg string, fields ...slog.Attr) | ||
| Info(msg string, fields ...slog.Attr) | ||
| Warn(msg string, fields ...slog.Attr) | ||
| Error(msg string, fields ...slog.Attr) | ||
| Fatal(msg string, fields ...slog.Attr) | ||
| WithFields(fields ...slog.Attr) Logger | ||
| WithError(err error) Logger | ||
| Named(name string) Logger | ||
| WithLevel(level Level) Logger | ||
| Sync() error | ||
| Level() Level | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,62 @@ | ||||||||||
| package log | ||||||||||
| import ( | ||||||||||
| "context" | ||||||||||
| "log/slog" | ||||||||||
| ) | ||||||||||
| type NoopLogger struct{} | ||||||||||
| var _ Logger = (*NoopLogger)(nil) | ||||||||||
| func NewNoopLogger() *NoopLogger { | ||||||||||
| return &NoopLogger{} | ||||||||||
| } | ||||||||||
| func (l *NoopLogger) Level() Level { | ||||||||||
| return DebugLevel | ||||||||||
| } | ||||||||||
| func (l *NoopLogger) Log(_ context.Context, _ Level, _ string, _ ...slog.Attr) { | ||||||||||
| // No-op | ||||||||||
| } | ||||||||||
| func (l *NoopLogger) Debug(_ string, _ ...slog.Attr) { | ||||||||||
| // No-op | ||||||||||
| } | ||||||||||
| func (l *NoopLogger) Info(_ string, _ ...slog.Attr) { | ||||||||||
| // No-op | ||||||||||
| } | ||||||||||
| func (l *NoopLogger) Warn(_ string, _ ...slog.Attr) { | ||||||||||
| // No-op | ||||||||||
| } | ||||||||||
| func (l *NoopLogger) Error(_ string, _ ...slog.Attr) { | ||||||||||
| // No-op | ||||||||||
| } | ||||||||||
| func (l *NoopLogger) Fatal(_ string, _ ...slog.Attr) { | ||||||||||
| // No-op | ||||||||||
Comment on lines +40 to +41 CopilotAI | ||||||||||
| func (l*NoopLogger)Fatal(_string,_...slog.Attr) { | |
| // No-op | |
| func (l*NoopLogger)Fatal(msgstring,_...slog.Attr) { | |
| panic("NoopLogger.Fatal called: "+msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Your a badass ai
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Your a badass ai
CopilotAIDec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The NoopLogger implementation lacks test coverage. While it's a simple no-op implementation, tests should verify that it correctly implements the Logger interface and that all methods are safe to call without side effects. Similar packages in this repository have corresponding test files.
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,103 @@ | ||||||||||||||
| package log | ||||||||||||||
| import ( | ||||||||||||||
| "context" | ||||||||||||||
| "log/slog" | ||||||||||||||
| ) | ||||||||||||||
| type SlogLogger struct { | ||||||||||||||
| logger *slog.Logger | ||||||||||||||
| level Level | ||||||||||||||
| } | ||||||||||||||
| func NewSlogLogger(logger *slog.Logger, level Level) *SlogLogger { | ||||||||||||||
| return &SlogLogger{ | ||||||||||||||
| logger: logger, | ||||||||||||||
| level: level, | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| func (l *SlogLogger) Level() Level { | ||||||||||||||
| return l.level | ||||||||||||||
| } | ||||||||||||||
| func (l *SlogLogger) Log(ctx context.Context, level Level, msg string, fields ...slog.Attr) { | ||||||||||||||
CopilotAI | ||||||||||||||
| func (l*SlogLogger)Log(ctx context.Context,levelLevel,msgstring,fields...slog.Attr) { | |
| func (l*SlogLogger)Log(ctx context.Context,levelLevel,msgstring,fields...slog.Attr) { | |
| // Only log messages at or above the configured level | |
| iflevel< l.level { | |
| return | |
| } |
CopilotAIDec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The Fatal method panics unconditionally after logging, which means the log message may not be flushed to its destination before the panic occurs. Since slog buffers writes, the fatal log entry could be lost. Consider calling Sync() on the underlying logger before panicking, or use os.Exit(1) instead of panic to allow deferred functions and cleanup to run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thank you
CopilotAIDec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The SlogLogger adapter lacks test coverage for its core functionality including level conversion, field marshaling, error handling, and logger chaining. Similar packages in this repository (like pkg/log and pkg/sanitize) have comprehensive test files. Consider adding tests to verify: level conversion accuracy, WithFields properly converts slog.Attr to key-value pairs, WithError extracts error strings correctly, and logger chaining with Named/WithLevel preserves the underlying logger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thank you
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,15 @@ | ||||||||||||||||||||||||||||||
| package observability | ||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||
| "github.com/github/github-mcp-server/pkg/observability/log" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| type Exporters struct { | ||||||||||||||||||||||||||||||
| Logger log.Logger | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| func NewExporters(logger log.Logger) *Exporters { | ||||||||||||||||||||||||||||||
| return &Exporters{ | ||||||||||||||||||||||||||||||
Comment on lines +7 to +12 CopilotAI | ||||||||||||||||||||||||||||||
| typeExportersstruct { | |
| Loggerlog.Logger | |
| } | |
| funcNewExporters(logger log.Logger)*Exporters { | |
| return&Exporters{ | |
| // Observability holds observability-related components such as logging. | |
| // Additional fields (e.g., metrics, tracing) can be added here in the future. | |
| typeObservabilitystruct { | |
| Loggerlog.Logger | |
| } | |
| funcNewObservability(logger log.Logger)*Observability { | |
| return&Observability{ |