-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Initial OSS logging adapter for http #2008
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
a8bf0b0
4a2f8ed
ba9d315
cbf99e7
cc5b8e7
77f6e57
3bbfaa0
dd57d84
2a20a2a
1648775
7c5ea0a
121daa3
39445b0
f7d3a75
30b2952
c0a68f5
457f64d
f94b729
e31ea3b
095d9f2
1ae4a03
2b18639
a7778ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package metrics | ||
|
|
||
| import "time" | ||
|
|
||
| // Metrics is a backend-agnostic interface for emitting metrics. | ||
| // Implementations can route to DataDog, log to slog, or discard (noop). | ||
| type Metrics interface { | ||
| Increment(key string, tags map[string]string) | ||
| Counter(key string, tags map[string]string, value int64) | ||
| Distribution(key string, tags map[string]string, value float64) | ||
| DistributionMs(key string, tags map[string]string, value time.Duration) | ||
| WithTags(tags map[string]string) Metrics | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package metrics | ||
|
|
||
| import "time" | ||
|
|
||
| // NoopMetrics is a no-op implementation of the Metrics interface. | ||
| type NoopMetrics struct{} | ||
|
|
||
| var _ Metrics = (*NoopMetrics)(nil) | ||
|
|
||
| // NewNoopMetrics returns a new NoopMetrics. | ||
| func NewNoopMetrics() *NoopMetrics { | ||
| return &NoopMetrics{} | ||
| } | ||
|
|
||
| func (n *NoopMetrics) Increment(_ string, _ map[string]string) {} | ||
| func (n *NoopMetrics) Counter(_ string, _ map[string]string, _ int64) {} | ||
| func (n *NoopMetrics) Distribution(_ string, _ map[string]string, _ float64) {} | ||
| func (n *NoopMetrics) DistributionMs(_ string, _ map[string]string, _ time.Duration) {} | ||
| func (n *NoopMetrics) WithTags(_ map[string]string) Metrics { return n } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package metrics | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func TestNoopMetrics_ImplementsInterface(_ *testing.T) { | ||
| var _ Metrics = (*NoopMetrics)(nil) | ||
| } | ||
|
|
||
| func TestNoopMetrics_NoPanics(t *testing.T) { | ||
| m := NewNoopMetrics() | ||
|
|
||
| assert.NotPanics(t, func() { | ||
| m.Increment("key", map[string]string{"a": "b"}) | ||
| m.Counter("key", map[string]string{"a": "b"}, 1) | ||
| m.Distribution("key", map[string]string{"a": "b"}, 1.5) | ||
| m.DistributionMs("key", map[string]string{"a": "b"}, time.Second) | ||
| }) | ||
| } | ||
|
|
||
| func TestNoopMetrics_NilTags(t *testing.T) { | ||
| m := NewNoopMetrics() | ||
|
|
||
| assert.NotPanics(t, func() { | ||
| m.Increment("key", nil) | ||
| m.Counter("key", nil, 1) | ||
| m.Distribution("key", nil, 1.5) | ||
| m.DistributionMs("key", nil, time.Second) | ||
| }) | ||
| } | ||
|
|
||
| func TestNoopMetrics_WithTags(t *testing.T) { | ||
| m := NewNoopMetrics() | ||
| tagged := m.WithTags(map[string]string{"env": "prod"}) | ||
|
|
||
| assert.NotNil(t, tagged) | ||
| assert.Equal(t, m, tagged) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||||||||||||||||||||||||
| package observability | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||
| "context" | ||||||||||||||||||||||||||||||
| "errors" | ||||||||||||||||||||||||||||||
| "log/slog" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| "github.com/github/github-mcp-server/pkg/observability/metrics" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Exporters bundles observability primitives (logger + metrics) for dependency injection. | ||||||||||||||||||||||||||||||
| // The logger is Go's stdlib *slog.Logger — integrators provide their own slog.Handler. | ||||||||||||||||||||||||||||||
| type Exporters interface { | ||||||||||||||||||||||||||||||
| Logger() *slog.Logger | ||||||||||||||||||||||||||||||
| Metrics(context.Context) metrics.Metrics | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| type exporters struct { | ||||||||||||||||||||||||||||||
| logger *slog.Logger | ||||||||||||||||||||||||||||||
| metrics metrics.Metrics | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // NewExporters creates an Exporters bundle. Pass a configured *slog.Logger | ||||||||||||||||||||||||||||||
| // (with whatever slog.Handler you need) and a Metrics implementation. | ||||||||||||||||||||||||||||||
| // The logger must not be nil; use slog.New(slog.DiscardHandler) if logging is unwanted. | ||||||||||||||||||||||||||||||
| func NewExporters(logger *slog.Logger, metrics metrics.Metrics) (Exporters, error) { | ||||||||||||||||||||||||||||||
| if logger == nil { | ||||||||||||||||||||||||||||||
| return nil, errors.New("logger must not be nil: use slog.New(slog.DiscardHandler) to discard logs") | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
| } | |
| } | |
| if metrics == nil { | |
| return nil, errors.New("metrics must not be nil; provide a no-op metrics implementation if metrics are disabled") | |
| } |
Outdated
Copilot
AI
Mar 26, 2026
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 metrics parameter name shadows the imported metrics package name, which makes this function harder to read/maintain. Renaming the parameter (e.g., m) avoids the shadowing.
| func NewExporters(logger *slog.Logger, metrics metrics.Metrics) (Exporters, error) { | |
| if logger == nil { | |
| return nil, errors.New("logger must not be nil: use slog.New(slog.DiscardHandler) to discard logs") | |
| } | |
| return &exporters{ | |
| logger: logger, | |
| metrics: metrics, | |
| func NewExporters(logger *slog.Logger, m metrics.Metrics) (Exporters, error) { | |
| if logger == nil { | |
| return nil, errors.New("logger must not be nil: use slog.New(slog.DiscardHandler) to discard logs") | |
| } | |
| return &exporters{ | |
| logger: logger, | |
| metrics: m, |
Uh oh!
There was an error while loading. Please reload this page.