sidekiq-datadog 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5ddc4b1590a916dadbc474764d087a15852d9be178052fdd307a74edd89795e
4
- data.tar.gz: 18a0882e9702f0fc156040909a9d1b9eca8a2322228b090e9edd5839f4227641
3
+ metadata.gz: ee5e44db186a926cdca3e3eae1a8c89c3665c67e067aa079b9a24bf8ae70fbd1
4
+ data.tar.gz: f0044e25b254c0e663e13a8f31e71f6f60a3d9048521e3592906377f787eba92
5
5
  SHA512:
6
- metadata.gz: 8dfd4d325eb51a8d555b2f881e531085c4b2533d78c5314400c7a82e9e3e0939a77e1e5cabf7fb972186bdd535be4cd89c658265e5f7c60e16df8fe7d3c00dfb
7
- data.tar.gz: 2a97a0ee5e274ca98a1eff4b06adbbe7be548ad7d2ecb1f63c35152a8afe9c605e742872008edcd5e61f0345bc3bcbe4cf13c1fca429448b096dbad0d75ddde8
6
+ metadata.gz: 35f1c25f0c901dc4bbf3fcd15c29c98d0bb6b2be7a39dc33ab61802bd3ee30fe4cb4c460a2858b76db75ce708b8f77130bb97df6fe82c1057d165a4de246726d
7
+ data.tar.gz: 7415541240b7aed33ae3a22af5661f58e48275733f048f041a69e538aeba5567f2d0066681d0c205ff069d360da5e3c1a8ba4ac7fa946f6a3229311f2cc289d3
@@ -0,0 +1,24 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ ruby:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby-version: ["2.5", "2.6", "2.7", "3.0"]
15
+ gemfile: [ Gemfile, Gemfile_dogstats_v4 ]
16
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
17
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
18
+ steps:
19
+ - uses: actions/checkout@v2
20
+ - uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby-version }}
23
+ bundler-cache: true
24
+ - run: bundle exec rake
data/.rubocop.yml CHANGED
@@ -1,9 +1,17 @@
1
- require: rubocop-performance
2
- inherit_from:
3
- - https://gitlab.com/bsm/misc/raw/master/rubocop/default.yml
1
+ inherit_gem:
2
+ rubocop-bsm:
3
+ - default.yml
4
+ inherit_mode:
5
+ merge:
6
+ - Exclude
7
+
4
8
  AllCops:
5
- TargetRubyVersion: "2.3"
9
+ TargetRubyVersion: "2.5"
10
+
11
+ Metrics/ParameterLists:
12
+ Max: 8
6
13
  Naming/FileName:
7
14
  Exclude:
8
- - "lib/sidekiq-datadog.rb"
9
- - "sidekiq-datadog.gemspec"
15
+ - lib/sidekiq-datadog.rb
16
+ RSpec/MultipleMemoizedHelpers:
17
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -1,57 +1,70 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sidekiq-datadog (0.5.3)
4
+ sidekiq-datadog (0.6.0)
5
5
  dogstatsd-ruby (>= 4.2.0)
6
6
  sidekiq
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- ast (2.4.0)
12
- connection_pool (2.2.2)
13
- diff-lcs (1.3)
14
- dogstatsd-ruby (4.5.0)
15
- jaro_winkler (1.5.4)
16
- parallel (1.19.1)
17
- parser (2.6.5.0)
18
- ast (~> 2.4.0)
19
- rack (2.0.8)
20
- rack-protection (2.0.7)
21
- rack
11
+ ast (2.4.2)
12
+ connection_pool (2.2.5)
13
+ diff-lcs (1.4.4)
14
+ dogstatsd-ruby (5.1.0)
15
+ parallel (1.20.1)
16
+ parser (3.0.1.1)
17
+ ast (~> 2.4.1)
18
+ rack (2.2.3)
22
19
  rainbow (3.0.0)
23
- rake (13.0.1)
24
- redis (4.1.3)
25
- rspec (3.9.0)
26
- rspec-core (~> 3.9.0)
27
- rspec-expectations (~> 3.9.0)
28
- rspec-mocks (~> 3.9.0)
29
- rspec-core (3.9.0)
30
- rspec-support (~> 3.9.0)
31
- rspec-expectations (3.9.0)
20
+ rake (13.0.3)
21
+ redis (4.3.1)
22
+ regexp_parser (2.1.1)
23
+ rexml (3.2.5)
24
+ rspec (3.10.0)
25
+ rspec-core (~> 3.10.0)
26
+ rspec-expectations (~> 3.10.0)
27
+ rspec-mocks (~> 3.10.0)
28
+ rspec-core (3.10.1)
29
+ rspec-support (~> 3.10.0)
30
+ rspec-expectations (3.10.1)
32
31
  diff-lcs (>= 1.2.0, < 2.0)
33
- rspec-support (~> 3.9.0)
34
- rspec-mocks (3.9.0)
32
+ rspec-support (~> 3.10.0)
33
+ rspec-mocks (3.10.2)
35
34
  diff-lcs (>= 1.2.0, < 2.0)
