Class: Wurk::Metrics::Statsd
- Inherits:
-
Object
- Object
- Wurk::Metrics::Statsd
- Includes:
- Wurk::Middleware::ServerMiddleware
- Defined in:
- lib/wurk/metrics/statsd.rb
Overview
Pro parity (§9): emits per-job timing + counters to a statsd / dogstatsd client. The client itself is plumbed in by the host app via:
Wurk.configure_server do |config|
config.dogstatsd = -> { Datadog::Statsd.new('metrics.example.com', 8125) }
config.server_middleware { |chain| chain.add Wurk::Metrics::Statsd }
end
The dogstatsd accessor is a callable — invoked once per process,
memoized — so the client is built lazily AFTER fork. Sharing a UDP
socket across forks is fine, but Datadog::Statsd keeps thread-locals
that must be initialized inside the child.
Per-job tuning via Statsd.options = ->(klass, job, queue) { {tags:, sample_rate:} }.
Default options: tags ["worker:<klass>", "queue:<q>"], sample_rate 1.0.
The dd_rate job option, when present, overrides sample_rate.
Metric naming follows Sidekiq Pro 8+: every metric prefixed sidekiq.
(the prefix is hardcoded, not configurable — third-party dashboards
built for Sidekiq Pro work unchanged).
Statsd.increment(metric, tags:) is the class-level fast path used by
other Wurk components (Buffered client, Expiry middleware, super_fetch
recovery, Batch lifecycle). No-op when no client is configured so
callers never have to guard.
Spec: docs/target/sidekiq-pro.md §9.
Constant Summary collapse
- METRIC_PREFIX =
'sidekiq.'- DEFAULT_SAMPLE_RATE =
1.0
Class Attribute Summary collapse
-
.options ⇒ Object
Returns the value of attribute options.
Instance Attribute Summary collapse
-
#config ⇒ Object
included
from Wurk::Middleware::ServerMiddleware
Returns the value of attribute config.
Class Method Summary collapse
-
.client ⇒ Object
Resolves the live client: invokes the configured
dogstatsdproc exactly once per process and memoizes. -
.distribution(metric, value, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) ⇒ Object
Distribution send.
- .gauge(metric, value, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) ⇒ Object
-
.increment(metric, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) ⇒ Object
Counter shortcut used across the codebase.
-
.reset! ⇒ Object
Test/lifecycle hook.
-
.safe_client ⇒ Object
Statsd.client without the raise: a builder proc that blows up is reported through the error handler and treated as "no client", so misconfigured metrics can never fail the work they were measuring.
Instance Method Summary collapse
-
#call(_worker, job, queue) ⇒ Object
rubocop:disable Metrics/AbcSize.
- #logger ⇒ Object included from Wurk::Middleware::ServerMiddleware
- #redis(idempotent: false) ⇒ Object included from Wurk::Middleware::ServerMiddleware
- #redis_pool ⇒ Object included from Wurk::Middleware::ServerMiddleware
Class Attribute Details
.options ⇒ Object
Returns the value of attribute options.
48 49 50 |
# File 'lib/wurk/metrics/statsd.rb', line 48 def @options end |
Instance Attribute Details
#config ⇒ Object Originally defined in module Wurk::Middleware::ServerMiddleware
Returns the value of attribute config.
Class Method Details
.client ⇒ Object
Resolves the live client: invokes the configured dogstatsd proc
exactly once per process and memoizes. Returns nil when no proc
is configured, so callers get a clean no-op without raising.
The unconfigured answer is memoized too, which is what makes the
no-client path free: Client#emit_enqueued asks once per payload, so
re-reading Wurk.configuration here would cost a config lookup per
job on every bulk push. Both invalidation points call reset!.
108 109 110 111 112 113 114 |
# File 'lib/wurk/metrics/statsd.rb', line 108 def client memo = @client return memo unless UNSET.equal?(memo) builder = Wurk.configuration.respond_to?(:dogstatsd) ? Wurk.configuration.dogstatsd : nil @client = builder.respond_to?(:call) ? builder.call : builder end |
.distribution(metric, value, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) ⇒ Object
Distribution send. Some statsd clients lack distribution (vanilla
statsd-ruby, for example) — fall back to histogram so the metric
still lands somewhere. dogstatsd-ruby always has distribution.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/wurk/metrics/statsd.rb', line 82 def distribution(metric, value, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) client = self.client return nil unless client opts = sample_rate_kw(sample_rate) opts[:tags] = if name = "#{METRIC_PREFIX}#{metric}" if client.respond_to?(:distribution) client.distribution(name, value, **opts) elsif client.respond_to?(:histogram) client.histogram(name, value, **opts) end nil rescue StandardError => e handle_error(e) nil end |
.gauge(metric, value, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/wurk/metrics/statsd.rb', line 66 def gauge(metric, value, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) client = self.client return nil unless client opts = sample_rate_kw(sample_rate) opts[:tags] = if client.gauge("#{METRIC_PREFIX}#{metric}", value, **opts) nil rescue StandardError => e handle_error(e) nil end |
.increment(metric, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) ⇒ Object
Counter shortcut used across the codebase. Tags are forwarded as
given — caller's job to namespace them ("class:Foo", "queue:bar").
No-op when no client is wired up.
53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/wurk/metrics/statsd.rb', line 53 def increment(metric, tags: nil, sample_rate: DEFAULT_SAMPLE_RATE) client = self.client return nil unless client opts = sample_rate_kw(sample_rate) opts[:tags] = if client.increment("#{METRIC_PREFIX}#{metric}", **opts) nil rescue StandardError => e handle_error(e) nil end |
.reset! ⇒ Object
Test/lifecycle hook. Reset between specs, after fork so the parent's
socket doesn't bleed into children, and on every config.dogstatsd=
— a memoized nil would otherwise hide a client configured later.
130 131 132 |
# File 'lib/wurk/metrics/statsd.rb', line 130 def reset! @client = UNSET end |
.safe_client ⇒ Object
client without the raise: a builder proc that blows up is reported through the error handler and treated as "no client", so misconfigured metrics can never fail the work they were measuring. Callers on a hot path use this to skip building anything the emit would drop.
120 121 122 123 124 125 |
# File 'lib/wurk/metrics/statsd.rb', line 120 def safe_client client rescue StandardError => e handle_error(e) nil end |
Instance Method Details
#call(_worker, job, queue) ⇒ Object
rubocop:disable Metrics/AbcSize
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/wurk/metrics/statsd.rb', line 145 def call(_worker, job, queue) # rubocop:disable Metrics/AbcSize return yield if self.class.safe_client.nil? klass = job['class'] opts = (klass, job, queue) = opts[:tags] rate = opts.fetch(:sample_rate, DEFAULT_SAMPLE_RATE) emit(:increment, 'jobs.count', tags: , sample_rate: rate) started = monotonic_ms success = false begin yield success = true ensure duration = monotonic_ms - started # Metrics are best-effort: an emit failure mid-finalize must not # corrupt the job result the caller already produced. begin finalize(success, duration, tags: , sample_rate: rate) rescue StandardError => e self.class.send(:handle_error, e) end end end |