sidekiq-datadog 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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