36
- rspec-support (~> 3.9.0)
37
- rspec-support (3.9.0)
38
- rubocop (0.78.0)
39
- jaro_winkler (~> 1.5.1)
35
+ rspec-support (~> 3.10.0)
36
+ rspec-support (3.10.2)
37
+ rubocop (1.17.0)
40
38
  parallel (~> 1.10)
41
- parser (>= 2.6)
39
+ parser (>= 3.0.0.0)
42
40
  rainbow (>= 2.2.2, < 4.0)
41
+ regexp_parser (>= 1.8, < 3.0)
42
+ rexml
43
+ rubocop-ast (>= 1.7.0, < 2.0)
43
44
  ruby-progressbar (~> 1.7)
44
- unicode-display_width (>= 1.4.0, < 1.7)
45
- rubocop-performance (1.5.1)
46
- rubocop (>= 0.71.0)
47
- ruby-progressbar (1.10.1)
48
- sidekiq (6.0.3)
45
+ unicode-display_width (>= 1.4.0, < 3.0)
46
+ rubocop-ast (1.7.0)
47
+ parser (>= 3.0.1.1)
48
+ rubocop-bsm (0.6.0)
49
+ rubocop (~> 1.0)
50
+ rubocop-performance
51
+ rubocop-rake
52
+ rubocop-rspec
53
+ rubocop-performance (1.11.3)
54
+ rubocop (>= 1.7.0, < 2.0)
55
+ rubocop-ast (>= 0.4.0)
56
+ rubocop-rake (0.5.1)
57
+ rubocop
58
+ rubocop-rspec (2.4.0)
59
+ rubocop (~> 1.0)
60
+ rubocop-ast (>= 1.1.0)
61
+ ruby-progressbar (1.11.0)
62
+ sidekiq (6.2.1)
49
63
  connection_pool (>= 2.2.2)
50
- rack (>= 2.0.0)
51
- rack-protection (>= 2.0.0)
52
- redis (>= 4.1.0)
53
- timecop (0.9.1)
54
- unicode-display_width (1.6.0)
64
+ rack (~> 2.0)
65
+ redis (>= 4.2.0)
66
+ timecop (0.9.4)
67
+ unicode-display_width (2.0.0)
55
68
 
56
69
  PLATFORMS
57
70
  ruby
@@ -60,10 +73,10 @@ DEPENDENCIES
60
73
  bundler
61
74
  rake
62
75
  rspec
63
- rubocop
76
+ rubocop-bsm
64
77
  rubocop-performance
65
78
  sidekiq-datadog!
66
79
  timecop
67
80
 
68
81
  BUNDLED WITH
69
- 2.0.2
82
+ 2.2.19
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'dogstatsd-ruby', '~> 4.8' # A statsd wrapper for reporting DataDog custom metrics
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sidekiq-datadog (0.6.0)
5
+ dogstatsd-ruby (>= 4.2.0)
6
+ sidekiq
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.2)
12
+ connection_pool (2.2.5)
13
+ diff-lcs (1.4.4)
14
+ dogstatsd-ruby (4.8.3)
15
+ parallel (1.20.1)
16
+ parser (3.0.1.1)
17
+ ast (~> 2.4.1)
18
+ rack (2.2.3)
19
+ rainbow (3.0.0)
20
+ rake (13.0.3)
21
+ redis (4.3.1)
22
+ regexp_parser (2.1.1)
23
+ rexml (3.2.5)
24
+ rspec (3.10.0)
25
+ rspec-core (~> 3.10.0)
26
+ rspec-expectations (~> 3.10.0)
27
+ rspec-mocks (~> 3.10.0)
28
+ rspec-core (3.10.1)
29
+ rspec-support (~> 3.10.0)
30
+ rspec-expectations (3.10.1)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.10.0)
33
+ rspec-mocks (3.10.2)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.10.0)
36
+ rspec-support (3.10.2)
37
+ rubocop (1.17.0)
38
+ parallel (~> 1.10)
39
+ parser (>= 3.0.0.0)
40
+ rainbow (>= 2.2.2, < 4.0)
41
+ regexp_parser (>= 1.8, < 3.0)
42
+ rexml
43
+ rubocop-ast (>= 1.7.0, < 2.0)
44
+ ruby-progressbar (~> 1.7)
45
+ unicode-display_width (>= 1.4.0, < 3.0)
46
+ rubocop-ast (1.7.0)
47
+ parser (>= 3.0.1.1)
48
+ rubocop-bsm (0.6.0)
49
+ rubocop (~> 1.0)
50
+ rubocop-performance
51
+ rubocop-rake
52
+ rubocop-rspec
53
+ rubocop-performance (1.11.3)
54
+ rubocop (>= 1.7.0, < 2.0)
55
+ rubocop-ast (>= 0.4.0)
56
+ rubocop-rake (0.5.1)
57
+ rubocop
58
+ rubocop-rspec (2.4.0)
59
+ rubocop (~> 1.0)
60
+ rubocop-ast (>= 1.1.0)
61
+ ruby-progressbar (1.11.0)
62
+ sidekiq (6.2.1)
63
+ connection_pool (>= 2.2.2)
64
+ rack (~> 2.0)
65
+ redis (>= 4.2.0)
66
+ timecop (0.9.4)
67
+ unicode-display_width (2.0.0)
68
+
69
+ PLATFORMS
70
+ ruby
71
+
72
+ DEPENDENCIES
73
+ bundler
74
+ dogstatsd-ruby (~> 4.8)
75
+ rake
76
+ rspec
77
+ rubocop-bsm
78
+ rubocop-performance
79
+ sidekiq-datadog!
80
+ timecop
81
+
82
+ BUNDLED WITH
83
+ 2.2.19
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  sidekiq-datadog
2
2
  =============
