sidekiq-datadog 0.4.1 → 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 +4 -4
- data/.github/workflows/test.yml +24 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +17 -0
- data/Gemfile.lock +62 -27
- data/Gemfile_dogstats_v4 +5 -0
- data/Gemfile_dogstats_v4.lock +83 -0
- data/README.md +68 -3
- data/Rakefile +19 -3
- data/lib/sidekiq-datadog.rb +1 -1
- data/lib/sidekiq/datadog.rb +3 -0
- data/lib/sidekiq/datadog/tag_builder.rb +75 -0
- data/lib/sidekiq/datadog/version.rb +1 -1
- data/lib/sidekiq/middleware/client/datadog.rb +67 -0
- data/lib/sidekiq/middleware/server/datadog.rb +25 -49
- data/sidekiq-datadog.gemspec +19 -16
- data/spec/sidekiq/datadog/tag_builder_spec.rb +84 -0
- data/spec/sidekiq/datadog_spec.rb +7 -0
- data/spec/sidekiq/middleware/client/datadog_spec.rb +71 -0
- data/spec/sidekiq/middleware/server/datadog_spec.rb +82 -34
- data/spec/spec_helper.rb +10 -9
- metadata +69 -18
- data/.travis.yml +0 -5
- data/spec/sidekiq/datadog/version_spec.rb +0 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee5e44db186a926cdca3e3eae1a8c89c3665c67e067aa079b9a24bf8ae70fbd1
|
|
4
|
+
data.tar.gz: f0044e25b254c0e663e13a8f31e71f6f60a3d9048521e3592906377f787eba92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
inherit_gem:
|
|
2
|
+
rubocop-bsm:
|
|
3
|
+
- default.yml
|
|
4
|
+
inherit_mode:
|
|
5
|
+
merge:
|
|
6
|
+
- Exclude
|
|
7
|
+
|
|
8
|
+
AllCops:
|
|
9
|
+
TargetRubyVersion: "2.5"
|
|
10
|
+
|
|
11
|
+
Metrics/ParameterLists:
|
|
12
|
+
Max: 8
|
|
13
|
+
Naming/FileName:
|
|
14
|
+
Exclude:
|
|
15
|
+
- lib/sidekiq-datadog.rb
|
|
16
|
+
RSpec/MultipleMemoizedHelpers:
|
|
17
|
+
Enabled: false
|
data/Gemfile.lock
CHANGED
|
@@ -1,38 +1,70 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
sidekiq-datadog (0.
|
|
5
|
-
dogstatsd-ruby (>=
|
|
4
|
+
sidekiq-datadog (0.6.0)
|
|
5
|
+
dogstatsd-ruby (>= 4.2.0)
|
|
6
6
|
sidekiq
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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)
|
|
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)
|
|
26
31
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
27
|
-
rspec-support (~> 3.
|
|
28
|
-
rspec-mocks (3.
|
|
32
|
+
rspec-support (~> 3.10.0)
|
|
33
|
+
rspec-mocks (3.10.2)
|
|
29
34
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
30
|
-
rspec-support (~> 3.
|
|
31
|
-
rspec-support (3.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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)
|
|
36
68
|
|
|
37
69
|
PLATFORMS
|
|
38
70
|
ruby
|
|
@@ -40,8 +72,11 @@ PLATFORMS
|
|
|
40
72
|
DEPENDENCIES
|
|
41
73
|
bundler
|
|
42
74
|
rake
|
|
43
|
-
rspec
|
|
75
|
+
rspec
|
|
76
|
+
rubocop-bsm
|
|
77
|
+
rubocop-performance
|
|
44
78
|
sidekiq-datadog!
|
|
79
|
+
timecop
|
|
45
80
|
|
|
46
81
|
BUNDLED WITH
|
|
47
|
-
|
|
82
|
+
2.2.19
|
data/Gemfile_dogstats_v4
ADDED
|
@@ -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
|
|
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,22 +55,62 @@ 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: [->(
|
|
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
|
-
- *tags* - array of custom tags. These can be plain strings or lambda blocks
|
|
90
|
+
- *tags* - array of custom tags. These can be plain strings or lambda blocks.
|
|
91
|
+
- *skip_tags* - array of tag names that shouldn't be emitted.
|
|
58
92
|
- *statsd_host* - the statsD host, defaults to "localhost". Can also be set with the `STATSD_HOST` env var
|
|
59
93
|
- *statsd_port* - the statsD port, defaults to 8125. Can also be set with the `STATSD_PORT` env var
|
|
60
94
|
- *statsd* - custom statsd instance
|
|
61
95
|
|
|
62
96
|
For more detailed configuration options, please see the [Documentation](http://www.rubydoc.info/gems/sidekiq-datadog).
|
|
63
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
|
+
|
|
64
114
|
## Contributing
|
|
65
115
|
|
|
66
116
|
1. Fork it
|
|
@@ -69,3 +119,18 @@ For more detailed configuration options, please see the [Documentation](http://w
|
|
|
69
119
|
4. Push to the branch (`git push origin my-new-feature`)
|
|
70
120
|
5. Make a pull request
|
|
71
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
|
@@ -1,7 +1,23 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'bundler/gem_tasks'
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
Bundler.setup(:default, :development)
|
|
5
|
+
rescue Bundler::BundlerError => e
|
|
6
|
+
warn e.message
|
|
7
|
+
warn 'Run `bundle install` to install missing gems'
|
|
8
|
+
exit e.status_code
|
|
9
|
+
end
|
|
10
|
+
|
|
2
11
|
require 'rake'
|
|
12
|
+
|
|
3
13
|
require 'rspec/core'
|
|
4
14
|
require 'rspec/core/rake_task'
|
|
5
15
|
|
|
6
|
-
RSpec::Core::RakeTask.new(:spec)
|
|
7
|
-
|
|
16
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
17
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
require 'rubocop/rake_task'
|
|
21
|
+
RuboCop::RakeTask.new(:rubocop)
|
|
22
|
+
|
|
23
|
+
task default: %i[rubocop spec]
|
data/lib/sidekiq-datadog.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'sidekiq/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
|
|
@@ -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|
|
|
@@ -20,76 +20,52 @@ module Sidekiq
|
|
|
20
20
|
# * <tt>:hostname</tt> - the hostname used for instrumentation, defaults to system hostname, respects +INSTRUMENTATION_HOSTNAME+ env variable
|
|
21
21
|
# * <tt>:metric_name</tt> - the metric name (prefix) to use, defaults to "sidekiq.job"
|
|
22
22
|
# * <tt>:tags</tt> - array of custom tags, these can be plain strings or lambda blocks accepting a rack env instance
|
|
23
|
+
# * <tt>:skip_tags</tt> - array of tag names that shouldn't be emitted
|
|
23
24
|
# * <tt>:statsd_host</tt> - the statsD host, defaults to "localhost", respects +STATSD_HOST+ env variable
|
|
24
25
|
# * <tt>:statsd_port</tt> - the statsD port, defaults to 8125, respects +STATSD_PORT+ env variable
|
|
25
26
|
# * <tt>:statsd</tt> - custom statsd instance
|
|
26
27
|
def initialize(opts = {})
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
statsd_port = (opts[:statsd_port] || ENV['STATSD_PORT'] || 8125).to_i
|
|
28
|
+
statsd_host = opts[:statsd_host] || ENV['STATSD_HOST'] || 'localhost'
|
|
29
|
+
statsd_port = (opts[:statsd_port] || ENV['STATSD_PORT'] || 8125).to_i
|
|
30
30
|
|
|
31
|
-
@metric_name = opts[:metric_name] ||
|
|
31
|
+
@metric_name = opts[:metric_name] || 'sidekiq.job'
|
|
32
32
|
@statsd = opts[:statsd] || ::Datadog::Statsd.new(statsd_host, statsd_port)
|
|
33
|
-
@
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
env = Sidekiq.options[:environment] || ENV['RACK_ENV']
|
|
40
|
-
if env && @tags.none? {|t| t =~ /^env\:/ }
|
|
41
|
-
@tags.push("env:#{ENV['RACK_ENV']}")
|
|
42
|
-
end
|
|
33
|
+
@tag_builder = Sidekiq::Datadog::TagBuilder.new(
|
|
34
|
+
opts[:tags],
|
|
35
|
+
opts[:skip_tags],
|
|
36
|
+
opts[:hostname],
|
|
37
|
+
)
|
|
43
38
|
end
|
|
44
39
|
|
|
45
40
|
def call(worker, job, queue, *)
|
|
46
41
|
start = Time.now
|
|
42
|
+
clock = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
|
|
43
|
+
|
|
47
44
|
begin
|
|
48
45
|
yield
|
|
49
|
-
record(worker, job, queue, start)
|
|
50
|
-
rescue => e
|
|
51
|
-
record(worker, job, queue, start, e)
|
|
46
|
+
record(worker, job, queue, start, clock)
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
record(worker, job, queue, start, clock, e)
|
|
52
49
|
raise
|
|
53
50
|
end
|
|
54
51
|
end
|
|
55
52
|
|
|
56
53
|
private
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
queued_ms = ((start - Time.at(job["enqueued_at"])) * 1000).round
|
|
62
|
-
end
|
|
63
|
-
name = underscore(job['wrapped'] || worker.class.to_s)
|
|
64
|
-
tags = @tags.flat_map do |tag|
|
|
65
|
-
case tag when String then tag when Proc then tag.call(worker, job, queue, error) end
|
|
66
|
-
end
|
|
55
|
+
def record(worker, job, queue, start, clock, error = nil)
|
|
56
|
+
msec = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) - clock
|
|
57
|
+
tags = @tag_builder.build_tags(worker, job, queue, error)
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if error
|
|
71
|
-
kind = underscore(error.class.name.sub(/Error$/, ''))
|
|
72
|
-
tags.push "status:error", "error:#{kind}"
|
|
73
|
-
else
|
|
74
|
-
tags.push "status:ok"
|
|
75
|
-
end
|
|
76
|
-
tags.compact!
|
|
59
|
+
@statsd.increment @metric_name, tags: tags
|
|
60
|
+
@statsd.timing "#{@metric_name}.time", msec, tags: tags
|
|
77
61
|
|
|
78
|
-
|
|
79
|
-
@statsd.timing "#{@metric_name}.time", ms, :tags => tags
|
|
80
|
-
if queued_ms
|
|
81
|
-
@statsd.timing "#{@metric_name}.queued_time", queued_ms, :tags => tags
|
|
82
|
-
end
|
|
83
|
-
end
|
|
62
|
+
return unless job['enqueued_at']
|
|
84
63
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
|
88
|
-
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
|
89
|
-
word.tr!("-", "_")
|
|
90
|
-
word.downcase
|
|
91
|
-
end
|
|
64
|
+
queued_ms = ((start - Time.at(job['enqueued_at'])) * 1000).round
|
|
65
|
+
@statsd.timing "#{@metric_name}.queued_time", queued_ms, tags: tags
|
|
92
66
|
|
|
67
|
+
@statsd.flush if @statsd.respond_to?(:flush) # dogstatsd-ruby >= 5.0.0
|
|
68
|
+
end
|
|
93
69
|
end
|
|
94
70
|
end
|
|
95
71
|
end
|
data/sidekiq-datadog.gemspec
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
3
|
require 'sidekiq/datadog/version'
|
|
5
4
|
|
|
6
5
|
Gem::Specification.new do |s|
|
|
7
|
-
s.name =
|
|
6
|
+
s.name = 'sidekiq-datadog'
|
|
8
7
|
s.version = Sidekiq::Datadog::VERSION.dup
|
|
9
|
-
s.authors = [
|
|
10
|
-
s.email = [
|
|
11
|
-
s.description =
|
|
12
|
-
s.summary =
|
|
13
|
-
s.homepage =
|
|
8
|
+
s.authors = ['Dimitrij Denissenko']
|
|
9
|
+
s.email = ['dimitrij@blacksquaremedia.com']
|
|
10
|
+
s.description = 'Datadog metrics for sidekiq'
|
|
11
|
+
s.summary = 'Datadog metrics for sidekiq'
|
|
12
|
+
s.homepage = 'https://github.com/bsm/sidekiq-datadog'
|
|
14
13
|
|
|
15
|
-
s.files = `git ls-files`.split(
|
|
16
|
-
s.executables = s.files.grep(%r{^bin/}).map{
|
|
14
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
|
15
|
+
s.executables = s.files.grep(%r{^bin/}).map {|f| File.basename(f) }
|
|
17
16
|
s.test_files = s.files.grep(%r{^(spec)/})
|
|
18
|
-
s.require_paths = [
|
|
17
|
+
s.require_paths = ['lib']
|
|
18
|
+
s.required_ruby_version = '>= 2.5'
|
|
19
19
|
|
|
20
|
-
s.add_runtime_dependency(
|
|
21
|
-
s.add_runtime_dependency(
|
|
20
|
+
s.add_runtime_dependency('dogstatsd-ruby', '>= 4.2.0')
|
|
21
|
+
s.add_runtime_dependency('sidekiq')
|
|
22
22
|
|
|
23
|
-
s.add_development_dependency(
|
|
24
|
-
s.add_development_dependency(
|
|
25
|
-
s.add_development_dependency(
|
|
23
|
+
s.add_development_dependency('bundler')
|
|
24
|
+
s.add_development_dependency('rake')
|
|
25
|
+
s.add_development_dependency('rspec')
|
|
26
|
+
s.add_development_dependency('rubocop-bsm')
|
|
27
|
+
s.add_development_dependency('rubocop-performance')
|
|
28
|
+
s.add_development_dependency('timecop')
|
|
26
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
|
|
@@ -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,59 +1,107 @@
|
|
|
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 }
|
|
7
|
-
let(:tags)
|
|
8
|
-
[
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
let(:tags) do
|
|
9
|
+
['custom:tag', ->(w, *) { "worker:#{w.class.name[1..2]}" }]
|
|
10
|
+
end
|
|
11
|
+
let(:options) { {} }
|
|
12
|
+
let(:enqueued_at) { 1461881794.9312189 }
|
|
13
|
+
let(:expected_queued_time_ms) { 444 }
|
|
14
|
+
|
|
15
|
+
before do
|
|
16
|
+
statsd.messages.clear
|
|
17
|
+
|
|
18
|
+
clock_gettime_call_count = 0
|
|
19
|
+
expect(Process).to receive(:clock_gettime).twice do
|
|
20
|
+
clock_gettime_call_count += 1
|
|
21
|
+
clock_gettime_call_count == 1 ? 0 : 333
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Timecop.freeze(Time.at(enqueued_at + expected_queued_time_ms.to_f / 1000))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'sends an increment and timing event for each job run' do
|
|
28
|
+
subject.call(worker, { 'enqueued_at' => enqueued_at }, 'default') { 'ok' }
|
|
29
|
+
expect(statsd.messages).to eq([
|
|
30
|
+
'sidekiq.job:1|c|#custom:tag,worker:oc,host:test.host,env:test,name:mock/worker,'\
|
|
31
|
+
'queue:default,status:ok',
|
|
32
|
+
'sidekiq.job.time:333|ms|#custom:tag,worker:oc,host:test.host,env:test,name:mock/worker,'\
|
|
33
|
+
'queue:default,status:ok',
|
|
34
|
+
"sidekiq.job.queued_time:#{expected_queued_time_ms}|ms|#custom:tag,worker:oc,host:test.host,"\
|
|
35
|
+
'env:test,name:mock/worker,queue:default,status:ok',
|
|
20
36
|
])
|
|
21
37
|
end
|
|
22
38
|
|
|
23
|
-
it '
|
|
24
|
-
subject.call(worker, { 'enqueued_at' =>
|
|
25
|
-
expect(statsd.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"sidekiq.job.queued_time
|
|
39
|
+
it 'supports wrappers' do
|
|
40
|
+
subject.call(worker, { 'enqueued_at' => enqueued_at, 'wrapped' => 'wrap' }, nil) { 'ok' }
|
|
41
|
+
expect(statsd.messages).to eq([
|
|
42
|
+
'sidekiq.job:1|c|#custom:tag,worker:oc,host:test.host,env:test,name:wrap,status:ok',
|
|
43
|
+
'sidekiq.job.time:333|ms|#custom:tag,worker:oc,host:test.host,env:test,name:wrap,status:ok',
|
|
44
|
+
"sidekiq.job.queued_time:#{expected_queued_time_ms}|ms|#custom:tag,worker:oc,host:test.host,"\
|
|
45
|
+
'env:test,name:wrap,status:ok',
|
|
29
46
|
])
|
|
30
47
|
end
|
|
31
48
|
|
|
32
|
-
it '
|
|
49
|
+
it 'handles errors' do
|
|
33
50
|
expect(lambda {
|
|
34
|
-
subject.call(worker, {}, nil) {
|
|
35
|
-
}).to raise_error(
|
|
51
|
+
subject.call(worker, {}, nil) { raise 'doh!' }
|
|
52
|
+
}).to raise_error('doh!')
|
|
36
53
|
|
|
37
|
-
expect(statsd.
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
expect(statsd.messages).to eq([
|
|
55
|
+
'sidekiq.job:1|c|#custom:tag,worker:oc,host:test.host,env:test,name:mock/worker,'\
|
|
56
|
+
'status:error,error:runtime',
|
|
57
|
+
'sidekiq.job.time:333|ms|#custom:tag,worker:oc,host:test.host,env:test,name:mock/worker,'\
|
|
58
|
+
'status:error,error:runtime',
|
|
40
59
|
])
|
|
41
60
|
end
|
|
42
61
|
|
|
43
62
|
context 'with a dynamic tag list' do
|
|
44
|
-
let(:tags)
|
|
45
|
-
[
|
|
46
|
-
|
|
63
|
+
let(:tags) do
|
|
64
|
+
['custom:tag', ->(_w, j, *) { j['args'].map {|n| "arg:#{n}" } }]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'generates the correct tags' do
|
|
68
|
+
subject.call(worker, { 'enqueued_at' => enqueued_at, 'args' => [1, 2] }, 'default') { 'ok' }
|
|
47
69
|
|
|
48
|
-
|
|
49
|
-
|
|
70
|
+
expect(statsd.messages).to eq([
|
|
71
|
+
'sidekiq.job:1|c|#custom:tag,arg:1,arg:2,host:test.host,env:test,name:mock/worker,'\
|
|
72
|
+
'queue:default,status:ok',
|
|
73
|
+
'sidekiq.job.time:333|ms|#custom:tag,arg:1,arg:2,host:test.host,env:test,name:mock/worker,'\
|
|
74
|
+
'queue:default,status:ok',
|
|
75
|
+
"sidekiq.job.queued_time:#{expected_queued_time_ms}|ms|#custom:tag,arg:1,arg:2,"\
|
|
76
|
+
'host:test.host,env:test,name:mock/worker,queue:default,status:ok',
|
|
77
|
+
])
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context 'with a list of skipped tags' do
|
|
82
|
+
let(:tags) { [] }
|
|
83
|
+
let(:options) { { skip_tags: %i[env host name] } }
|
|
84
|
+
|
|
85
|
+
it 'sends metrics without the skipped tags' do
|
|
86
|
+
subject.call(worker, { 'enqueued_at' => 1461881794.9312189 }, 'default') { 'ok' }
|
|
50
87
|
|
|
51
|
-
expect(statsd.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"sidekiq.job.queued_time
|
|
88
|
+
expect(statsd.messages).to eq([
|
|
89
|
+
'sidekiq.job:1|c|#queue:default,status:ok',
|
|
90
|
+
'sidekiq.job.time:333|ms|#queue:default,status:ok',
|
|
91
|
+
"sidekiq.job.queued_time:#{expected_queued_time_ms}|ms|#queue:default,status:ok",
|
|
55
92
|
])
|
|
56
93
|
end
|
|
57
94
|
end
|
|
58
95
|
|
|
96
|
+
context 'when sidekiq/api is required' do
|
|
97
|
+
before do
|
|
98
|
+
require 'sidekiq/api'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'does not raise any errors' do
|
|
102
|
+
expect do
|
|
103
|
+
subject.call(worker, { 'enqueued_at' => enqueued_at }, 'default') { 'ok' }
|
|
104
|
+
end.not_to raise_error
|
|
105
|
+
end
|
|
106
|
+
end
|
|
59
107
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
ENV['RACK_ENV'] ||= 'test'
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
4
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
5
|
-
|
|
6
2
|
require 'sidekiq-datadog'
|
|
3
|
+
require 'timecop'
|
|
7
4
|
|
|
8
5
|
module Mock
|
|
9
|
-
class Worker
|
|
10
|
-
end
|
|
6
|
+
class Worker; end # rubocop:disable Lint/EmptyClass
|
|
11
7
|
|
|
12
8
|
class Statsd < ::Datadog::Statsd
|
|
13
|
-
def
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def messages
|
|
15
|
+
@messages ||= []
|
|
16
|
+
end
|
|
16
17
|
end
|
|
17
18
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sidekiq-datadog
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
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:
|
|
11
|
+
date: 2021-06-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: dogstatsd-ruby
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 4.2.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 4.2.0
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: sidekiq
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -25,19 +39,19 @@ dependencies:
|
|
|
25
39
|
- !ruby/object:Gem::Version
|
|
26
40
|
version: '0'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
42
|
+
name: bundler
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
30
44
|
requirements:
|
|
31
45
|
- - ">="
|
|
32
46
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
34
|
-
type: :
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
35
49
|
prerelease: false
|
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
51
|
requirements:
|
|
38
52
|
- - ">="
|
|
39
53
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
54
|
+
version: '0'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
56
|
name: rake
|
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,7 +67,7 @@ dependencies:
|
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
68
|
version: '0'
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
70
|
+
name: rspec
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
58
72
|
requirements:
|
|
59
73
|
- - ">="
|
|
@@ -67,19 +81,47 @@ dependencies:
|
|
|
67
81
|
- !ruby/object:Gem::Version
|
|
68
82
|
version: '0'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
84
|
+
name: rubocop-bsm
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rubocop-performance
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: timecop
|
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
|
72
114
|
requirements:
|
|
73
|
-
- - "
|
|
115
|
+
- - ">="
|
|
74
116
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '
|
|
117
|
+
version: '0'
|
|
76
118
|
type: :development
|
|
77
119
|
prerelease: false
|
|
78
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
121
|
requirements:
|
|
80
|
-
- - "
|
|
122
|
+
- - ">="
|
|
81
123
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '
|
|
124
|
+
version: '0'
|
|
83
125
|
description: Datadog metrics for sidekiq
|
|
84
126
|
email:
|
|
85
127
|
- dimitrij@blacksquaremedia.com
|
|
@@ -87,18 +129,26 @@ executables: []
|
|
|
87
129
|
extensions: []
|
|
88
130
|
extra_rdoc_files: []
|
|
89
131
|
files:
|
|
132
|
+
- ".github/workflows/test.yml"
|
|
90
133
|
- ".gitignore"
|
|
91
|
-
- ".
|
|
134
|
+
- ".rubocop.yml"
|
|
92
135
|
- Gemfile
|
|
93
136
|
- Gemfile.lock
|
|
137
|
+
- Gemfile_dogstats_v4
|
|
138
|
+
- Gemfile_dogstats_v4.lock
|
|
94
139
|
- LICENSE.txt
|
|
95
140
|
- README.md
|
|
96
141
|
- Rakefile
|
|
97
142
|
- lib/sidekiq-datadog.rb
|
|
143
|
+
- lib/sidekiq/datadog.rb
|
|
144
|
+
- lib/sidekiq/datadog/tag_builder.rb
|
|
98
145
|
- lib/sidekiq/datadog/version.rb
|
|
146
|
+
- lib/sidekiq/middleware/client/datadog.rb
|
|
99
147
|
- lib/sidekiq/middleware/server/datadog.rb
|
|
100
148
|
- sidekiq-datadog.gemspec
|
|
101
|
-
- spec/sidekiq/datadog/
|
|
149
|
+
- spec/sidekiq/datadog/tag_builder_spec.rb
|
|
150
|
+
- spec/sidekiq/datadog_spec.rb
|
|
151
|
+
- spec/sidekiq/middleware/client/datadog_spec.rb
|
|
102
152
|
- spec/sidekiq/middleware/server/datadog_spec.rb
|
|
103
153
|
- spec/spec_helper.rb
|
|
104
154
|
homepage: https://github.com/bsm/sidekiq-datadog
|
|
@@ -112,19 +162,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
112
162
|
requirements:
|
|
113
163
|
- - ">="
|
|
114
164
|
- !ruby/object:Gem::Version
|
|
115
|
-
version: '
|
|
165
|
+
version: '2.5'
|
|
116
166
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
167
|
requirements:
|
|
118
168
|
- - ">="
|
|
119
169
|
- !ruby/object:Gem::Version
|
|
120
170
|
version: '0'
|
|
121
171
|
requirements: []
|
|
122
|
-
|
|
123
|
-
rubygems_version: 2.7.6
|
|
172
|
+
rubygems_version: 3.2.15
|
|
124
173
|
signing_key:
|
|
125
174
|
specification_version: 4
|
|
126
175
|
summary: Datadog metrics for sidekiq
|
|
127
176
|
test_files:
|
|
128
|
-
- spec/sidekiq/datadog/
|
|
177
|
+
- spec/sidekiq/datadog/tag_builder_spec.rb
|
|
178
|
+
- spec/sidekiq/datadog_spec.rb
|
|
179
|
+
- spec/sidekiq/middleware/client/datadog_spec.rb
|
|
129
180
|
- spec/sidekiq/middleware/server/datadog_spec.rb
|
|
130
181
|
- spec/spec_helper.rb
|
data/.travis.yml
DELETED