datadog-statsd-schema 0.1.1 → 0.1.2
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/.envrc +2 -0
- data/.rspec +2 -1
- data/.rubocop.yml +6 -0
- data/.rubocop_todo.yml +10 -30
- data/FUTURE_DIRECTION.md +32 -0
- data/README.md +529 -250
- data/Rakefile +7 -7
- data/examples/shared.rb +1 -1
- data/lib/datadog/statsd/emitter.rb +102 -21
- data/lib/datadog/statsd/schema/errors.rb +54 -1
- data/lib/datadog/statsd/schema/metric_definition.rb +86 -3
- data/lib/datadog/statsd/schema/namespace.rb +91 -5
- data/lib/datadog/statsd/schema/schema_builder.rb +162 -4
- data/lib/datadog/statsd/schema/tag_definition.rb +66 -6
- data/lib/datadog/statsd/schema/version.rb +6 -1
- data/lib/datadog/statsd/schema.rb +89 -13
- metadata +4 -2
@@ -5,22 +5,58 @@ require "dry-types"
|
|
5
5
|
require_relative "tag_definition"
|
6
6
|
require_relative "metric_definition"
|
7
7
|
|
8
|
+
# @author Datadog Team
|
9
|
+
# @since 0.1.0
|
8
10
|
module Datadog
|
9
11
|
class Statsd
|
12
|
+
# Schema definition and validation module for StatsD metrics
|
10
13
|
module Schema
|
14
|
+
# Represents a namespace in the metric schema hierarchy
|
15
|
+
# Namespaces contain tags, metrics, and nested namespaces, providing organization and scoping
|
16
|
+
# @example Basic namespace
|
17
|
+
# namespace = Namespace.new(
|
18
|
+
# name: :web,
|
19
|
+
# description: "Web application metrics"
|
20
|
+
# )
|
21
|
+
# @example Namespace with tags and metrics
|
22
|
+
# namespace = Namespace.new(
|
23
|
+
# name: :api,
|
24
|
+
# tags: { controller: tag_def, action: tag_def2 },
|
25
|
+
# metrics: { requests: metric_def }
|
26
|
+
# )
|
27
|
+
# @author Datadog Team
|
28
|
+
# @since 0.1.0
|
11
29
|
class Namespace < Dry::Struct
|
12
|
-
# Include the types module for easier access
|
30
|
+
# Include the types module for easier access to Dry::Types
|
13
31
|
module Types
|
14
32
|
include Dry.Types()
|
15
33
|
end
|
16
34
|
|
35
|
+
# The namespace name as a symbol
|
36
|
+
# @return [Symbol] Namespace name
|
17
37
|
attribute :name, Types::Strict::Symbol
|
38
|
+
|
39
|
+
# Hash of tag definitions within this namespace
|
40
|
+
# @return [Hash<Symbol, TagDefinition>] Tag name to TagDefinition mapping
|
18
41
|
attribute :tags, Types::Hash.map(Types::Symbol, TagDefinition).default({}.freeze)
|
42
|
+
|
43
|
+
# Hash of metric definitions within this namespace
|
44
|
+
# @return [Hash<Symbol, MetricDefinition>] Metric name to MetricDefinition mapping
|
19
45
|
attribute :metrics, Types::Hash.map(Types::Symbol, MetricDefinition).default({}.freeze)
|
46
|
+
|
47
|
+
# Hash of nested namespaces within this namespace
|
48
|
+
# @return [Hash<Symbol, Namespace>] Namespace name to Namespace mapping
|
20
49
|
attribute :namespaces, Types::Hash.map(Types::Symbol, Namespace).default({}.freeze)
|
50
|
+
|
51
|
+
# Human-readable description of this namespace
|
52
|
+
# @return [String, nil] Description text
|
21
53
|
attribute :description, Types::String.optional.default(nil)
|
22
54
|
|
23
|
-
# Get the full path of this namespace
|
55
|
+
# Get the full path of this namespace including parent namespaces
|
56
|
+
# @param parent_path [Array<Symbol>] Array of parent namespace names
|
57
|
+
# @return [Array<Symbol>] Full namespace path
|
58
|
+
# @example
|
59
|
+
# namespace.full_path([:web, :api]) # => [:web, :api, :request]
|
24
60
|
def full_path(parent_path = [])
|
25
61
|
return [name] if parent_path.empty?
|
26
62
|
|
@@ -28,72 +64,109 @@ module Datadog
|
|
28
64
|
end
|
29
65
|
|
30
66
|
# Find a metric by name within this namespace
|
67
|
+
# @param metric_name [String, Symbol] Name of the metric to find
|
68
|
+
# @return [MetricDefinition, nil] The metric definition or nil if not found
|
69
|
+
# @example
|
70
|
+
# namespace.find_metric(:page_views) # => MetricDefinition instance
|
31
71
|
def find_metric(metric_name)
|
32
72
|
metric_symbol = metric_name.to_sym
|
33
73
|
metrics[metric_symbol]
|
34
74
|
end
|
35
75
|
|
36
76
|
# Find a tag definition by name within this namespace
|
77
|
+
# @param tag_name [String, Symbol] Name of the tag to find
|
78
|
+
# @return [TagDefinition, nil] The tag definition or nil if not found
|
79
|
+
# @example
|
80
|
+
# namespace.find_tag(:controller) # => TagDefinition instance
|
37
81
|
def find_tag(tag_name)
|
38
82
|
tag_symbol = tag_name.to_sym
|
39
83
|
tags[tag_symbol]
|
40
84
|
end
|
41
85
|
|
42
86
|
# Find a nested namespace by name
|
87
|
+
# @param namespace_name [String, Symbol] Name of the namespace to find
|
88
|
+
# @return [Namespace, nil] The nested namespace or nil if not found
|
89
|
+
# @example
|
90
|
+
# namespace.find_namespace(:api) # => Namespace instance
|
43
91
|
def find_namespace(namespace_name)
|
44
92
|
namespace_symbol = namespace_name.to_sym
|
45
93
|
namespaces[namespace_symbol]
|
46
94
|
end
|
47
95
|
|
48
|
-
# Add a new metric to this namespace
|
96
|
+
# Add a new metric to this namespace (returns new namespace instance)
|
97
|
+
# @param metric_definition [MetricDefinition] The metric definition to add
|
98
|
+
# @return [Namespace] New namespace instance with the added metric
|
49
99
|
def add_metric(metric_definition)
|
50
100
|
new(metrics: metrics.merge(metric_definition.name => metric_definition))
|
51
101
|
end
|
52
102
|
|
53
|
-
# Add a new tag definition to this namespace
|
103
|
+
# Add a new tag definition to this namespace (returns new namespace instance)
|
104
|
+
# @param tag_definition [TagDefinition] The tag definition to add
|
105
|
+
# @return [Namespace] New namespace instance with the added tag
|
54
106
|
def add_tag(tag_definition)
|
55
107
|
new(tags: tags.merge(tag_definition.name => tag_definition))
|
56
108
|
end
|
57
109
|
|
58
|
-
# Add a nested namespace
|
110
|
+
# Add a nested namespace (returns new namespace instance)
|
111
|
+
# @param namespace [Namespace] The namespace to add
|
112
|
+
# @return [Namespace] New namespace instance with the added namespace
|
59
113
|
def add_namespace(namespace)
|
60
114
|
new(namespaces: namespaces.merge(namespace.name => namespace))
|
61
115
|
end
|
62
116
|
|
63
117
|
# Get all metric names in this namespace (not including nested)
|
118
|
+
# @return [Array<Symbol>] Array of metric names
|
64
119
|
def metric_names
|
65
120
|
metrics.keys
|
66
121
|
end
|
67
122
|
|
68
123
|
# Get all tag names in this namespace
|
124
|
+
# @return [Array<Symbol>] Array of tag names
|
69
125
|
def tag_names
|
70
126
|
tags.keys
|
71
127
|
end
|
72
128
|
|
73
129
|
# Get all nested namespace names
|
130
|
+
# @return [Array<Symbol>] Array of namespace names
|
74
131
|
def namespace_names
|
75
132
|
namespaces.keys
|
76
133
|
end
|
77
134
|
|
78
135
|
# Check if this namespace contains a metric
|
136
|
+
# @param metric_name [String, Symbol] Name of the metric to check
|
137
|
+
# @return [Boolean] true if metric exists
|
79
138
|
def has_metric?(metric_name)
|
80
139
|
metric_symbol = metric_name.to_sym
|
81
140
|
metrics.key?(metric_symbol)
|
82
141
|
end
|
83
142
|
|
84
143
|
# Check if this namespace contains a tag definition
|
144
|
+
# @param tag_name [String, Symbol] Name of the tag to check
|
145
|
+
# @return [Boolean] true if tag exists
|
85
146
|
def has_tag?(tag_name)
|
86
147
|
tag_symbol = tag_name.to_sym
|
87
148
|
tags.key?(tag_symbol)
|
88
149
|
end
|
89
150
|
|
90
151
|
# Check if this namespace contains a nested namespace
|
152
|
+
# @param namespace_name [String, Symbol] Name of the namespace to check
|
153
|
+
# @return [Boolean] true if namespace exists
|
91
154
|
def has_namespace?(namespace_name)
|
92
155
|
namespace_symbol = namespace_name.to_sym
|
93
156
|
namespaces.key?(namespace_symbol)
|
94
157
|
end
|
95
158
|
|
96
159
|
# Get all metrics recursively including from nested namespaces
|
160
|
+
# @param path [Array<Symbol>] Current namespace path (used for recursion)
|
161
|
+
# @return [Hash] Hash mapping full metric names to metric info
|
162
|
+
# @example
|
163
|
+
# {
|
164
|
+
# "web.page_views" => {
|
165
|
+
# definition: MetricDefinition,
|
166
|
+
# namespace_path: [:web],
|
167
|
+
# namespace: Namespace
|
168
|
+
# }
|
169
|
+
# }
|
97
170
|
def all_metrics(path = [])
|
98
171
|
# Filter out :root from the path to avoid it appearing in metric names
|
99
172
|
current_path = path + [name]
|
@@ -119,11 +192,14 @@ module Datadog
|
|
119
192
|
end
|
120
193
|
|
121
194
|
# Get all tag definitions including inherited from parent namespaces
|
195
|
+
# @param parent_tags [Hash] Tag definitions from parent namespaces
|
196
|
+
# @return [Hash<Symbol, TagDefinition>] Combined tag definitions
|
122
197
|
def effective_tags(parent_tags = {})
|
123
198
|
parent_tags.merge(tags)
|
124
199
|
end
|
125
200
|
|
126
201
|
# Validate that all tag references in metrics exist
|
202
|
+
# @return [Array<String>] Array of validation error messages
|
127
203
|
def validate_tag_references
|
128
204
|
errors = []
|
129
205
|
|
@@ -148,6 +224,10 @@ module Datadog
|
|
148
224
|
end
|
149
225
|
|
150
226
|
# Find metric by path (e.g., "request.duration" within web namespace)
|
227
|
+
# @param path [String] Dot-separated path to the metric
|
228
|
+
# @return [MetricDefinition, nil] The metric definition or nil if not found
|
229
|
+
# @example
|
230
|
+
# namespace.find_metric_by_path("api.requests") # => MetricDefinition
|
151
231
|
def find_metric_by_path(path)
|
152
232
|
parts = path.split(".")
|
153
233
|
|
@@ -167,6 +247,10 @@ module Datadog
|
|
167
247
|
end
|
168
248
|
|
169
249
|
# Get namespace by path (e.g., "web.request")
|
250
|
+
# @param path [String] Dot-separated path to the namespace
|
251
|
+
# @return [Namespace, nil] The namespace or nil if not found
|
252
|
+
# @example
|
253
|
+
# root_namespace.find_namespace_by_path("web.api") # => Namespace
|
170
254
|
def find_namespace_by_path(path)
|
171
255
|
return self if path.empty?
|
172
256
|
|
@@ -186,11 +270,13 @@ module Datadog
|
|
186
270
|
end
|
187
271
|
|
188
272
|
# Count total metrics including nested namespaces
|
273
|
+
# @return [Integer] Total number of metrics in this namespace tree
|
189
274
|
def total_metrics_count
|
190
275
|
metrics.count + namespaces.values.sum(&:total_metrics_count)
|
191
276
|
end
|
192
277
|
|
193
278
|
# Count total namespaces including nested
|
279
|
+
# @return [Integer] Total number of namespaces in this namespace tree
|
194
280
|
def total_namespaces_count
|
195
281
|
namespaces.count + namespaces.values.sum(&:total_namespaces_count)
|
196
282
|
end
|
@@ -4,18 +4,50 @@ require_relative "namespace"
|
|
4
4
|
require_relative "tag_definition"
|
5
5
|
require_relative "metric_definition"
|
6
6
|
|
7
|
+
# @author Datadog Team
|
8
|
+
# @since 0.1.0
|
7
9
|
module Datadog
|
8
10
|
class Statsd
|
11
|
+
# Schema definition and validation module for StatsD metrics
|
9
12
|
module Schema
|
13
|
+
# Builder class for constructing metric schemas using a DSL
|
14
|
+
# Provides a fluent interface for defining namespaces, tags, and metrics
|
15
|
+
# @example Basic schema building
|
16
|
+
# builder = SchemaBuilder.new
|
17
|
+
# builder.namespace :web do
|
18
|
+
# tags do
|
19
|
+
# tag :controller, values: %w[users posts]
|
20
|
+
# end
|
21
|
+
# metrics do
|
22
|
+
# counter :page_views, tags: { required: [:controller] }
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# schema = builder.build
|
26
|
+
# @author Datadog Team
|
27
|
+
# @since 0.1.0
|
10
28
|
class SchemaBuilder
|
11
|
-
|
29
|
+
# Hash of transformer functions available for tag transformations
|
30
|
+
# @return [Hash<Symbol, Proc>] Transformer name to proc mapping
|
31
|
+
attr_reader :transformers
|
12
32
|
|
33
|
+
# The root namespace of the schema being built
|
34
|
+
# @return [Namespace] Root namespace instance
|
35
|
+
attr_reader :root_namespace
|
36
|
+
|
37
|
+
# Initialize a new schema builder
|
13
38
|
def initialize
|
14
39
|
@transformers = {}
|
15
40
|
@root_namespace = Namespace.new(name: :root)
|
16
41
|
end
|
17
42
|
|
18
43
|
# Define transformers that can be used by tag definitions
|
44
|
+
# @yield [TransformerBuilder] Block for defining transformers
|
45
|
+
# @return [Hash<Symbol, Proc>] Hash of defined transformers
|
46
|
+
# @example
|
47
|
+
# builder.transformers do
|
48
|
+
# underscore { |value| value.to_s.underscore }
|
49
|
+
# downcase { |value| value.to_s.downcase }
|
50
|
+
# end
|
19
51
|
def transformers(&)
|
20
52
|
return @transformers unless block_given?
|
21
53
|
|
@@ -23,6 +55,14 @@ module Datadog
|
|
23
55
|
end
|
24
56
|
|
25
57
|
# Define a namespace
|
58
|
+
# @param name [Symbol] Name of the namespace
|
59
|
+
# @yield [NamespaceBuilder] Block for defining namespace contents
|
60
|
+
# @return [void]
|
61
|
+
# @example
|
62
|
+
# builder.namespace :web do
|
63
|
+
# description "Web application metrics"
|
64
|
+
# # ... tags and metrics definitions
|
65
|
+
# end
|
26
66
|
def namespace(name, &)
|
27
67
|
builder = NamespaceBuilder.new(name, @transformers)
|
28
68
|
builder.instance_eval(&) if block_given?
|
@@ -32,35 +72,76 @@ module Datadog
|
|
32
72
|
end
|
33
73
|
|
34
74
|
# Build the final schema (returns the root namespace)
|
75
|
+
# @return [Namespace] The root namespace containing the entire schema
|
35
76
|
def build
|
36
77
|
@root_namespace
|
37
78
|
end
|
38
79
|
|
39
80
|
# Validate the schema for consistency
|
81
|
+
# @raise [SchemaError] If schema validation fails
|
82
|
+
# @return [void]
|
40
83
|
def validate!
|
41
84
|
errors = @root_namespace.validate_tag_references
|
42
85
|
raise SchemaError, "Schema validation failed: #{errors.join(", ")}" unless errors.empty?
|
43
86
|
end
|
44
87
|
|
45
|
-
# Helper class for building transformers
|
88
|
+
# Helper class for building transformers within the DSL
|
89
|
+
# @api private
|
46
90
|
class TransformerBuilder
|
91
|
+
# Initialize with the transformers hash
|
92
|
+
# @param transformers [Hash] Hash to store transformer definitions
|
47
93
|
def initialize(transformers)
|
48
94
|
@transformers = transformers
|
49
95
|
end
|
50
96
|
|
97
|
+
# Dynamic method to define transformers
|
98
|
+
# @param name [Symbol] Name of the transformer
|
99
|
+
# @param proc [Proc] Transformer procedure (alternative to block)
|
100
|
+
# @yield [Object] Value to transform
|
101
|
+
# @return [void]
|
51
102
|
def method_missing(name, proc = nil, &block)
|
52
103
|
@transformers[name.to_sym] = proc || block
|
53
104
|
end
|
54
105
|
|
106
|
+
# Always respond to any method for transformer definition
|
107
|
+
# @param _name [Symbol] Method name
|
108
|
+
# @param _include_private [Boolean] Whether to include private methods
|
109
|
+
# @return [Boolean] Always true
|
55
110
|
def respond_to_missing?(_name, _include_private = false)
|
56
111
|
true
|
57
112
|
end
|
58
113
|
end
|
59
114
|
|
60
|
-
# Helper class for building namespaces
|
115
|
+
# Helper class for building namespaces within the DSL
|
116
|
+
# @api private
|
61
117
|
class NamespaceBuilder
|
62
|
-
|
118
|
+
# Name of the namespace being built
|
119
|
+
# @return [Symbol] Namespace name
|
120
|
+
attr_reader :name
|
121
|
+
|
122
|
+
# Available transformers for tags
|
123
|
+
# @return [Hash<Symbol, Proc>] Transformer definitions
|
124
|
+
attr_reader :transformers
|
125
|
+
|
126
|
+
# Tag definitions for this namespace
|
127
|
+
# @return [Hash<Symbol, TagDefinition>] Tag definitions
|
128
|
+
attr_reader :tags
|
129
|
+
|
130
|
+
# Metric definitions for this namespace
|
131
|
+
# @return [Hash<Symbol, MetricDefinition>] Metric definitions
|
132
|
+
attr_reader :metrics
|
133
|
+
|
134
|
+
# Nested namespaces
|
135
|
+
# @return [Hash<Symbol, Namespace>] Nested namespace definitions
|
136
|
+
attr_reader :namespaces
|
137
|
+
|
138
|
+
# Description of this namespace
|
139
|
+
# @return [String, nil] Description text
|
140
|
+
attr_reader :description
|
63
141
|
|
142
|
+
# Initialize a new namespace builder
|
143
|
+
# @param name [Symbol] Name of the namespace
|
144
|
+
# @param transformers [Hash] Available transformer functions
|
64
145
|
def initialize(name, transformers = {})
|
65
146
|
@name = name.to_sym
|
66
147
|
@transformers = transformers
|
@@ -71,27 +152,48 @@ module Datadog
|
|
71
152
|
end
|
72
153
|
|
73
154
|
# Set description for this namespace
|
155
|
+
# @param desc [String] Description text
|
156
|
+
# @return [void]
|
74
157
|
def description(desc)
|
75
158
|
@description = desc
|
76
159
|
end
|
77
160
|
|
78
161
|
# Define tags for this namespace
|
162
|
+
# @yield [TagsBuilder] Block for defining tags
|
163
|
+
# @return [void]
|
164
|
+
# @example
|
165
|
+
# tags do
|
166
|
+
# tag :controller, values: %w[users posts]
|
167
|
+
# tag :action, values: %w[index show create]
|
168
|
+
# end
|
79
169
|
def tags(&)
|
80
170
|
TagsBuilder.new(@tags, @transformers).instance_eval(&)
|
81
171
|
end
|
82
172
|
|
83
173
|
# Define metrics for this namespace
|
174
|
+
# @yield [MetricsBuilder] Block for defining metrics
|
175
|
+
# @return [void]
|
176
|
+
# @example
|
177
|
+
# metrics do
|
178
|
+
# counter :page_views, tags: { required: [:controller] }
|
179
|
+
# gauge :memory_usage
|
180
|
+
# end
|
84
181
|
def metrics(&)
|
85
182
|
MetricsBuilder.new(@metrics, @transformers).instance_eval(&)
|
86
183
|
end
|
87
184
|
|
88
185
|
# Define nested namespace
|
186
|
+
# @param name [Symbol] Name of the nested namespace
|
187
|
+
# @yield [NamespaceBuilder] Block for defining nested namespace
|
188
|
+
# @return [void]
|
89
189
|
def namespace(name, &)
|
90
190
|
builder = NamespaceBuilder.new(name, @transformers)
|
91
191
|
builder.instance_eval(&) if block_given?
|
92
192
|
@namespaces[name.to_sym] = builder.build
|
93
193
|
end
|
94
194
|
|
195
|
+
# Build the namespace instance
|
196
|
+
# @return [Namespace] The constructed namespace
|
95
197
|
def build
|
96
198
|
Namespace.new(
|
97
199
|
name: @name,
|
@@ -104,12 +206,27 @@ module Datadog
|
|
104
206
|
end
|
105
207
|
|
106
208
|
# Helper class for building tags within a namespace
|
209
|
+
# @api private
|
107
210
|
class TagsBuilder
|
211
|
+
# Initialize with tags hash and transformers
|
212
|
+
# @param tags [Hash] Hash to store tag definitions
|
213
|
+
# @param transformers [Hash] Available transformer functions
|
108
214
|
def initialize(tags, transformers)
|
109
215
|
@tags = tags
|
110
216
|
@transformers = transformers
|
111
217
|
end
|
112
218
|
|
219
|
+
# Define a tag
|
220
|
+
# @param name [Symbol] Name of the tag
|
221
|
+
# @param options [Hash] Tag options
|
222
|
+
# @option options [Array, Regexp, Proc] :values Allowed values
|
223
|
+
# @option options [Symbol] :type Data type (:string, :integer, :symbol)
|
224
|
+
# @option options [Array<Symbol>] :transform Transformation functions to apply
|
225
|
+
# @option options [Proc] :validate Custom validation function
|
226
|
+
# @return [void]
|
227
|
+
# @example
|
228
|
+
# tag :controller, values: %w[users posts], type: :string
|
229
|
+
# tag :status_code, type: :integer, validate: ->(code) { (100..599).include?(code) }
|
113
230
|
def tag(name, **options)
|
114
231
|
tag_def = TagDefinition.new(
|
115
232
|
name: name.to_sym,
|
@@ -124,7 +241,11 @@ module Datadog
|
|
124
241
|
end
|
125
242
|
|
126
243
|
# Helper class for building metrics within a namespace
|
244
|
+
# @api private
|
127
245
|
class MetricsBuilder
|
246
|
+
# Initialize with metrics hash and transformers
|
247
|
+
# @param metrics [Hash] Hash to store metric definitions
|
248
|
+
# @param transformers [Hash] Available transformer functions
|
128
249
|
def initialize(metrics, transformers)
|
129
250
|
@metrics = metrics
|
130
251
|
@transformers = transformers
|
@@ -132,6 +253,9 @@ module Datadog
|
|
132
253
|
end
|
133
254
|
|
134
255
|
# Define a nested namespace for metrics
|
256
|
+
# @param name [Symbol] Namespace name
|
257
|
+
# @yield Block for defining metrics within the namespace
|
258
|
+
# @return [void]
|
135
259
|
def namespace(name, &)
|
136
260
|
@current_namespace = name.to_sym
|
137
261
|
instance_eval(&)
|
@@ -140,6 +264,15 @@ module Datadog
|
|
140
264
|
|
141
265
|
# Define individual metric types
|
142
266
|
%i[counter gauge histogram distribution timing set].each do |metric_type|
|
267
|
+
# Define a metric of the specified type
|
268
|
+
# @param name [Symbol] Metric name
|
269
|
+
# @param options [Hash] Metric options
|
270
|
+
# @option options [String] :description Human-readable description
|
271
|
+
# @option options [Hash, Array] :tags Tag configuration
|
272
|
+
# @option options [String] :inherit_tags Path to metric to inherit from
|
273
|
+
# @option options [String] :units Unit of measurement
|
274
|
+
# @yield [MetricBuilder] Block for additional metric configuration
|
275
|
+
# @return [void]
|
143
276
|
define_method(metric_type) do |name, **options, &block|
|
144
277
|
metric_name = @current_namespace ? :"#{@current_namespace}_#{name}" : name.to_sym
|
145
278
|
|
@@ -166,6 +299,9 @@ module Datadog
|
|
166
299
|
|
167
300
|
private
|
168
301
|
|
302
|
+
# Extract allowed tags from options
|
303
|
+
# @param options [Hash] Metric options
|
304
|
+
# @return [Array<Symbol>] Allowed tag names
|
169
305
|
def extract_allowed_tags(options)
|
170
306
|
tags_option = options[:tags]
|
171
307
|
return [] unless tags_option
|
@@ -177,6 +313,9 @@ module Datadog
|
|
177
313
|
end
|
178
314
|
end
|
179
315
|
|
316
|
+
# Extract required tags from options
|
317
|
+
# @param options [Hash] Metric options
|
318
|
+
# @return [Array<Symbol>] Required tag names
|
180
319
|
def extract_required_tags(options)
|
181
320
|
tags_option = options[:tags]
|
182
321
|
return [] unless tags_option
|
@@ -190,15 +329,26 @@ module Datadog
|
|
190
329
|
end
|
191
330
|
|
192
331
|
# Helper class for building individual metrics with block syntax
|
332
|
+
# @api private
|
193
333
|
class MetricBuilder
|
334
|
+
# Initialize with a metric definition
|
335
|
+
# @param metric_def [MetricDefinition] Initial metric definition
|
194
336
|
def initialize(metric_def)
|
195
337
|
@metric_def = metric_def
|
196
338
|
end
|
197
339
|
|
340
|
+
# Set metric description
|
341
|
+
# @param desc [String] Description text
|
342
|
+
# @return [void]
|
198
343
|
def description(desc)
|
199
344
|
@metric_def = @metric_def.new(description: desc)
|
200
345
|
end
|
201
346
|
|
347
|
+
# Configure metric tags
|
348
|
+
# @param options [Hash] Tag configuration
|
349
|
+
# @option options [Array] :allowed Allowed tag names
|
350
|
+
# @option options [Array] :required Required tag names
|
351
|
+
# @return [void]
|
202
352
|
def tags(**options)
|
203
353
|
allowed = Array(options[:allowed] || []).map(&:to_sym)
|
204
354
|
required = Array(options[:required] || []).map(&:to_sym)
|
@@ -209,14 +359,22 @@ module Datadog
|
|
209
359
|
)
|
210
360
|
end
|
211
361
|
|
362
|
+
# Set metric units
|
363
|
+
# @param unit_name [String] Unit description
|
364
|
+
# @return [void]
|
212
365
|
def units(unit_name)
|
213
366
|
@metric_def = @metric_def.new(units: unit_name)
|
214
367
|
end
|
215
368
|
|
369
|
+
# Set metric inheritance
|
370
|
+
# @param metric_path [String] Path to parent metric
|
371
|
+
# @return [void]
|
216
372
|
def inherit_tags(metric_path)
|
217
373
|
@metric_def = @metric_def.new(inherit_tags: metric_path)
|
218
374
|
end
|
219
375
|
|
376
|
+
# Build the final metric definition
|
377
|
+
# @return [MetricDefinition] The constructed metric definition
|
220
378
|
def build
|
221
379
|
@metric_def
|
222
380
|
end
|
@@ -3,23 +3,65 @@
|
|
3
3
|
require "dry-struct"
|
4
4
|
require "dry-types"
|
5
5
|
|
6
|
+
# @author Datadog Team
|
7
|
+
# @since 0.1.0
|
6
8
|
module Datadog
|
7
9
|
class Statsd
|
10
|
+
# Schema definition and validation module for StatsD metrics
|
8
11
|
module Schema
|
12
|
+
# Represents a tag definition within a schema namespace
|
13
|
+
# Defines validation rules, allowed values, transformations, and type constraints for tags
|
14
|
+
# @example Basic tag definition
|
15
|
+
# tag_def = TagDefinition.new(
|
16
|
+
# name: :environment,
|
17
|
+
# values: [:production, :staging, :development],
|
18
|
+
# type: :symbol
|
19
|
+
# )
|
20
|
+
# @example Tag with custom validation
|
21
|
+
# tag_def = TagDefinition.new(
|
22
|
+
# name: :user_id,
|
23
|
+
# type: :integer,
|
24
|
+
# validate: ->(value) { value > 0 }
|
25
|
+
# )
|
26
|
+
# @author Datadog Team
|
27
|
+
# @since 0.1.0
|
9
28
|
class TagDefinition < Dry::Struct
|
10
|
-
# Include the types module for easier access
|
29
|
+
# Include the types module for easier access to Dry::Types
|
11
30
|
module Types
|
12
31
|
include Dry.Types()
|
13
32
|
end
|
14
33
|
|
34
|
+
# The tag name as a symbol
|
35
|
+
# @return [Symbol] Tag name
|
15
36
|
attribute :name, Types::Strict::Symbol
|
16
|
-
|
37
|
+
|
38
|
+
# Allowed values for this tag (can be Array, Regexp, Proc, or single value)
|
39
|
+
# @return [Array, Regexp, Proc, Object, nil] Allowed values constraint
|
40
|
+
attribute :values, Types::Any.optional.default(nil)
|
41
|
+
|
42
|
+
# The expected data type for tag values
|
43
|
+
# @return [Symbol] Type constraint (:string, :integer, :symbol)
|
17
44
|
attribute :type, Types::Strict::Symbol.default(:string)
|
45
|
+
|
46
|
+
# Array of transformation functions to apply to values
|
47
|
+
# @return [Array<Symbol>] Transformation function names
|
18
48
|
attribute :transform, Types::Array.of(Types::Symbol).default([].freeze)
|
19
|
-
|
49
|
+
|
50
|
+
# Custom validation procedure for tag values
|
51
|
+
# @return [Proc, nil] Custom validation function
|
52
|
+
attribute :validate, Types::Any.optional.default(nil)
|
53
|
+
|
54
|
+
# The namespace this tag belongs to
|
55
|
+
# @return [Symbol, nil] Namespace name
|
20
56
|
attribute :namespace, Types::Strict::Symbol.optional.default(nil)
|
21
57
|
|
22
|
-
# Check if a value is allowed for this tag
|
58
|
+
# Check if a value is allowed for this tag according to the values constraint
|
59
|
+
# @param value [Object] The value to check
|
60
|
+
# @return [Boolean] true if the value is allowed
|
61
|
+
# @example
|
62
|
+
# tag_def = TagDefinition.new(name: :env, values: [:prod, :dev])
|
63
|
+
# tag_def.allows_value?(:prod) # => true
|
64
|
+
# tag_def.allows_value?(:test) # => false
|
23
65
|
def allows_value?(value)
|
24
66
|
return true if values.nil? # No restrictions
|
25
67
|
|
@@ -35,7 +77,14 @@ module Datadog
|
|
35
77
|
end
|
36
78
|
end
|
37
79
|
|
38
|
-
# Apply transformations to a value
|
80
|
+
# Apply transformations to a value using the provided transformer functions
|
81
|
+
# @param value [Object] The value to transform
|
82
|
+
# @param transformers [Hash<Symbol, Proc>] Hash of transformer name to proc mappings
|
83
|
+
# @return [Object] The transformed value
|
84
|
+
# @example
|
85
|
+
# tag_def = TagDefinition.new(name: :service, transform: [:downcase])
|
86
|
+
# transformers = { downcase: ->(val) { val.to_s.downcase } }
|
87
|
+
# tag_def.transform_value("WEB-SERVICE", transformers) # => "web-service"
|
39
88
|
def transform_value(value, transformers = {})
|
40
89
|
return value if transform.empty?
|
41
90
|
|
@@ -45,7 +94,18 @@ module Datadog
|
|
45
94
|
end
|
46
95
|
end
|
47
96
|
|
48
|
-
# Validate a value using custom validation
|
97
|
+
# Validate a value using type checking, transformations, and custom validation
|
98
|
+
# @param value [Object] The value to validate
|
99
|
+
# @param transformers [Hash<Symbol, Proc>] Hash of transformer name to proc mappings
|
100
|
+
# @return [Boolean] true if the value is valid
|
101
|
+
# @example
|
102
|
+
# tag_def = TagDefinition.new(
|
103
|
+
# name: :port,
|
104
|
+
# type: :integer,
|
105
|
+
# validate: ->(val) { val > 0 && val < 65536 }
|
106
|
+
# )
|
107
|
+
# tag_def.valid_value?(8080) # => true
|
108
|
+
# tag_def.valid_value?(-1) # => false
|
49
109
|
def valid_value?(value, transformers = {})
|
50
110
|
transformed_value = transform_value(value, transformers)
|
51
111
|
|
@@ -1,9 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# @author Datadog Team
|
4
|
+
# @since 0.1.0
|
3
5
|
module Datadog
|
4
6
|
class Statsd
|
7
|
+
# Schema definition and validation module for StatsD metrics
|
5
8
|
module Schema
|
6
|
-
|
9
|
+
# Current version of the datadog-statsd-schema gem
|
10
|
+
# @return [String] The semantic version string
|
11
|
+
VERSION = "0.1.2"
|
7
12
|
end
|
8
13
|
end
|
9
14
|
end
|