3
3
 
4
- Datadog intrumentation for [Sidekiq](https://github.com/mperham/sidekiq), integrated via server middleware.
4
+ Datadog instrumentation for [Sidekiq](https://github.com/mperham/sidekiq), integrated via server middleware.
5
5
 
6
6
  ## Installation
7
7
 
@@ -15,7 +15,17 @@ Or install:
15
15
 
16
16
  Configure it in an initializer:
17
17
 
18
+ Sidekiq.configure_client do |config|
19
+ config.client_middleware do |chain|
20
+ chain.add Sidekiq::Middleware::Client::Datadog
21
+ end
22
+ end
23
+
18
24
  Sidekiq.configure_server do |config|
25
+ config.client_middleware do |chain|
26
+ chain.add Sidekiq::Middleware::Client::Datadog
27
+ end
28
+
19
29
  config.server_middleware do |chain|
20
30
  chain.add Sidekiq::Middleware::Server::Datadog
21
31
  end
@@ -45,13 +55,36 @@ executed at runtime when the job is processed
45
55
 
46
56
  Sidekiq.configure_server do |config|
47
57
  config.server_middleware do |chain|
48
- chain.add(Sidekiq::Middleware::Server::Datadog, tags: [->(worker, job, queue, error){
58
+ chain.add(Sidekiq::Middleware::Server::Datadog, tags: [->(worker_or_worker_class, job, queue, error){
49
59
  "source:#{job['source']}"
50
60
  }])
51
61
  end
52
62
  end
53
63
 
64
+ # NOTE: Your lambda will either receive a `Worker` object for the Server middleware,
65
+ # or a String with the `worker_class` for the Client middleware.
66
+ # If you are using that argument, your lambda should be able to handle both cases.
67
+
68
+ You can supress some of the default tags from being emitted by passing in `skip_tags`.
69
+ This is also useful if you would like to change one of the default tags, you can define
70
+ a custom lambda **and** define it as `skip_tags`
71
+
72
+
73
+ Sidekiq.configure_server do |config|
74
+ config.server_middleware do |chain|
75
+ chain.add(Sidekiq::Middleware::Server::Datadog,
76
+ skip_tags: ["name"],
77
+ tags: [->(worker_or_worker_class, job, queue, error){
78
+ "name:#{ my_logic_for_name }"
79
+ }])
80
+ end
81
+ end
82
+
83
+
54
84
  #### supported options
85
+
86
+ Both Client and Server middlewares support the same options:
87
+
55
88
  - *hostname* - the hostname used for instrumentation, defaults to system hostname. Can also be set with the `INSTRUMENTATION_HOSTNAME` env var.
56
89
  - *metric_name* - the metric name (prefix) to use, defaults to "sidekiq.job".
57
90
  - *tags* - array of custom tags. These can be plain strings or lambda blocks.
@@ -62,6 +95,22 @@ executed at runtime when the job is processed
62
95
 
63
96
  For more detailed configuration options, please see the [Documentation](http://www.rubydoc.info/gems/sidekiq-datadog).
64
97
 
98
+ ## Metrics exposed
99
+
100
+ The client middleware will expose:
101
+ - `sidekiq.job_enqueued` counter, with tags: `host`, `env`, `name` (the job name) and `queue`
102
+
103
+ The server middleware will expose:
104
+ - `sidekiq.job` counter, with tags: `host`, `env`, `name` (the job name), `queue`,
105
+ and `status` (`ok` or `error`). If `status` is `error`, there will be an additional
106
+ `error` tag with the exception class.
107
+ - `sidekiq.job.time` timing (`ms`) metric with the same tags, specifying the job runtime.
108
+ - `sidekiq.job.queued_time` timing (`ms`) metric with the same tags, specifying how long
109
+ the job was waiting in the queue before starting.
110
+
111
+ The base metric names `sidekiq.job` and `sidekiq.job_enqueued` can be overriden using the
112
+ `metric_name` option.
113
+
65
114
  ## Contributing
66
115
 
67
116
  1. Fork it
@@ -70,3 +119,18 @@ For more detailed configuration options, please see the [Documentation](http://w
70
119
  4. Push to the branch (`git push origin my-new-feature`)
71
120
  5. Make a pull request
72
121
 
122
+
123
+ **`dogstatsd-ruby` Version note:** `dogstatsd-ruby` has major backwards incompatibilities
124
+ between v4.8.3 and 5.0.0. This gem is compatible with both, and CI tests both versions.
125
+
126
+ In order to test through both, we have 2 Gemfiles (and their respective `.lock` files):
127
+ - `Gemfile`
128
+ - `Gemfile_dogstats_v4`
129
+
130
+ By default, everything runs against `dogstatsd-ruby` >= 5.0.
131
+
132
+ To update `Gemfile_dogstats_v4.lock`, or run tests with c4.8:
133
+
134
+ `BUNDLE_GEMFILE=Gemfile_dogstats_v4 bundle update`
135
+
136
+ `BUNDLE_GEMFILE=Gemfile_dogstats_v4 bundle exec rspec`
data/Rakefile CHANGED
@@ -20,4 +20,4 @@ end
20
20
  require 'rubocop/rake_task'
21
21
  RuboCop::RakeTask.new(:rubocop)
22
22
 
23
- task default: %i[spec rubocop]
23
+ task default: %i[rubocop spec]
@@ -1 +1 @@
1
- require 'sidekiq/middleware/server/datadog'
1
+ require 'sidekiq/datadog'
@@ -0,0 +1,3 @@
1
+ require 'sidekiq/datadog/version'
2
+ require 'sidekiq/middleware/client/datadog'
3
+ require 'sidekiq/middleware/server/datadog'
@@ -0,0 +1,75 @@
1
+ module Sidekiq
2
+ module Datadog
3
+ class TagBuilder
4
+ def initialize(custom_tags = [], skip_tags = [], custom_hostname = nil)
5
+ @tags = Array(custom_tags)
6
+ @skip_tags = Array(skip_tags).map(&:to_s)
7
+
8
+ env = Sidekiq.options[:environment] || ENV['RACK_ENV']
9
+ host = custom_hostname || ENV['INSTRUMENTATION_HOSTNAME'] || Socket.gethostname
10
+ setup_defaults(host: host, env: env)
11
+ end
12
+
13
+ def build_tags(worker_or_worker_class, job, queue = nil, error = nil)
14
+ tags = @tags.flat_map do |tag|
15
+ case tag
16
+ when String then tag
17
+ when Proc then tag.call(worker_or_worker_class, job, queue, error)
18
+ end
19
+ end
20
+ tags.compact!
21
+
22
+ tags.push "name:#{job_name(worker_or_worker_class, job)}" if include_tag?('name')
23
+ tags.push "queue:#{queue}" if queue && include_tag?('queue')
24
+
25
+ if error.nil?
26
+ tags.push 'status:ok' if include_tag?('status')
27
+ else
28
+ kind = underscore(error.class.name.sub(/Error$/, ''))
29
+ tags.push 'status:error' if include_tag?('status')
30
+ tags.push "error:#{kind}" if include_tag?('error')
31
+ end
32
+
33
+ tags
34
+ end
35
+
36
+ private
37
+
38
+ def job_name(worker_or_worker_class, job)
39
+ # Client middleware sends a `worker_class` String,
40
+ # whereas Server middleware send a Worker object
41
+ worker_class = if worker_or_worker_class.is_a?(String)
42
+ worker_or_worker_class
43
+ else
44
+ worker_or_worker_class.class.to_s
45
+ end
46
+
47
+ underscore(job['wrapped'] || worker_class)
48
+ end
49
+
50
+ def setup_defaults(hash)
51
+ hash.each do |key, val|
52
+ key = key.to_s
53
+ next unless val && include_tag?(key)
54
+
55
+ prefix = "#{key}:"
56
+ next if @tags.any? {|t| t.is_a?(String) && t.start_with?(prefix) }
57
+
58
+ @tags.push [key, val].join(':')
59
+ end
60
+ end
61
+
62
+ def include_tag?(tag)
63
+ !@skip_tags.include?(tag)
64
+ end
65
+
66
+ def underscore(word)
67
+ word = word.to_s.gsub(/::/, '/')
68
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
69
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
70
+ word.tr!('-', '_')
71
+ word.downcase
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,5 +1,5 @@
1
1
  module Sidekiq
2
2
  module Datadog
3
- VERSION = '0.5.3'.freeze
3
+ VERSION = '0.6.0'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,67 @@
1
+ require 'sidekiq'
2
+ require 'sidekiq/datadog/version'
3
+ require 'sidekiq/datadog/tag_builder'
4
+ require 'datadog/statsd'
5
+ require 'socket'
6
+
7
+ module Sidekiq
8
+ module Middleware
9
+ module Client
10
+ class Datadog
11
+ # Configure and install datadog instrumentation. Example:
12
+ #
13
+ # Sidekiq.configure_client do |config|
14
+ # config.client_middleware do |chain|
15
+ # chain.add Sidekiq::Middleware::Client::Datadog
16
+ # end
17
+ # end
18
+ #
19
+ # You might want to also call `client_middleware` in your `configure_server` call,
20
+ # since enqueued jobs can enqueue other jobs.
21
+ #
22
+ # If you have other client middleware that can stop jobs from getting pushed,
23
+ # you might want to ensure this middleware is added last, to avoid reporting
24
+ # enqueues that later get stopped.
25
+ #
26
+ # Options:
27
+ # * <tt>:hostname</tt> - the hostname used for instrumentation, defaults to system hostname, respects +INSTRUMENTATION_HOSTNAME+ env variable
28
+ # * <tt>:metric_name</tt> - the metric name (prefix) to use, defaults to "sidekiq.job_enqueued"
29
+ # * <tt>:tags</tt> - array of custom tags, these can be plain strings or lambda blocks accepting a rack env instance
30
+ # * <tt>:skip_tags</tt> - array of tag names that shouldn't be emitted
31
+ # * <tt>:statsd_host</tt> - the statsD host, defaults to "localhost", respects +STATSD_HOST+ env variable
32
+ # * <tt>:statsd_port</tt> - the statsD port, defaults to 8125, respects +STATSD_PORT+ env variable
33
+ # * <tt>:statsd</tt> - custom statsd instance
34
+ def initialize(opts = {})
35
+ statsd_host = opts[:statsd_host] || ENV['STATSD_HOST'] || 'localhost'
36
+ statsd_port = (opts[:statsd_port] || ENV['STATSD_PORT'] || 8125).to_i
37
+
38
+ @metric_name = opts[:metric_name] || 'sidekiq.job_enqueued'
39
+ @statsd = opts[:statsd] || ::Datadog::Statsd.new(statsd_host, statsd_port)
40
+
41
+ # `status` is meaningless when enqueueing
42
+ skip_tags = Array(opts[:skip_tags]) + ['status']
43
+
44
+ @tag_builder = Sidekiq::Datadog::TagBuilder.new(
45
+ opts[:tags],
46
+ skip_tags,
47
+ opts[:hostname],
48
+ )
49
+ end
50
+
51
+ def call(worker_class, job, queue, _redis_pool, *)
52
+ record(worker_class, job, queue)
53
+ yield
54
+ end
55
+
56
+ private
57
+
58
+ def record(worker_class, job, queue)
59
+ tags = @tag_builder.build_tags(worker_class, job, queue)
60
+ @statsd.increment @metric_name, tags: tags
61
+
62
+ @statsd.flush if @statsd.respond_to?(:flush) # dogstatsd-ruby >= 5.0.0
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,5 +1,6 @@
1
1
  require 'sidekiq'
2
2
  require 'sidekiq/datadog/version'
3
+ require 'sidekiq/datadog/tag_builder'
3
4
  require 'datadog/statsd'
4
5
  require 'socket'
5
6
 
@@ -7,7 +8,6 @@ module Sidekiq
7
8
  module Middleware
8
9
  module Server
9
10
  class Datadog
10
-
11
11
  # Configure and install datadog instrumentation. Example:
12
12
  #
13
13
  # Sidekiq.configure_server do |config|
@@ -24,20 +24,17 @@ module Sidekiq
24
24
  # * <tt>:statsd_host</tt> - the statsD host, defaults to "localhost", respects +STATSD_HOST+ env variable
25
25
  # * <tt>:statsd_port</tt> - the statsD port, defaults to 8125, respects +STATSD_PORT+ env variable
26
26
  # * <tt>:statsd</tt> - custom statsd instance
27
- def initialize(opts={})
28
- hostname = opts[:hostname] || ENV['INSTRUMENTATION_HOSTNAME'] || Socket.gethostname
29
- statsd_host = opts[:statsd_host] || ENV['STATSD_HOST'] || 'localhost'
30
- statsd_port = (opts[:statsd_port] || ENV['STATSD_PORT'] || 8125).to_i
27
+ def initialize(opts = {})
28
+ statsd_host = opts[:statsd_host] || ENV['STATSD_HOST'] || 'localhost'
29
+ statsd_port = (opts[:statsd_port] || ENV['STATSD_PORT'] || 8125).to_i
31
30
 
32
31
  @metric_name = opts[:metric_name] || 'sidekiq.job'
33
32
  @statsd = opts[:statsd] || ::Datadog::Statsd.new(statsd_host, statsd_port)
34
- @tags = opts[:tags] || []
35
- @skip_tags = Array(opts[:skip_tags]).map(&:to_s)
36
-
37
- @tags.push("host:#{hostname}") if include_tag?('host') && @tags.none? {|t| t =~ /^host\:/ }
38
-
39
- env = Sidekiq.options[:environment] || ENV['RACK_ENV']
40
- @tags.push("env:#{ENV['RACK_ENV']}") if env && include_tag?('env') && @tags.none? {|t| t =~ /^env\:/ }
33
+ @tag_builder = Sidekiq::Datadog::TagBuilder.new(
34
+ opts[:tags],
35
+ opts[:skip_tags],
36
+ opts[:hostname],
37
+ )
41
38
  end
42
39
 
43
40
  def call(worker, job, queue, *)
@@ -55,53 +52,19 @@ module Sidekiq
55
52
 
56
53
  private
57
54
 
58
- def record(worker, job, queue, start, clock, error=nil) # rubocop:disable Metrics/ParameterLists
55
+ def record(worker, job, queue, start, clock, error = nil)
59
56
  msec = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) - clock
60
- tags = build_tags(worker, job, queue, error)
57
+ tags = @tag_builder.build_tags(worker, job, queue, error)
61
58
 
62
59
  @statsd.increment @metric_name, tags: tags
63
60
  @statsd.timing "#{@metric_name}.time", msec, tags: tags
64
61
 
65
- if job['enqueued_at'] # rubocop:disable Style/GuardClause
66
- queued_ms = ((start - Time.at(job['enqueued_at'])) * 1000).round
67
- @statsd.timing "#{@metric_name}.queued_time", queued_ms, tags: tags
68
- end
69
- end
70
-
71
- def build_tags(worker, job, queue=nil, error=nil)
72
- name = underscore(job['wrapped'] || worker.class.to_s)
73
- tags = @tags.flat_map do |tag|
74
- case tag
75
- when String then tag
76
- when Proc then tag.call(worker, job, queue, error)
77
- end
78
- end
79
- tags.compact!
62
+ return unless job['enqueued_at']
80
63
 
81
- tags.push "name:#{name}" if include_tag?('name')
82
- tags.push "queue:#{queue}" if queue && include_tag?('queue')
83
-
84
- if error.nil?
85
- tags.push 'status:ok' if include_tag?('status')
86
- else
87
- kind = underscore(error.class.name.sub(/Error$/, ''))
88
- tags.push 'status:error' if include_tag?('status')
89
- tags.push "error:#{kind}" if include_tag?('error')
90
- end
91
-
92
- tags
93
- end
94
-
95
- def include_tag?(tag)
96
- !@skip_tags.include?(tag)
97
- end
64
+ queued_ms = ((start - Time.at(job['enqueued_at'])) * 1000).round
65
+ @statsd.timing "#{@metric_name}.queued_time", queued_ms, tags: tags
98
66
 
99
- def underscore(word)
100
- word = word.to_s.gsub(/::/, '/')
101
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
102
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
103
- word.tr!('-', '_')
104
- word.downcase
67
+ @statsd.flush if @statsd.respond_to?(:flush) # dogstatsd-ruby >= 5.0.0
105
68
  end
106
69
  end
107
70
  end
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.executables = s.files.grep(%r{^bin/}).map {|f| File.basename(f) }
16
16
  s.test_files = s.files.grep(%r{^(spec)/})
17
17
  s.require_paths = ['lib']
18
- s.required_ruby_version = '>= 2.3'
18
+ s.required_ruby_version = '>= 2.5'
19
19
 
20
20
  s.add_runtime_dependency('dogstatsd-ruby', '>= 4.2.0')
21
21
  s.add_runtime_dependency('sidekiq')
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency('bundler')
24
24
  s.add_development_dependency('rake')
25
25
  s.add_development_dependency('rspec')
26
- s.add_development_dependency('rubocop')
26
+ s.add_development_dependency('rubocop-bsm')
27
27
  s.add_development_dependency('rubocop-performance')
28
28
  s.add_development_dependency('timecop')
29
29
  end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sidekiq::Datadog::TagBuilder do
4
+ subject { described_class.new(custom_tags, skip_tags, custom_hostname) }
5
+
6
+ let(:custom_tags) { nil }
7
+ let(:worker) { Mock::Worker.new }
8
+ let(:job) { { 'enqueued_at' => 1461881794 } }
9
+ let(:queue) { 'queue_name' }
10
+ let(:error) { nil }
11
+ let(:skip_tags) { nil }
12
+ let(:custom_hostname) { nil }
13
+
14
+ it 'builds basic default tags without any parameters' do
15
+ result = subject.build_tags(worker, job, queue, error)
16
+
17
+ expect(result).to eql([
18
+ "host:#{Socket.gethostname}",
19
+ 'env:test',
20
+ 'name:mock/worker',
21
+ 'queue:queue_name',
22
+ 'status:ok',
23
+ ])
24
+ end
25
+
26
+ context 'with custom hostname' do
27
+ let(:custom_hostname) { 'myhostname' }
28
+
29
+ it 'reports the custom hostname' do
30
+ result = subject.build_tags(worker, job, queue, error)
31
+ expect(result).to include('host:myhostname')
32
+ end
33
+ end
34
+
35
+ context 'with custom tags' do
36
+ let(:custom_tags) { ['custom:tag', ->(w, *) { "worker:#{w.class.name[1..2]}" }] }
37
+
38
+ it 'reports the custom tags, fixed and Procs' do
39
+ result = subject.build_tags(worker, job, queue, error)
40
+ expect(result).to include('custom:tag')
41
+ expect(result).to include('worker:oc')
42
+ end
43
+ end
44
+
45
+ context 'with skipped tags' do
46
+ let(:skip_tags) { %w[name env] }
47
+
48
+ it "doesn't output the skipped tags" do
49
+ result = subject.build_tags(worker, job, queue, error)
50
+ result_keys = result.map {|t| t.split(':').first }
51
+
52
+ expect(result_keys).not_to include('name')
53
+ expect(result_keys).not_to include('env')
54
+ end
55
+ end
56
+
57
+ context 'with a wrapped job' do
58
+ let(:job) { { 'wrapped' => 'Module::WrappedJobName' } }
59
+
60
+ it 'reports the wrapped job name' do
61
+ result = subject.build_tags(worker, job, queue, error)
62
+ expect(result).to include('name:module/wrapped_job_name')
63
+ end
64
+ end
65
+
66
+ context 'with an error' do
67
+ let(:error) { ArgumentError.new }
68
+
69
+ it 'reports the error class, and status' do
70
+ result = subject.build_tags(worker, job, queue, error)
71
+ expect(result).to include('error:argument')
72
+ expect(result).to include('status:error')
73
+ end
74
+ end
75
+
76
+ context 'with a worker_class instead of a Worker object' do
77
+ let(:worker_class) { 'Module::Worker' }
78
+
79
+ it 'reports the error class, and status' do
80
+ result = subject.build_tags(worker_class, job, queue, error)
81
+ expect(result).to include('name:module/worker')
82
+ end
83
+ end
84
+ end
@@ -2,6 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe Sidekiq::Datadog do
4
4
  it 'has a version' do
5
- expect(Sidekiq::Datadog::VERSION).to be_instance_of(String)
5
+ expect(described_class::VERSION).to be_instance_of(String)
6
6
  end
7
7
  end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sidekiq::Middleware::Client::Datadog do
4
+ subject { described_class.new(hostname: 'test.host', statsd: statsd, tags: tags, **options) }
5
+
6
+ let(:statsd) { Mock::Statsd.new('localhost', 55555) }
7
+ let(:worker_class) { 'Mock::Worker' }
8
+ let(:tags) do
9
+ ['custom:tag', ->(worker_class, *) { "worker:#{worker_class[1..2]}" }]
10
+ end
11
+ let(:options) { {} }
12
+
13
+ before do
14
+ statsd.messages.clear
15
+ end
16
+
17
+ it 'sends an increment event for each job enqueued' do
18
+ subject.call(worker_class, {}, 'default', nil) { 'ok' }
19
+ expect(statsd.messages).to eq([
20
+ 'sidekiq.job_enqueued:1|c|#custom:tag,worker:oc,host:test.host,env:test,name:mock/worker,'\
21
+ 'queue:default',
22
+ ])
23
+ end
24
+
25
+ it 'supports wrappers' do
26
+ subject.call(worker_class, { 'wrapped' => 'wrap' }, nil, nil) { 'ok' }
27
+ expect(statsd.messages).to eq([
28
+ 'sidekiq.job_enqueued:1|c|#custom:tag,worker:oc,host:test.host,env:test,name:wrap',
29
+ ])
30
+ end
31
+
32
+ context 'with a dynamic tag list' do
33
+ let(:tags) do
34
+ ['custom:tag', ->(_w, j, *) { j['args'].map {|n| "arg:#{n}" } }]
35
+ end
36
+
37
+ it 'generates the correct tags' do
38
+ subject.call(worker_class, { 'args' => [1, 2] }, 'default', nil) { 'ok' }
39
+
40
+ expect(statsd.messages).to eq([
41
+ 'sidekiq.job_enqueued:1|c|#custom:tag,arg:1,arg:2,host:test.host,env:test,name:mock/worker,'\
42
+ 'queue:default',
43
+ ])
44
+ end
45
+ end
46
+
47
+ context 'with a list of skipped tags' do
48
+ let(:tags) { [] }
49
+ let(:options) { { skip_tags: %i[env host name] } }
50
+
51
+ it 'sends metrics without the skipped tags' do
52
+ subject.call(worker_class, {}, 'default', nil) { 'ok' }
53
+
54
+ expect(statsd.messages).to eq([
55
+ 'sidekiq.job_enqueued:1|c|#queue:default',
56
+ ])
57
+ end
58
+ end
59
+
60
+ context 'when sidekiq/api is required' do
61
+ before do
62
+ require 'sidekiq/api'
63
+ end
64
+
65
+ it 'does not raise any errors' do
66
+ expect do
67
+ subject.call(worker_class, {}, 'default', nil) { 'ok' }
68
+ end.not_to raise_error
69
+ end
70
+ end
71
+ end
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Sidekiq::Middleware::Server::Datadog do
4
+ subject { described_class.new(hostname: 'test.host', statsd: statsd, tags: tags, **options) }
4
5
 
5
6
  let(:statsd) { Mock::Statsd.new('localhost', 55555) }
6
7
  let(:worker) { Mock::Worker.new }
@@ -23,9 +24,7 @@ describe Sidekiq::Middleware::Server::Datadog do
23
24
  Timecop.freeze(Time.at(enqueued_at + expected_queued_time_ms.to_f / 1000))
24
25
  end
25
26
 
26
- subject { described_class.new(hostname: 'test.host', statsd: statsd, tags: tags, **options) }
27
-
28
- it 'should send an increment and timing event for each job run' do
27
+ it 'sends an increment and timing event for each job run' do
29
28
  subject.call(worker, { 'enqueued_at' => enqueued_at }, 'default') { 'ok' }
30
29
  expect(statsd.messages).to eq([
31
30
  'sidekiq.job:1|c|#custom:tag,worker:oc,host:test.host,env:test,name:mock/worker,'\
@@ -37,7 +36,7 @@ describe Sidekiq::Middleware::Server::Datadog do
37
36
  ])
38
37
  end
39
38
 
40
- it 'should support wrappers' do
39
+ it 'supports wrappers' do
41
40
  subject.call(worker, { 'enqueued_at' => enqueued_at, 'wrapped' => 'wrap' }, nil) { 'ok' }
42
41
  expect(statsd.messages).to eq([
43
42
  'sidekiq.job:1|c|#custom:tag,worker:oc,host:test.host,env:test,name:wrap,status:ok',
@@ -47,7 +46,7 @@ describe Sidekiq::Middleware::Server::Datadog do
47
46
  ])
48
47
  end
49
48
 
50
- it 'should handle errors' do
49
+ it 'handles errors' do
51
50
  expect(lambda {
52
51
  subject.call(worker, {}, nil) { raise 'doh!' }
53
52
  }).to raise_error('doh!')
@@ -65,7 +64,7 @@ describe Sidekiq::Middleware::Server::Datadog do
65
64
  ['custom:tag', ->(_w, j, *) { j['args'].map {|n| "arg:#{n}" } }]
66
65
  end
67
66
 
68
- it 'should generate the correct tags' do
67
+ it 'generates the correct tags' do
69
68
  subject.call(worker, { 'enqueued_at' => enqueued_at, 'args' => [1, 2] }, 'default') { 'ok' }
70
69
 
71
70
  expect(statsd.messages).to eq([
@@ -83,7 +82,7 @@ describe Sidekiq::Middleware::Server::Datadog do
83
82
  let(:tags) { [] }
84
83
  let(:options) { { skip_tags: %i[env host name] } }
85
84
 
86
- it 'should send metrics without the skipped tags' do
85
+ it 'sends metrics without the skipped tags' do
87
86
  subject.call(worker, { 'enqueued_at' => 1461881794.9312189 }, 'default') { 'ok' }
88
87
 
89
88
  expect(statsd.messages).to eq([
@@ -99,7 +98,7 @@ describe Sidekiq::Middleware::Server::Datadog do
99
98
  require 'sidekiq/api'
100
99
  end
101
100
 
102
- it 'should not raise any errors' do
101
+ it 'does not raise any errors' do
103
102
  expect do
104
103
  subject.call(worker, { 'enqueued_at' => enqueued_at }, 'default') { 'ok' }
105
104
  end.not_to raise_error
data/spec/spec_helper.rb CHANGED
@@ -3,11 +3,12 @@ require 'sidekiq-datadog'
3
3
  require 'timecop'
4
4
 
5
5
  module Mock
6
- class Worker; end
6
+ class Worker; end # rubocop:disable Lint/EmptyClass
7
7
 
8
8
  class Statsd < ::Datadog::Statsd
9
- def send_stat(message)
10
- messages.push message
9
+ def send_stats(stat, delta, type, opts = EMPTY_OPTIONS)
10
+ full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags])
11
+ messages.push full_stat
11
12
  end
12
13
 
13
14
  def messages
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-datadog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitrij Denissenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-19 00:00:00.000000000 Z
11
+ date: 2021-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dogstatsd-ruby
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rubocop
84
+ name: rubocop-bsm
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -129,19 +129,26 @@ executables: []
129
129
  extensions: []
130
130
  extra_rdoc_files: []
131
131
  files:
132
+ - ".github/workflows/test.yml"
132
133
  - ".gitignore"
133
134
  - ".rubocop.yml"
134
- - ".travis.yml"
135
135
  - Gemfile
136
136
  - Gemfile.lock
137
+ - Gemfile_dogstats_v4
138
+ - Gemfile_dogstats_v4.lock
137
139
  - LICENSE.txt
138
140
  - README.md
139
141
  - Rakefile
140
142
  - lib/sidekiq-datadog.rb
143
+ - lib/sidekiq/datadog.rb
144
+ - lib/sidekiq/datadog/tag_builder.rb
141
145
  - lib/sidekiq/datadog/version.rb
146
+ - lib/sidekiq/middleware/client/datadog.rb
142
147
  - lib/sidekiq/middleware/server/datadog.rb
143
148
  - sidekiq-datadog.gemspec
144
- - spec/sidekiq/datadog/version_spec.rb
149
+ - spec/sidekiq/datadog/tag_builder_spec.rb
150
+ - spec/sidekiq/datadog_spec.rb
151
+ - spec/sidekiq/middleware/client/datadog_spec.rb
145
152
  - spec/sidekiq/middleware/server/datadog_spec.rb
146
153
  - spec/spec_helper.rb
147
154
  homepage: https://github.com/bsm/sidekiq-datadog
@@ -155,18 +162,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
162
  requirements:
156
163
  - - ">="
157
164
  - !ruby/object:Gem::Version
158
- version: '2.3'
165
+ version: '2.5'
159
166
  required_rubygems_version: !ruby/object:Gem::Requirement
160
167
  requirements:
161
168
  - - ">="
162
169
  - !ruby/object:Gem::Version
163
170
  version: '0'
164
171
  requirements: []
165
- rubygems_version: 3.0.6
172
+ rubygems_version: 3.2.15
166
173
  signing_key:
167
174
  specification_version: 4
168
175
  summary: Datadog metrics for sidekiq
169
176
  test_files:
170
- - spec/sidekiq/datadog/version_spec.rb
177
+ - spec/sidekiq/datadog/tag_builder_spec.rb
178
+ - spec/sidekiq/datadog_spec.rb
179
+ - spec/sidekiq/middleware/client/datadog_spec.rb
171
180
  - spec/sidekiq/middleware/server/datadog_spec.rb
172
181
  - spec/spec_helper.rb
data/.travis.yml DELETED
@@ -1,9 +0,0 @@
1
- cache: bundler
2
- language: ruby
3
- before_install:
4
- - gem install bundler
5
- rvm:
6
- - 2.6
7
- - 2.5
8
- - 2.4
9
- - 2.3