Class: Wurk::Metrics::Statsd

Inherits:
Object
  • Object
show all
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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.optionsObject

Returns the value of attribute options.



39
40
41
# File 'lib/wurk/metrics/statsd.rb', line 39

def options
  @options
end

Instance Attribute Details

#configObject Originally defined in module Wurk::Middleware::ServerMiddleware

Returns the value of attribute config.

Class Method Details

.clientObject

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.



94
95
96
97
98
99
100
101
# File 'lib/wurk/metrics/statsd.rb', line 94

def client
  return @client if defined?(@client) && !@client.nil?

  builder = Wurk.configuration.respond_to?(:dogstatsd) ? Wurk.configuration.dogstatsd : nil
  return nil if builder.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.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/wurk/metrics/statsd.rb', line 73

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] = tags if tags
  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



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/wurk/metrics/statsd.rb', line 57

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] = tags if tags
  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.



44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/wurk/metrics/statsd.rb', line 44

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] = tags if tags
  client.increment("#{METRIC_PREFIX}#{metric}", **opts)
  nil
rescue StandardError => e
  handle_error(e)
  nil
end

.reset!Object

Test/lifecycle hook. Reset between specs and after fork so the parent's socket doesn't bleed into children.



105
106
107
# File 'lib/wurk/metrics/statsd.rb', line 105

def reset!
  @client = nil
end

Instance Method Details

#call(_worker, job, queue) ⇒ Object

rubocop:disable Metrics/AbcSize



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/wurk/metrics/statsd.rb', line 120

def call(_worker, job, queue) # rubocop:disable Metrics/AbcSize
  client = safe_client
  return yield if client.nil?

  klass = job['class']
  opts  = per_job_options(klass, job, queue)
  tags  = opts[:tags]
  rate  = opts.fetch(:sample_rate, DEFAULT_SAMPLE_RATE)

  emit(:increment, 'jobs.count', tags: 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: tags, sample_rate: rate)
    rescue StandardError => e
      self.class.send(:handle_error, e)
    end
  end
end

#loggerObject Originally defined in module Wurk::Middleware::ServerMiddleware

#redisObject Originally defined in module Wurk::Middleware::ServerMiddleware

#redis_poolObject Originally defined in module Wurk::Middleware::ServerMiddleware