datadog-statsd-schema 0.1.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/.rubocop_todo.yml +227 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +360 -0
- data/Rakefile +29 -0
- data/examples/README.md +44 -0
- data/examples/schema_emitter.png +0 -0
- data/examples/schema_emitter.rb +54 -0
- data/examples/shared.rb +43 -0
- data/examples/simple_emitter.rb +23 -0
- data/exe/datadog-statsd-schema +3 -0
- data/lib/datadog/statsd/emitter.rb +471 -0
- data/lib/datadog/statsd/schema/errors.rb +35 -0
- data/lib/datadog/statsd/schema/metric_definition.rb +106 -0
- data/lib/datadog/statsd/schema/namespace.rb +200 -0
- data/lib/datadog/statsd/schema/schema_builder.rb +227 -0
- data/lib/datadog/statsd/schema/tag_definition.rb +71 -0
- data/lib/datadog/statsd/schema/version.rb +9 -0
- data/lib/datadog/statsd/schema.rb +71 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bb6a6aae5d3d756626118bbf0215ea4a0fb6701a547d6543966480ef50512b2e
|
4
|
+
data.tar.gz: 7115508fd6fb2d1945f26472c7d8a9c718fed5c7096cdc037faeeaf1112da822
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e826c7e448f1a44ec76eba91b2643f04deb1093a89507a531dd051fc2dac6ace95482c30691295a219d740c217ede37c436d3328a29d256fb0550f53bf412727
|
7
|
+
data.tar.gz: 673df2c80be0e15af9a09cdd780c96092949ce8b00ad7bb1290d3e61efc1e7d46463ac0011db9b297335c425cf6ba693fcc9b17f1cc0b920dae8d7513a791d21
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
plugins:
|
4
|
+
- rubocop-rspec
|
5
|
+
|
6
|
+
AllCops:
|
7
|
+
TargetRubyVersion: 3.1
|
8
|
+
NewCops: enable
|
9
|
+
|
10
|
+
Style/StringLiterals:
|
11
|
+
EnforcedStyle: double_quotes
|
12
|
+
|
13
|
+
Style/StringLiteralsInInterpolation:
|
14
|
+
EnforcedStyle: double_quotes
|
15
|
+
|
16
|
+
Metrics/MethodLength:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/BlockLength:
|
20
|
+
Enabled: false
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2025-05-31 23:20:37 UTC using RuboCop version 1.75.8.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 2
|
10
|
+
# Configuration parameters: EnforcedStyle, AllowedGems, Include.
|
11
|
+
# SupportedStyles: Gemfile, gems.rb, gemspec
|
12
|
+
# Include: **/*.gemspec, **/Gemfile, **/gems.rb
|
13
|
+
Gemspec/DevelopmentDependencies:
|
14
|
+
Exclude:
|
15
|
+
- 'datadog-statsd-schema.gemspec'
|
16
|
+
|
17
|
+
# Offense count: 4
|
18
|
+
Lint/DuplicateMethods:
|
19
|
+
Exclude:
|
20
|
+
- 'lib/datadog/statsd/schema/schema_builder.rb'
|
21
|
+
|
22
|
+
# Offense count: 1
|
23
|
+
Lint/FloatComparison:
|
24
|
+
Exclude:
|
25
|
+
- 'lib/datadog/statsd/emitter.rb'
|
26
|
+
|
27
|
+
# Offense count: 8
|
28
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
29
|
+
Metrics/AbcSize:
|
30
|
+
Max: 48
|
31
|
+
|
32
|
+
# Offense count: 4
|
33
|
+
# Configuration parameters: CountComments, CountAsOne.
|
34
|
+
Metrics/ClassLength:
|
35
|
+
Max: 356
|
36
|
+
|
37
|
+
# Offense count: 6
|
38
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
39
|
+
Metrics/CyclomaticComplexity:
|
40
|
+
Max: 21
|
41
|
+
|
42
|
+
# Offense count: 5
|
43
|
+
# Configuration parameters: CountComments, CountAsOne.
|
44
|
+
Metrics/ModuleLength:
|
45
|
+
Max: 577
|
46
|
+
|
47
|
+
# Offense count: 2
|
48
|
+
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
49
|
+
Metrics/ParameterLists:
|
50
|
+
Max: 7
|
51
|
+
|
52
|
+
# Offense count: 5
|
53
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
54
|
+
Metrics/PerceivedComplexity:
|
55
|
+
Max: 20
|
56
|
+
|
57
|
+
# Offense count: 3
|
58
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
59
|
+
# Configuration parameters: EnforcedStyleForLeadingUnderscores.
|
60
|
+
# SupportedStylesForLeadingUnderscores: disallowed, required, optional
|
61
|
+
Naming/MemoizedInstanceVariableName:
|
62
|
+
Exclude:
|
63
|
+
- 'lib/datadog/statsd/emitter.rb'
|
64
|
+
|
65
|
+
# Offense count: 3
|
66
|
+
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
67
|
+
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
|
68
|
+
Naming/MethodParameterName:
|
69
|
+
Exclude:
|
70
|
+
- 'examples/shared.rb'
|
71
|
+
- 'lib/datadog/statsd/emitter.rb'
|
72
|
+
|
73
|
+
# Offense count: 3
|
74
|
+
# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
|
75
|
+
# NamePrefix: is_, has_, have_, does_
|
76
|
+
# ForbiddenPrefixes: is_, has_, have_, does_
|
77
|
+
# AllowedMethods: is_a?
|
78
|
+
# MethodDefinitionMacros: define_method, define_singleton_method
|
79
|
+
Naming/PredicateName:
|
80
|
+
Exclude:
|
81
|
+
- 'spec/**/*'
|
82
|
+
- 'lib/datadog/statsd/schema/namespace.rb'
|
83
|
+
|
84
|
+
# Offense count: 4
|
85
|
+
# Configuration parameters: Prefixes, AllowedPatterns.
|
86
|
+
# Prefixes: when, with, without
|
87
|
+
RSpec/ContextWording:
|
88
|
+
Exclude:
|
89
|
+
- 'spec/datadog/statsd/schema/namespace_spec.rb'
|
90
|
+
|
91
|
+
# Offense count: 15
|
92
|
+
# Configuration parameters: CountAsOne.
|
93
|
+
RSpec/ExampleLength:
|
94
|
+
Max: 19
|
95
|
+
|
96
|
+
# Offense count: 4
|
97
|
+
# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns.
|
98
|
+
RSpec/IndexedLet:
|
99
|
+
Exclude:
|
100
|
+
- 'spec/datadog/statsd/schema/namespace_spec.rb'
|
101
|
+
|
102
|
+
# Offense count: 36
|
103
|
+
# Configuration parameters: .
|
104
|
+
# SupportedStyles: have_received, receive
|
105
|
+
RSpec/MessageSpies:
|
106
|
+
EnforcedStyle: receive
|
107
|
+
|
108
|
+
# Offense count: 4
|
109
|
+
RSpec/MultipleExpectations:
|
110
|
+
Max: 3
|
111
|
+
|
112
|
+
# Offense count: 39
|
113
|
+
# Configuration parameters: AllowSubject.
|
114
|
+
RSpec/MultipleMemoizedHelpers:
|
115
|
+
Max: 9
|
116
|
+
|
117
|
+
# Offense count: 38
|
118
|
+
# Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
|
119
|
+
# SupportedStyles: always, named_only
|
120
|
+
RSpec/NamedSubject:
|
121
|
+
Exclude:
|
122
|
+
- 'spec/datadog/statsd/schema/namespace_spec.rb'
|
123
|
+
- 'spec/datadog/statsd/schema/schema_builder_spec.rb'
|
124
|
+
|
125
|
+
# Offense count: 2
|
126
|
+
RSpec/RepeatedExampleGroupBody:
|
127
|
+
Exclude:
|
128
|
+
- 'spec/datadog/statsd/schema/tag_definition_spec.rb'
|
129
|
+
|
130
|
+
# Offense count: 5
|
131
|
+
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
|
132
|
+
RSpec/VerifiedDoubles:
|
133
|
+
Exclude:
|
134
|
+
- 'spec/datadog/statsd/schema/metric_definition_spec.rb'
|
135
|
+
- 'spec/datadog/statsd/schema_spec.rb'
|
136
|
+
|
137
|
+
# Offense count: 8
|
138
|
+
# Configuration parameters: AllowedConstants.
|
139
|
+
Style/Documentation:
|
140
|
+
Exclude:
|
141
|
+
- 'spec/**/*'
|
142
|
+
- 'test/**/*'
|
143
|
+
- 'lib/datadog/statsd/emitter.rb'
|
144
|
+
- 'lib/datadog/statsd/schema.rb'
|
145
|
+
- 'lib/datadog/statsd/schema/errors.rb'
|
146
|
+
- 'lib/datadog/statsd/schema/metric_definition.rb'
|
147
|
+
- 'lib/datadog/statsd/schema/namespace.rb'
|
148
|
+
- 'lib/datadog/statsd/schema/schema_builder.rb'
|
149
|
+
- 'lib/datadog/statsd/schema/tag_definition.rb'
|
150
|
+
|
151
|
+
# Offense count: 1
|
152
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
153
|
+
# Configuration parameters: EnforcedStyle.
|
154
|
+
# SupportedStyles: always, always_true, never
|
155
|
+
Style/FrozenStringLiteralComment:
|
156
|
+
Exclude:
|
157
|
+
- '**/*.arb'
|
158
|
+
- 'exe/datadog-statsd-schema'
|
159
|
+
|
160
|
+
# Offense count: 3
|
161
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
162
|
+
# Configuration parameters: AllowedReceivers.
|
163
|
+
# AllowedReceivers: Thread.current
|
164
|
+
Style/HashEachMethods:
|
165
|
+
Exclude:
|
166
|
+
- 'lib/datadog/statsd/schema/namespace.rb'
|
167
|
+
|
168
|
+
# Offense count: 1
|
169
|
+
# Configuration parameters: MinBranchesCount.
|
170
|
+
Style/HashLikeCase:
|
171
|
+
Exclude:
|
172
|
+
- 'spec/datadog/statsd/schema/schema_builder_spec.rb'
|
173
|
+
|
174
|
+
# Offense count: 3
|
175
|
+
# This cop supports safe autocorrection (--autocorrect).
|
176
|
+
Style/IfUnlessModifier:
|
177
|
+
Exclude:
|
178
|
+
- 'lib/datadog/statsd/emitter.rb'
|
179
|
+
|
180
|
+
# Offense count: 1
|
181
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
182
|
+
# Configuration parameters: EnforcedStyle.
|
183
|
+
# SupportedStyles: literals, strict
|
184
|
+
Style/MutableConstant:
|
185
|
+
Exclude:
|
186
|
+
- 'lib/datadog/statsd/emitter.rb'
|
187
|
+
|
188
|
+
# Offense count: 1
|
189
|
+
Style/OpenStructUse:
|
190
|
+
Exclude:
|
191
|
+
- 'lib/datadog/statsd/emitter.rb'
|
192
|
+
|
193
|
+
# Offense count: 1
|
194
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
195
|
+
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
196
|
+
# AllowedMethods: present?, blank?, presence, try, try!
|
197
|
+
Style/SafeNavigation:
|
198
|
+
Exclude:
|
199
|
+
- 'lib/datadog/statsd/emitter.rb'
|
200
|
+
|
201
|
+
# Offense count: 1
|
202
|
+
# Configuration parameters: Max.
|
203
|
+
Style/SafeNavigationChainLength:
|
204
|
+
Exclude:
|
205
|
+
- 'lib/datadog/statsd/emitter.rb'
|
206
|
+
|
207
|
+
# Offense count: 2
|
208
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
209
|
+
Style/SlicingWithRange:
|
210
|
+
Exclude:
|
211
|
+
- 'lib/datadog/statsd/schema/namespace.rb'
|
212
|
+
|
213
|
+
# Offense count: 5
|
214
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
215
|
+
# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
|
216
|
+
# AllowedMethods: define_method
|
217
|
+
Style/SymbolProc:
|
218
|
+
Exclude:
|
219
|
+
- 'spec/datadog/statsd/schema/schema_builder_spec.rb'
|
220
|
+
- 'spec/datadog/statsd/schema_spec.rb'
|
221
|
+
|
222
|
+
# Offense count: 4
|
223
|
+
# This cop supports safe autocorrection (--autocorrect).
|
224
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
|
225
|
+
# URISchemes: http, https
|
226
|
+
Layout/LineLength:
|
227
|
+
Max: 140
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Konstantin Gredeskoul
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,360 @@
|
|
1
|
+
[](https://github.com/kigster/datadog-statsd-schema/actions/workflows/ruby.yml)
|
2
|
+
|
3
|
+
# Datadog::Statsd::Schema
|
4
|
+
|
5
|
+
This is a wrapper around [dogstatsd-ruby](https://github.com/DataDog/dogstatsd-ruby) gem that sends custom metrics via StatsD, with additional layer of validation based on a configurable schemas. Schemas can validate allowed metric names, associated tag and tag values. This approach can guide an organization towards a clear declarative approach to metrics and their tags, and then emitting them from within the application with the insurance that any invalid value would raise an exception.
|
6
|
+
|
7
|
+
We invite you to explore some of the provided [examples](./examples/README.md) which can be run from project's root, and are described in the linked README.
|
8
|
+
|
9
|
+
## Introduction
|
10
|
+
|
11
|
+
This is an extension to gem [dogstatsd-ruby](https://github.com/DataDog/dogstatsd-ruby) which enhances the original with a robust schema definition for both the custom metrics being sent, and the tags allowed (or required) to attach to the metric.
|
12
|
+
|
13
|
+
There are several interfaces to `Datadog::Statsd` instance — you can use the class methods of `Datadog::Statsd::Emitter`, and pass the typical statsd methods. But you can also use an instance of this class, which adds a number of features and powerful shortcuts.
|
14
|
+
|
15
|
+
If you do not pass the schema argument to the emitter, it will act as a wrapper around `Datadog::Statsd` instance: it will merge the global and local tags together, it will concatenate metric names together, so it's quite useful on it' on.
|
16
|
+
|
17
|
+
But the real power comes from defining a Schema of metrics and tags, and providing the schema to the Emitter as a constructor argument. In that case every metric send will be validated against the schema.
|
18
|
+
|
19
|
+
## Metric Types
|
20
|
+
|
21
|
+
> For more information about the metrics, please see the [Datadog Documentation](https://docs.datadoghq.com/metrics/custom_metrics/dogstatsd_metrics_submission/?tab=ruby).
|
22
|
+
|
23
|
+
There are 5 total metric types you can send with Statsd, and it's important to understand the differences:
|
24
|
+
|
25
|
+
* `COUNT` (eg, `Datadog::Statsd::Emitter.increment('emails.sent', by: 2)`)
|
26
|
+
* `GAUGE` (eg, `Datadog::Statsd::Emitter.gauge('users.on.site', 100)`)
|
27
|
+
* `HISTOGRAM` (eg, `Datadog::Statsd::Emitter.histogram('page.load.time', 100)`)
|
28
|
+
* `DISTRIBUTION` (eg,`Datadog::Statsd::Emitter.distribution('page.load.time', 100)`)
|
29
|
+
* `SET` (eg, `Datadog::Statsd::Emitter.set('users.unique', '12345')`)
|
30
|
+
|
31
|
+
NOTE: that `HISTOGRAM` converts your metric into FIVE separate metrics (with suffixes .`max`, .`median`, `avg`, .`count`, `p95`), while `DISTRIBUTION` explodes into TEN separate metrics (see the documentation). Do NOT use SET unless you know what you are doing.
|
32
|
+
|
33
|
+
You can send metrics via class methods of `Datadog::Statsd::Emitter`, or by instantiating the class.
|
34
|
+
|
35
|
+
## Sending Metrics
|
36
|
+
|
37
|
+
### Class Methods
|
38
|
+
|
39
|
+
This is the most straightforward way of using this gem. You can just pass your metric names and tags to the standard operations on Statsd, just like so:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
require 'datadog/statsd'
|
43
|
+
require 'datadog/statsd/schema'
|
44
|
+
|
45
|
+
Datadog::Statsd::Emitter.increment(
|
46
|
+
'marathon.started.total',
|
47
|
+
by: 7,
|
48
|
+
tags: {
|
49
|
+
course: "sf-marathon",
|
50
|
+
length: 26.212,
|
51
|
+
units: "miles"
|
52
|
+
},
|
53
|
+
schema: ....
|
54
|
+
)
|
55
|
+
```
|
56
|
+
|
57
|
+
As you can see, the API is identical to `Datadog::Statsd`. The main difference is that, if you provide a schema argument, the metric `marathon.started.total` must be pre-declared using the schema DSL language. In addition, the metric type ("count") and all of the tags and their possible values must be predeclared in the schema. Schema does support opening up a tag to any number of values, but that is not recommended.
|
58
|
+
|
59
|
+
So let's look at a more elaborate use case.
|
60
|
+
|
61
|
+
### Defining Schema
|
62
|
+
|
63
|
+
Below is an example of configuring the gem by creating a schema using the provided DSL. This can be a single global schema or assigned to a specific Statsd Sender, although you can have any number of Senders of type `Datadog::Statsd::Emitter` that map to a new connection and new defaults.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
require 'etc'
|
67
|
+
require 'git'
|
68
|
+
|
69
|
+
require 'datadog/statsd'
|
70
|
+
require 'datadog/statsd/schema'
|
71
|
+
|
72
|
+
# Define the global statsd instance that we'll use to send data through
|
73
|
+
$statsd = ::Datadog::Statsd.new(
|
74
|
+
'localhost', 8125,
|
75
|
+
delay_serialization: true
|
76
|
+
)
|
77
|
+
|
78
|
+
# Configure the schema with global tags and the above-created Statsd instance
|
79
|
+
Datadog::Statsd::Schema.configure do |config|
|
80
|
+
# This configures the global tags that will be attached to all methods
|
81
|
+
config.tags = {
|
82
|
+
env: "development",
|
83
|
+
arch: Etc.uname[:machine],
|
84
|
+
version: Git.open('.').object('HEAD').sha
|
85
|
+
}
|
86
|
+
|
87
|
+
config.statsd = $statsd
|
88
|
+
end
|
89
|
+
|
90
|
+
# Now we'll create a Schema using the provided Schema DSL:
|
91
|
+
schema = Datadog.schema do
|
92
|
+
# Transformers can be attached to the tags, and applied before the tags are submitted
|
93
|
+
# or validated.
|
94
|
+
transformers do
|
95
|
+
underscore: ->(text) { text.underscore },
|
96
|
+
downcase: ->(text) { text.downcase }
|
97
|
+
end
|
98
|
+
|
99
|
+
namespace :marathon do
|
100
|
+
tags do
|
101
|
+
tag :course,
|
102
|
+
values: ["san francisco", "boston", "new york"],
|
103
|
+
transform: %i[downcase underscore],
|
104
|
+
|
105
|
+
tag :marathon_type, values: %w[half full]
|
106
|
+
tag :status, values: %w[finished no-show incomplete]
|
107
|
+
tag :sponsorship, values: %w[nike cocacola redbull]
|
108
|
+
end
|
109
|
+
|
110
|
+
metrics do
|
111
|
+
# This defines a single metric "marathon.started.total"
|
112
|
+
namespace :started do
|
113
|
+
counter :total do
|
114
|
+
description "Incrementing - the total number of people who were registered for this marathon"
|
115
|
+
tags required: %i[ course marathon_type ],
|
116
|
+
allowed: %i[ sponsorship ]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# defines two metrics: a counter metric named "marathon.finished.total" and
|
121
|
+
# a distribution metric "marathon.finished.duration"
|
122
|
+
namespace :finished do
|
123
|
+
counter :total, inherit_tags: "marathon.started.total",
|
124
|
+
description "The number of people who finished a given marathon"
|
125
|
+
tags required: %i[ status ]
|
126
|
+
end
|
127
|
+
|
128
|
+
distribution :duration, units: "minutes", inherit_tags: "marathon.finished.count" do
|
129
|
+
description "The distribution of all finish times registered."
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
my_sender = Datadog.emitter(
|
137
|
+
metric: 'marathon',
|
138
|
+
schema: schema,
|
139
|
+
validation_mode: :strict,
|
140
|
+
tags: { marathon_type: :full, course: "san-francisco" }
|
141
|
+
)
|
142
|
+
|
143
|
+
my_sender.increment('started.total', by: 43579) # register all participants at start
|
144
|
+
# time passes, first runners start to arrive
|
145
|
+
my_sender.increment('finished.total') # register one at a time
|
146
|
+
my_sender.distribution('finished.duration', 33.21, tags: { sponsorship: 'nike' })
|
147
|
+
...
|
148
|
+
my_sender.increment('finished.total')
|
149
|
+
my_sender.distribution('finished.duration', 35.09, tags: { sponsorship: "redbull" })
|
150
|
+
```
|
151
|
+
|
152
|
+
In this case, the schema will validate that the metrics are named `marathon.finished.total` and `marathon.finished.duration`, and that their tags are appropriately defined.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
finish_sender = Datadog.emitter(
|
156
|
+
schema: schema,
|
157
|
+
validation_mode: :warn,
|
158
|
+
metric: "marathon.finished",
|
159
|
+
tags: { marathon_type: :full, course: "san-francisco" }
|
160
|
+
)
|
161
|
+
finish.increment("total")
|
162
|
+
finish.distribution("duration", 34)
|
163
|
+
```
|
164
|
+
|
165
|
+
The above code will transmit the following metric, with the following tags:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
$statsd.increment(
|
169
|
+
"marathon.finished.total",
|
170
|
+
tags: { marathon_type: :full, course: "san-francisco" }
|
171
|
+
)
|
172
|
+
|
173
|
+
$statsd.distribution(
|
174
|
+
"marathon.finished.duration",
|
175
|
+
tags: { marathon_type: :full, course: "san-francisco" }
|
176
|
+
)
|
177
|
+
```
|
178
|
+
|
179
|
+
### Validation Mode
|
180
|
+
|
181
|
+
There are four validation modes you can pass to an emitter to accompany a schema:
|
182
|
+
|
183
|
+
1. `:strict` — raise an exception when anything is out of the ordinary is passed
|
184
|
+
2. `:warn` — print to stderr and continue
|
185
|
+
3. `:drop` — drop this metric
|
186
|
+
4. `:off` — no validation, as if schema was not even passed.
|
187
|
+
|
188
|
+
### An Example Tracking Web Performance
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
Datadog::Statsd::Schema.configure do |config|
|
192
|
+
config.statsd = $statsd
|
193
|
+
config.schema = Datadog::Statsd::Schema.new do
|
194
|
+
namespace "web" do
|
195
|
+
namespace "request" do
|
196
|
+
tags do
|
197
|
+
tag :uri,
|
198
|
+
values: %r{.*}
|
199
|
+
|
200
|
+
tag :logged_in,
|
201
|
+
values: %w[logged_in logged_out]
|
202
|
+
|
203
|
+
tag :billing_plan,
|
204
|
+
values: %w[premium trial free]
|
205
|
+
|
206
|
+
tag :controller,
|
207
|
+
values: %r{[a-z.]*},
|
208
|
+
transform: [ :underscore, :downcase ]
|
209
|
+
|
210
|
+
tag :action,
|
211
|
+
values: %r{[a-z.]*},
|
212
|
+
transform: [ :underscore, :downcase ]
|
213
|
+
|
214
|
+
tag :method,
|
215
|
+
values: %i[get post put patch delete head options trace connect],
|
216
|
+
transform: [ :downcase ]
|
217
|
+
|
218
|
+
tag :status_code,
|
219
|
+
type: :integer,
|
220
|
+
validate: ->(code) { (100..599).include?(code) }
|
221
|
+
end
|
222
|
+
|
223
|
+
metrics do
|
224
|
+
# This distribution allows tracking of the latency of the request.
|
225
|
+
distribution :duration do
|
226
|
+
description "HTTP request processing time in milliseconds"
|
227
|
+
tags allowed: %w[controller action method status_code region]
|
228
|
+
required: %w[controller]
|
229
|
+
end
|
230
|
+
|
231
|
+
# This counter allows tracking the frequency of each controller/action
|
232
|
+
counter :total, inherit_tags: :duration do
|
233
|
+
description "Total number of requests received"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
|
243
|
+
Let's say this monitor only tracks requests from logged in premium users, then you can provide those tags here, and they will be sent together with individual invocations:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
# We'll use the shorthand version to create this Emitter.
|
247
|
+
# It's equivalent to *Datadog::Statsd::Emitter.new*
|
248
|
+
traffic_monitor = Datadog.emitter(
|
249
|
+
self,
|
250
|
+
metric: "web.request",
|
251
|
+
tags: { billing_plan: :premium, logged_in: :logged_in }
|
252
|
+
)
|
253
|
+
|
254
|
+
my_sender.increment('total', tags: { uri: '/home/settings', method: :get } )
|
255
|
+
my_sender.distribution('duration', tags: { uri: '/home/settings', method: :get } )
|
256
|
+
|
257
|
+
my_sender.increment('total', tags: { uri: '/app/calendar', method: :post} )
|
258
|
+
my_sender.distribution('duration', tags: { uri: '/app/calendar', method: :post } )
|
259
|
+
```
|
260
|
+
|
261
|
+
The above code will send two metrics: `web.request.total` as a counter, tagged with: `{ billing_plan: :premium, logged_in: :logged_in, uri: '/home/settings' }` and the second time for the `uri: '/app/calendar'`.
|
262
|
+
|
263
|
+
### Emitter
|
264
|
+
|
265
|
+
You can create instances of this class and use the instance to emit custom metrics. You may want to do this, instead of using the class methods directly, for two reasons:
|
266
|
+
|
267
|
+
1. You want to send metrics from several places in the codebase, but have them share the "emitter" tag (which i.e. defines the source, a class, or object)emitting the metric, or any other tags for that matter.
|
268
|
+
|
269
|
+
2. You want to send metrics with a different sample rate than the defaults.
|
270
|
+
|
271
|
+
In both cases, you can create an instance of this class and use it to emit metrics.
|
272
|
+
|
273
|
+
#### Naming Metrics
|
274
|
+
|
275
|
+
Please remember that naming *IS* important. Good naming is self-documenting, easy to slice the data by, and easy to understand and analyze. Keep the number of unique metric names down, number of tags down, and the number of possible tag values should always be finite. If in doubt, set a tag, instead of creating a new metric.
|
276
|
+
|
277
|
+
#### Example — Tracking email delivery
|
278
|
+
|
279
|
+
Imagine that we want to track email delivery. But we have many types of emails that we send. Instead of creating new metric for each new email type, use the tag "email_type" to specify what type of email it is.
|
280
|
+
|
281
|
+
Keep metric name list short, eg: "emails.queued", "emails.sent", "emails.delivered" are good metrics as they define a distinctly unique events. However, should you want to differentiate between different types of emails, you could theoretically do the following: (BAD EXAMPLE, DO NOT FOLLOW) — "emails.sent.welcome", "emails.sent.payment". But this example conflates two distinct events into a single metric. Instead, we should use tags to set event properties, such as what type of email that is.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
|
285
|
+
emails_emitter = Datadog.emitter(
|
286
|
+
self,
|
287
|
+
metric: 'emails'
|
288
|
+
)
|
289
|
+
|
290
|
+
emails_emitter.increment('queued.total')
|
291
|
+
emails_emitter.increment('delivered.total', by: count)
|
292
|
+
emails_emitter.gauge('queue.size', EmailQueue.size)
|
293
|
+
```
|
294
|
+
|
295
|
+
#### What's the Emitter Constructor Arguments?
|
296
|
+
|
297
|
+
The first argument to the `Emitter.new()` or `Datadog.emitter()` (those are equivalent) is an object or a string or a class that's converted to a tag called `emitter`. This is the source class or object that sent the metric. The same mwtric may come from various places in your code, and `emitter` tag allows you to differentiate between them.
|
298
|
+
|
299
|
+
Subsequent arguments are hash arguments.
|
300
|
+
|
301
|
+
* `metric` — The (optional) name of the metric to track. If set to, eg. `emails`, then any subsequent method sending metric will prepend `emails.` to it, for example:
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
emitter.increment('sent.total', by: 3)
|
305
|
+
```
|
306
|
+
|
307
|
+
Will actually increment the metric `emails.sent.total`.
|
308
|
+
|
309
|
+
#### Other Examples
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
|
313
|
+
Datadog.emitter(self)
|
314
|
+
.increment('emails.sent', by: 2)
|
315
|
+
|
316
|
+
Datadog.emitter(ab_test: { 'login_test_2025' => 'control' })
|
317
|
+
.increment('users.logged_in')
|
318
|
+
# => tags: { ab_test_name: 'login_test_2025',
|
319
|
+
# ab_test_group: 'control' }
|
320
|
+
|
321
|
+
Datadog.emitter(SessionsController, metric: 'users')
|
322
|
+
.gauge('logged_in', 100)
|
323
|
+
|
324
|
+
sessions = Datadog.emitter(SessionsController, metric: 'users')
|
325
|
+
# => tags: { emitter: "sessions_controller" }
|
326
|
+
sessions.gauge('active', 100)
|
327
|
+
sessions.distribution('active.total', 114)
|
328
|
+
```
|
329
|
+
|
330
|
+
## Installation
|
331
|
+
|
332
|
+
|
333
|
+
```bash
|
334
|
+
bundle add datadog-statsd-schema
|
335
|
+
```
|
336
|
+
|
337
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
338
|
+
|
339
|
+
```bash
|
340
|
+
gem install datadog-statsd-schema
|
341
|
+
```
|
342
|
+
|
343
|
+
## Usage
|
344
|
+
|
345
|
+
1. Define your metrics and tagging schema
|
346
|
+
2. Create as many "emitters" as necessary and start sending!
|
347
|
+
|
348
|
+
## Development
|
349
|
+
|
350
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
351
|
+
|
352
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
353
|
+
|
354
|
+
## Contributing
|
355
|
+
|
356
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/kigster/datadog-statsd-schema](https://github.com/kigster/datadog-statsd-schema)
|
357
|
+
|
358
|
+
## License
|
359
|
+
|
360
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
def shell(*args)
|
8
|
+
puts "running: #{args.join(' ')}"
|
9
|
+
system(args.join(' '))
|
10
|
+
end
|
11
|
+
|
12
|
+
task :clean do
|
13
|
+
shell('rm -rf pkg/ tmp/ coverage/ doc/ ' )
|
14
|
+
end
|
15
|
+
|
16
|
+
task gem: [:build] do
|
17
|
+
shell('gem install pkg/*')
|
18
|
+
end
|
19
|
+
|
20
|
+
task permissions: [:clean] do
|
21
|
+
shell("chmod -v o+r,g+r * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*")
|
22
|
+
shell("find . -type d -exec chmod o+x,g+x {} \\;")
|
23
|
+
end
|
24
|
+
|
25
|
+
task build: :permissions
|
26
|
+
|
27
|
+
RSpec::Core::RakeTask.new(:spec)
|
28
|
+
|
29
|
+
task default: :spec
|