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 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
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
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
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-05-30
4
+
5
+ - Initial release
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
+ [![RSpec and Rubocop](https://github.com/kigster/datadog-statsd-schema/actions/workflows/ruby.yml/badge.svg)](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