Skip to content

Commit 0c62e84

Browse files
authored
feat: export BaseMetrics for forward-compatible custom implementations (#2)
* feat: export BaseMetrics for forward-compatible custom implementations Replace unexported noopMetrics with exported BaseMetrics. Custom Metrics implementations embed BaseMetrics so that new methods added to the interface get safe no-op defaults. Remove redundant NoopMetrics var — use &BaseMetrics{} directly. * fix: use BaseMetrics{} value type to avoid allocation, guard nil WithMetrics
1 parent efe3938 commit 0c62e84

4 files changed

Lines changed: 143 additions & 47 deletions

File tree

README.md

Lines changed: 113 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,14 @@ shutdown complete
192192
- [func EveryInterval\(d time.Duration, fn func\(WorkerContext\) error\) func\(WorkerContext\) error](<#EveryInterval>)
193193
- [func Run\(ctx context.Context, workers \[\]\*Worker, opts ...RunOption\) error](<#Run>)
194194
- [func RunWorker\(ctx context.Context, w \*Worker, opts ...RunOption\)](<#RunWorker>)
195+
- [type BaseMetrics](<#BaseMetrics>)
196+
- [func \(BaseMetrics\) ObserveRunDuration\(string, time.Duration\)](<#BaseMetrics.ObserveRunDuration>)
197+
- [func \(BaseMetrics\) SetActiveWorkers\(int\)](<#BaseMetrics.SetActiveWorkers>)
198+
- [func \(BaseMetrics\) WorkerFailed\(string, error\)](<#BaseMetrics.WorkerFailed>)
199+
- [func \(BaseMetrics\) WorkerPanicked\(string\)](<#BaseMetrics.WorkerPanicked>)
200+
- [func \(BaseMetrics\) WorkerRestarted\(string, int\)](<#BaseMetrics.WorkerRestarted>)
201+
- [func \(BaseMetrics\) WorkerStarted\(string\)](<#BaseMetrics.WorkerStarted>)
202+
- [func \(BaseMetrics\) WorkerStopped\(string\)](<#BaseMetrics.WorkerStopped>)
195203
- [type Metrics](<#Metrics>)
196204
- [func NewPrometheusMetrics\(namespace string\) Metrics](<#NewPrometheusMetrics>)
197205
- [type RunOption](<#RunOption>)
@@ -210,7 +218,7 @@ shutdown complete
210218

211219

212220
<a name="BatchChannelWorker"></a>
213-
## func BatchChannelWorker
221+
## func [BatchChannelWorker](<https://github.com/go-coldbrew/workers/blob/main/helpers.go#L49>)
214222

215223
```go
216224
func BatchChannelWorker[T any](ch <-chan T, maxSize int, maxDelay time.Duration, fn func(WorkerContext, []T) error) func(WorkerContext) error
@@ -265,7 +273,7 @@ func main() {
265273
</details>
266274

267275
<a name="ChannelWorker"></a>
268-
## func ChannelWorker
276+
## func [ChannelWorker](<https://github.com/go-coldbrew/workers/blob/main/helpers.go#L27>)
269277

270278
```go
271279
func ChannelWorker[T any](ch <-chan T, fn func(WorkerContext, T) error) func(WorkerContext) error
@@ -321,7 +329,7 @@ world
321329
</details>
322330

323331
<a name="EveryInterval"></a>
324-
## func EveryInterval
332+
## func [EveryInterval](<https://github.com/go-coldbrew/workers/blob/main/helpers.go#L8>)
325333

326334
```go
327335
func EveryInterval(d time.Duration, fn func(WorkerContext) error) func(WorkerContext) error
@@ -373,7 +381,7 @@ tick 2
373381
</details>
374382

375383
<a name="Run"></a>
376-
## func Run
384+
## func [Run](<https://github.com/go-coldbrew/workers/blob/main/run.go#L120>)
377385

378386
```go
379387
func Run(ctx context.Context, workers []*Worker, opts ...RunOption) error
@@ -429,7 +437,7 @@ all workers stopped
429437
</details>
430438

431439
<a name="RunWorker"></a>
432-
## func RunWorker
440+
## func [RunWorker](<https://github.com/go-coldbrew/workers/blob/main/run.go#L143>)
433441

434442
```go
435443
func RunWorker(ctx context.Context, w *Worker, opts ...RunOption)
@@ -478,10 +486,93 @@ done
478486
</p>
479487
</details>
480488

489+
<a name="BaseMetrics"></a>
490+
## type [BaseMetrics](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L43>)
491+
492+
BaseMetrics provides no\-op implementations of all Metrics methods. Embed it in custom Metrics implementations so that new methods added to the Metrics interface in future versions get safe no\-op defaults instead of breaking your build:
493+
494+
```
495+
type myMetrics struct {
496+
workers.BaseMetrics // forward-compatible
497+
client *statsd.Client
498+
}
499+
500+
func (m *myMetrics) WorkerStarted(name string) {
501+
m.client.Incr("worker.started", []string{"worker:" + name}, 1)
502+
}
503+
```
504+
505+
```go
506+
type BaseMetrics struct{}
507+
```
508+
509+
<a name="BaseMetrics.ObserveRunDuration"></a>
510+
### func \(BaseMetrics\) [ObserveRunDuration](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L50>)
511+
512+
```go
513+
func (BaseMetrics) ObserveRunDuration(string, time.Duration)
514+
```
515+
516+
517+
518+
<a name="BaseMetrics.SetActiveWorkers"></a>
519+
### func \(BaseMetrics\) [SetActiveWorkers](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L51>)
520+
521+
```go
522+
func (BaseMetrics) SetActiveWorkers(int)
523+
```
524+
525+
526+
527+
<a name="BaseMetrics.WorkerFailed"></a>
528+
### func \(BaseMetrics\) [WorkerFailed](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L48>)
529+
530+
```go
531+
func (BaseMetrics) WorkerFailed(string, error)
532+
```
533+
534+
535+
536+
<a name="BaseMetrics.WorkerPanicked"></a>
537+
### func \(BaseMetrics\) [WorkerPanicked](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L47>)
538+
539+
```go
540+
func (BaseMetrics) WorkerPanicked(string)
541+
```
542+
543+
544+
545+
<a name="BaseMetrics.WorkerRestarted"></a>
546+
### func \(BaseMetrics\) [WorkerRestarted](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L49>)
547+
548+
```go
549+
func (BaseMetrics) WorkerRestarted(string, int)
550+
```
551+
552+
553+
554+
<a name="BaseMetrics.WorkerStarted"></a>
555+
### func \(BaseMetrics\) [WorkerStarted](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L45>)
556+
557+
```go
558+
func (BaseMetrics) WorkerStarted(string)
559+
```
560+
561+
562+
563+
<a name="BaseMetrics.WorkerStopped"></a>
564+
### func \(BaseMetrics\) [WorkerStopped](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L46>)
565+
566+
```go
567+
func (BaseMetrics) WorkerStopped(string)
568+
```
569+
570+
571+
481572
<a name="Metrics"></a>
482-
## type Metrics
573+
## type [Metrics](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L20-L28>)
483574

484-
Metrics collects worker lifecycle metrics. Implement this interface to provide custom metrics \(e.g., Datadog, StatsD\). Use NoopMetrics to disable metrics, or NewPrometheusMetrics for the built\-in Prometheus implementation.
575+
Metrics collects worker lifecycle metrics. Implement this interface to provide custom metrics \(e.g., Datadog, StatsD\). Use BaseMetrics\{\} to disable metrics, or NewPrometheusMetrics for the built\-in Prometheus implementation.
485576

486577
```go
487578
type Metrics interface {
@@ -495,14 +586,8 @@ type Metrics interface {
495586
}
496587
```
497588

498-
<a name="NoopMetrics"></a>NoopMetrics is a no\-op implementation of Metrics. Used as the default when no metrics are configured via WithMetrics.
499-
500-
```go
501-
var NoopMetrics Metrics = &noopMetrics{}
502-
```
503-
504589
<a name="NewPrometheusMetrics"></a>
505-
### func NewPrometheusMetrics
590+
### func [NewPrometheusMetrics](<https://github.com/go-coldbrew/workers/blob/main/metrics.go#L71>)
506591

507592
```go
508593
func NewPrometheusMetrics(namespace string) Metrics
@@ -511,7 +596,7 @@ func NewPrometheusMetrics(namespace string) Metrics
511596
NewPrometheusMetrics creates a Metrics implementation backed by Prometheus. The namespace is prepended to all metric names \(e.g., "myapp""myapp\_worker\_started\_total"\). Metrics are auto\-registered with the default Prometheus registry. Safe to call multiple times with the same namespace — returns the cached instance. The cache is process\-global; use a small number of static namespaces \(not per\-request/tenant values\).
512597

513598
<a name="RunOption"></a>
514-
## type RunOption
599+
## type [RunOption](<https://github.com/go-coldbrew/workers/blob/main/run.go#L15>)
515600

516601
RunOption configures the behavior of Run.
517602

@@ -520,16 +605,16 @@ type RunOption func(*runConfig)
520605
```
521606

522607
<a name="WithMetrics"></a>
523-
### func WithMetrics
608+
### func [WithMetrics](<https://github.com/go-coldbrew/workers/blob/main/run.go#L24>)
524609

525610
```go
526611
func WithMetrics(m Metrics) RunOption
527612
```
528613

529-
WithMetrics sets the metrics implementation for all workers started by Run. Workers inherit this unless they override via Worker.WithMetrics. If not set, NoopMetrics is used.
614+
WithMetrics sets the metrics implementation for all workers started by Run. Workers inherit this unless they override via Worker.WithMetrics. If not set, BaseMetrics\{\} is used.
530615

531616
<a name="Worker"></a>
532-
## type Worker
617+
## type [Worker](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L127-L137>)
533618

534619
Worker represents a background goroutine managed by the framework. Create with NewWorker and configure with builder methods.
535620

@@ -540,7 +625,7 @@ type Worker struct {
540625
```
541626

542627
<a name="NewWorker"></a>
543-
### func NewWorker
628+
### func [NewWorker](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L141>)
544629

545630
```go
546631
func NewWorker(name string, run func(WorkerContext) error) *Worker
@@ -588,7 +673,7 @@ worker "greeter" started (attempt 0)
588673
</details>
589674
590675
<a name="Worker.Every"></a>
591-
### func \(\*Worker\) Every
676+
### func \(\*Worker\) [Every](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L198>)
592677
593678
```go
594679
func (w *Worker) Every(d time.Duration) *Worker
@@ -638,7 +723,7 @@ tick 2
638723
</details>
639724

640725
<a name="Worker.WithBackoffJitter"></a>
641-
### func \(\*Worker\) WithBackoffJitter
726+
### func \(\*Worker\) [WithBackoffJitter](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L175>)
642727

643728
```go
644729
func (w *Worker) WithBackoffJitter(jitter suture.Jitter) *Worker
@@ -647,7 +732,7 @@ func (w *Worker) WithBackoffJitter(jitter suture.Jitter) *Worker
647732
WithBackoffJitter adds random jitter to the backoff duration to prevent thundering herd on coordinated restarts.
648733

649734
<a name="Worker.WithFailureBackoff"></a>
650-
### func \(\*Worker\) WithFailureBackoff
735+
### func \(\*Worker\) [WithFailureBackoff](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L168>)
651736

652737
```go
653738
func (w *Worker) WithFailureBackoff(d time.Duration) *Worker
@@ -656,7 +741,7 @@ func (w *Worker) WithFailureBackoff(d time.Duration) *Worker
656741
WithFailureBackoff sets the duration to wait between restarts. Suture default is 15 seconds.
657742

658743
<a name="Worker.WithFailureDecay"></a>
659-
### func \(\*Worker\) WithFailureDecay
744+
### func \(\*Worker\) [WithFailureDecay](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L154>)
660745

661746
```go
662747
func (w *Worker) WithFailureDecay(decay float64) *Worker
@@ -665,7 +750,7 @@ func (w *Worker) WithFailureDecay(decay float64) *Worker
665750
WithFailureDecay sets the rate at which failure count decays over time. A value of 1.0 means failures decay by one per second. Suture default is 1.0.
666751

667752
<a name="Worker.WithFailureThreshold"></a>
668-
### func \(\*Worker\) WithFailureThreshold
753+
### func \(\*Worker\) [WithFailureThreshold](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L161>)
669754

670755
```go
671756
func (w *Worker) WithFailureThreshold(threshold float64) *Worker
@@ -674,7 +759,7 @@ func (w *Worker) WithFailureThreshold(threshold float64) *Worker
674759
WithFailureThreshold sets the number of failures allowed before the supervisor gives up restarting. Suture default is 5.
675760

676761
<a name="Worker.WithMetrics"></a>
677-
### func \(\*Worker\) WithMetrics
762+
### func \(\*Worker\) [WithMetrics](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L189>)
678763

679764
```go
680765
func (w *Worker) WithMetrics(m Metrics) *Worker
@@ -683,7 +768,7 @@ func (w *Worker) WithMetrics(m Metrics) *Worker
683768
WithMetrics sets a per\-worker metrics implementation, overriding the metrics inherited from the parent WorkerContext or Run options.
684769

685770
<a name="Worker.WithRestart"></a>
686-
### func \(\*Worker\) WithRestart
771+
### func \(\*Worker\) [WithRestart](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L147>)
687772

688773
```go
689774
func (w *Worker) WithRestart(restart bool) *Worker
@@ -732,7 +817,7 @@ func main() {
732817
</details>
733818

734819
<a name="Worker.WithTimeout"></a>
735-
### func \(\*Worker\) WithTimeout
820+
### func \(\*Worker\) [WithTimeout](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L182>)
736821

737822
```go
738823
func (w *Worker) WithTimeout(d time.Duration) *Worker
@@ -741,7 +826,7 @@ func (w *Worker) WithTimeout(d time.Duration) *Worker
741826
WithTimeout sets the maximum time to wait for the worker to stop during graceful shutdown. Suture default is 10 seconds.
742827

743828
<a name="WorkerContext"></a>
744-
## type WorkerContext
829+
## type [WorkerContext](<https://github.com/go-coldbrew/workers/blob/main/worker.go#L52-L66>)
745830

746831
WorkerContext extends context.Context with worker metadata and dynamic child worker management. The framework creates these — users never need to implement this interface.
747832

metrics.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var (
1515

1616
// Metrics collects worker lifecycle metrics.
1717
// Implement this interface to provide custom metrics (e.g., Datadog, StatsD).
18-
// Use NoopMetrics to disable metrics, or NewPrometheusMetrics for the built-in
18+
// Use BaseMetrics{} to disable metrics, or NewPrometheusMetrics for the built-in
1919
// Prometheus implementation.
2020
type Metrics interface {
2121
WorkerStarted(name string)
@@ -27,19 +27,28 @@ type Metrics interface {
2727
SetActiveWorkers(count int)
2828
}
2929

30-
// NoopMetrics is a no-op implementation of Metrics. Used as the default
31-
// when no metrics are configured via WithMetrics.
32-
var NoopMetrics Metrics = &noopMetrics{}
33-
34-
type noopMetrics struct{}
35-
36-
func (n *noopMetrics) WorkerStarted(string) {}
37-
func (n *noopMetrics) WorkerStopped(string) {}
38-
func (n *noopMetrics) WorkerPanicked(string) {}
39-
func (n *noopMetrics) WorkerFailed(string, error) {}
40-
func (n *noopMetrics) WorkerRestarted(string, int) {}
41-
func (n *noopMetrics) ObserveRunDuration(string, time.Duration) {}
42-
func (n *noopMetrics) SetActiveWorkers(int) {}
30+
// BaseMetrics provides no-op implementations of all Metrics methods.
31+
// Embed it in custom Metrics implementations so that new methods added
32+
// to the Metrics interface in future versions get safe no-op defaults
33+
// instead of breaking your build:
34+
//
35+
// type myMetrics struct {
36+
// workers.BaseMetrics // forward-compatible
37+
// client *statsd.Client
38+
// }
39+
//
40+
// func (m *myMetrics) WorkerStarted(name string) {
41+
// m.client.Incr("worker.started", []string{"worker:" + name}, 1)
42+
// }
43+
type BaseMetrics struct{}
44+
45+
func (BaseMetrics) WorkerStarted(string) {}
46+
func (BaseMetrics) WorkerStopped(string) {}
47+
func (BaseMetrics) WorkerPanicked(string) {}
48+
func (BaseMetrics) WorkerFailed(string, error) {}
49+
func (BaseMetrics) WorkerRestarted(string, int) {}
50+
func (BaseMetrics) ObserveRunDuration(string, time.Duration) {}
51+
func (BaseMetrics) SetActiveWorkers(int) {}
4352

4453
// prometheusMetrics implements Metrics using Prometheus counters, histograms,
4554
// and gauges registered via promauto.

run.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ type runConfig struct {
2020

2121
// WithMetrics sets the metrics implementation for all workers started by Run.
2222
// Workers inherit this unless they override via Worker.WithMetrics.
23-
// If not set, NoopMetrics is used.
23+
// If not set, BaseMetrics{} is used.
2424
func WithMetrics(m Metrics) RunOption {
2525
return func(c *runConfig) {
26-
c.metrics = m
26+
if m != nil {
27+
c.metrics = m
28+
}
2729
}
2830
}
2931

@@ -97,7 +99,7 @@ func resolveMetrics(w *Worker, parent Metrics) Metrics {
9799
if parent != nil {
98100
return parent
99101
}
100-
return NoopMetrics
102+
return BaseMetrics{}
101103
}
102104

103105
// addWorkerToSupervisor creates a child supervisor for the worker,
@@ -116,7 +118,7 @@ func addWorkerToSupervisor(parent *suture.Supervisor, w *Worker, metrics Metrics
116118
// A worker exiting early (without restart) does not stop other workers.
117119
// Returns nil on clean shutdown.
118120
func Run(ctx context.Context, workers []*Worker, opts ...RunOption) error {
119-
cfg := &runConfig{metrics: NoopMetrics}
121+
cfg := &runConfig{metrics: BaseMetrics{}}
120122
for _, opt := range opts {
121123
opt(cfg)
122124
}

worker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func (wc *workerContext) Children() []string {
117117

118118
func newWorkerContext(ctx context.Context, name string, attempt int, sup *suture.Supervisor, metrics Metrics, active *atomic.Int32) WorkerContext {
119119
if metrics == nil {
120-
metrics = NoopMetrics
120+
metrics = BaseMetrics{}
121121
}
122122
return &workerContext{Context: ctx, name: name, attempt: attempt, sup: sup, metrics: metrics, active: active}
123123
}

0 commit comments

Comments
 (0)