diff --git a/README.md b/README.md
index 6839c03..0e06151 100644
--- a/README.md
+++ b/README.md
@@ -192,6 +192,14 @@ shutdown complete
- [func EveryInterval\(d time.Duration, fn func\(WorkerContext\) error\) func\(WorkerContext\) error](<#EveryInterval>)
- [func Run\(ctx context.Context, workers \[\]\*Worker, opts ...RunOption\) error](<#Run>)
- [func RunWorker\(ctx context.Context, w \*Worker, opts ...RunOption\)](<#RunWorker>)
+- [type BaseMetrics](<#BaseMetrics>)
+ - [func \(BaseMetrics\) ObserveRunDuration\(string, time.Duration\)](<#BaseMetrics.ObserveRunDuration>)
+ - [func \(BaseMetrics\) SetActiveWorkers\(int\)](<#BaseMetrics.SetActiveWorkers>)
+ - [func \(BaseMetrics\) WorkerFailed\(string, error\)](<#BaseMetrics.WorkerFailed>)
+ - [func \(BaseMetrics\) WorkerPanicked\(string\)](<#BaseMetrics.WorkerPanicked>)
+ - [func \(BaseMetrics\) WorkerRestarted\(string, int\)](<#BaseMetrics.WorkerRestarted>)
+ - [func \(BaseMetrics\) WorkerStarted\(string\)](<#BaseMetrics.WorkerStarted>)
+ - [func \(BaseMetrics\) WorkerStopped\(string\)](<#BaseMetrics.WorkerStopped>)
- [type Metrics](<#Metrics>)
- [func NewPrometheusMetrics\(namespace string\) Metrics](<#NewPrometheusMetrics>)
- [type RunOption](<#RunOption>)
@@ -210,7 +218,7 @@ shutdown complete
-## func BatchChannelWorker
+## func [BatchChannelWorker]()
```go
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() {
-## func ChannelWorker
+## func [ChannelWorker]()
```go
func ChannelWorker[T any](ch <-chan T, fn func(WorkerContext, T) error) func(WorkerContext) error
@@ -321,7 +329,7 @@ world
-## func EveryInterval
+## func [EveryInterval]()
```go
func EveryInterval(d time.Duration, fn func(WorkerContext) error) func(WorkerContext) error
@@ -373,7 +381,7 @@ tick 2
-## func Run
+## func [Run]()
```go
func Run(ctx context.Context, workers []*Worker, opts ...RunOption) error
@@ -429,7 +437,7 @@ all workers stopped
-## func RunWorker
+## func [RunWorker]()
```go
func RunWorker(ctx context.Context, w *Worker, opts ...RunOption)
@@ -478,10 +486,93 @@ done
+
+## type [BaseMetrics]()
+
+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:
+
+```
+type myMetrics struct {
+ workers.BaseMetrics // forward-compatible
+ client *statsd.Client
+}
+
+func (m *myMetrics) WorkerStarted(name string) {
+ m.client.Incr("worker.started", []string{"worker:" + name}, 1)
+}
+```
+
+```go
+type BaseMetrics struct{}
+```
+
+
+### func \(BaseMetrics\) [ObserveRunDuration]()
+
+```go
+func (BaseMetrics) ObserveRunDuration(string, time.Duration)
+```
+
+
+
+
+### func \(BaseMetrics\) [SetActiveWorkers]()
+
+```go
+func (BaseMetrics) SetActiveWorkers(int)
+```
+
+
+
+
+### func \(BaseMetrics\) [WorkerFailed]()
+
+```go
+func (BaseMetrics) WorkerFailed(string, error)
+```
+
+
+
+
+### func \(BaseMetrics\) [WorkerPanicked]()
+
+```go
+func (BaseMetrics) WorkerPanicked(string)
+```
+
+
+
+
+### func \(BaseMetrics\) [WorkerRestarted]()
+
+```go
+func (BaseMetrics) WorkerRestarted(string, int)
+```
+
+
+
+
+### func \(BaseMetrics\) [WorkerStarted]()
+
+```go
+func (BaseMetrics) WorkerStarted(string)
+```
+
+
+
+
+### func \(BaseMetrics\) [WorkerStopped]()
+
+```go
+func (BaseMetrics) WorkerStopped(string)
+```
+
+
+
-## type Metrics
+## type [Metrics]()
-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.
+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.
```go
type Metrics interface {
@@ -495,14 +586,8 @@ type Metrics interface {
}
```
-NoopMetrics is a no\-op implementation of Metrics. Used as the default when no metrics are configured via WithMetrics.
-
-```go
-var NoopMetrics Metrics = &noopMetrics{}
-```
-
-### func NewPrometheusMetrics
+### func [NewPrometheusMetrics]()
```go
func NewPrometheusMetrics(namespace string) Metrics
@@ -511,7 +596,7 @@ func NewPrometheusMetrics(namespace string) Metrics
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\).
-## type RunOption
+## type [RunOption]()
RunOption configures the behavior of Run.
@@ -520,16 +605,16 @@ type RunOption func(*runConfig)
```
-### func WithMetrics
+### func [WithMetrics]()
```go
func WithMetrics(m Metrics) RunOption
```
-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.
+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.
-## type Worker
+## type [Worker]()
Worker represents a background goroutine managed by the framework. Create with NewWorker and configure with builder methods.
@@ -540,7 +625,7 @@ type Worker struct {
```
-### func NewWorker
+### func [NewWorker]()
```go
func NewWorker(name string, run func(WorkerContext) error) *Worker
@@ -588,7 +673,7 @@ worker "greeter" started (attempt 0)
-### func \(\*Worker\) Every
+### func \(\*Worker\) [Every]()
```go
func (w *Worker) Every(d time.Duration) *Worker
@@ -638,7 +723,7 @@ tick 2
-### func \(\*Worker\) WithBackoffJitter
+### func \(\*Worker\) [WithBackoffJitter]()
```go
func (w *Worker) WithBackoffJitter(jitter suture.Jitter) *Worker
@@ -647,7 +732,7 @@ func (w *Worker) WithBackoffJitter(jitter suture.Jitter) *Worker
WithBackoffJitter adds random jitter to the backoff duration to prevent thundering herd on coordinated restarts.
-### func \(\*Worker\) WithFailureBackoff
+### func \(\*Worker\) [WithFailureBackoff]()
```go
func (w *Worker) WithFailureBackoff(d time.Duration) *Worker
@@ -656,7 +741,7 @@ func (w *Worker) WithFailureBackoff(d time.Duration) *Worker
WithFailureBackoff sets the duration to wait between restarts. Suture default is 15 seconds.
-### func \(\*Worker\) WithFailureDecay
+### func \(\*Worker\) [WithFailureDecay]()
```go
func (w *Worker) WithFailureDecay(decay float64) *Worker
@@ -665,7 +750,7 @@ func (w *Worker) WithFailureDecay(decay float64) *Worker
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.
-### func \(\*Worker\) WithFailureThreshold
+### func \(\*Worker\) [WithFailureThreshold]()
```go
func (w *Worker) WithFailureThreshold(threshold float64) *Worker
@@ -674,7 +759,7 @@ func (w *Worker) WithFailureThreshold(threshold float64) *Worker
WithFailureThreshold sets the number of failures allowed before the supervisor gives up restarting. Suture default is 5.
-### func \(\*Worker\) WithMetrics
+### func \(\*Worker\) [WithMetrics]()
```go
func (w *Worker) WithMetrics(m Metrics) *Worker
@@ -683,7 +768,7 @@ func (w *Worker) WithMetrics(m Metrics) *Worker
WithMetrics sets a per\-worker metrics implementation, overriding the metrics inherited from the parent WorkerContext or Run options.
-### func \(\*Worker\) WithRestart
+### func \(\*Worker\) [WithRestart]()
```go
func (w *Worker) WithRestart(restart bool) *Worker
@@ -732,7 +817,7 @@ func main() {
-### func \(\*Worker\) WithTimeout
+### func \(\*Worker\) [WithTimeout]()
```go
func (w *Worker) WithTimeout(d time.Duration) *Worker
@@ -741,7 +826,7 @@ func (w *Worker) WithTimeout(d time.Duration) *Worker
WithTimeout sets the maximum time to wait for the worker to stop during graceful shutdown. Suture default is 10 seconds.
-## type WorkerContext
+## type [WorkerContext]()
WorkerContext extends context.Context with worker metadata and dynamic child worker management. The framework creates these — users never need to implement this interface.
diff --git a/metrics.go b/metrics.go
index a30a86f..b1beb19 100644
--- a/metrics.go
+++ b/metrics.go
@@ -15,7 +15,7 @@ var (
// 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
+// Use BaseMetrics{} to disable metrics, or NewPrometheusMetrics for the built-in
// Prometheus implementation.
type Metrics interface {
WorkerStarted(name string)
@@ -27,19 +27,28 @@ type Metrics interface {
SetActiveWorkers(count int)
}
-// NoopMetrics is a no-op implementation of Metrics. Used as the default
-// when no metrics are configured via WithMetrics.
-var NoopMetrics Metrics = &noopMetrics{}
-
-type noopMetrics struct{}
-
-func (n *noopMetrics) WorkerStarted(string) {}
-func (n *noopMetrics) WorkerStopped(string) {}
-func (n *noopMetrics) WorkerPanicked(string) {}
-func (n *noopMetrics) WorkerFailed(string, error) {}
-func (n *noopMetrics) WorkerRestarted(string, int) {}
-func (n *noopMetrics) ObserveRunDuration(string, time.Duration) {}
-func (n *noopMetrics) SetActiveWorkers(int) {}
+// 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:
+//
+// type myMetrics struct {
+// workers.BaseMetrics // forward-compatible
+// client *statsd.Client
+// }
+//
+// func (m *myMetrics) WorkerStarted(name string) {
+// m.client.Incr("worker.started", []string{"worker:" + name}, 1)
+// }
+type BaseMetrics struct{}
+
+func (BaseMetrics) WorkerStarted(string) {}
+func (BaseMetrics) WorkerStopped(string) {}
+func (BaseMetrics) WorkerPanicked(string) {}
+func (BaseMetrics) WorkerFailed(string, error) {}
+func (BaseMetrics) WorkerRestarted(string, int) {}
+func (BaseMetrics) ObserveRunDuration(string, time.Duration) {}
+func (BaseMetrics) SetActiveWorkers(int) {}
// prometheusMetrics implements Metrics using Prometheus counters, histograms,
// and gauges registered via promauto.
diff --git a/run.go b/run.go
index 8cdb297..dd11fc7 100644
--- a/run.go
+++ b/run.go
@@ -20,10 +20,12 @@ type runConfig struct {
// 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.
+// If not set, BaseMetrics{} is used.
func WithMetrics(m Metrics) RunOption {
return func(c *runConfig) {
- c.metrics = m
+ if m != nil {
+ c.metrics = m
+ }
}
}
@@ -97,7 +99,7 @@ func resolveMetrics(w *Worker, parent Metrics) Metrics {
if parent != nil {
return parent
}
- return NoopMetrics
+ return BaseMetrics{}
}
// addWorkerToSupervisor creates a child supervisor for the worker,
@@ -116,7 +118,7 @@ func addWorkerToSupervisor(parent *suture.Supervisor, w *Worker, metrics Metrics
// A worker exiting early (without restart) does not stop other workers.
// Returns nil on clean shutdown.
func Run(ctx context.Context, workers []*Worker, opts ...RunOption) error {
- cfg := &runConfig{metrics: NoopMetrics}
+ cfg := &runConfig{metrics: BaseMetrics{}}
for _, opt := range opts {
opt(cfg)
}
diff --git a/worker.go b/worker.go
index ef59284..b407385 100644
--- a/worker.go
+++ b/worker.go
@@ -117,7 +117,7 @@ func (wc *workerContext) Children() []string {
func newWorkerContext(ctx context.Context, name string, attempt int, sup *suture.Supervisor, metrics Metrics, active *atomic.Int32) WorkerContext {
if metrics == nil {
- metrics = NoopMetrics
+ metrics = BaseMetrics{}
}
return &workerContext{Context: ctx, name: name, attempt: attempt, sup: sup, metrics: metrics, active: active}
}