Skip to content

Commit bc7d967

Browse files
authored
[connector/routing] Add ability to route by datapoint context (#36523)
1 parent 4b45285 commit bc7d967

File tree

11 files changed

+2455
-222
lines changed

11 files changed

+2455
-222
lines changed
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: routingconnector
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add abiilty to route by 'datapoint' context
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [36523]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

connector/routingconnector/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ If you are not already familiar with connectors, you may find it helpful to firs
3333
The following settings are available:
3434

3535
- `table (required)`: the routing table for this connector.
36-
- `table.context (optional, default: resource)`: the [OTTL Context] in which the statement will be evaluated. Currently, only `resource`, `span`, `metric`, `log`, and `request` are supported.
36+
- `table.context (optional, default: resource)`: the [OTTL Context] in which the statement will be evaluated. Currently, only `resource`, `span`, `metric`, `datapoint`, `log`, and `request` are supported.
3737
- `table.statement`: the routing condition provided as the [OTTL] statement. Required if `table.condition` is not provided. May not be used for `request` context.
3838
- `table.condition`: the routing condition provided as the [OTTL] condition. Required if `table.statement` is not provided. Required for `request` context.
3939
- `table.pipelines (required)`: the list of pipelines to use when the routing condition is met.
@@ -43,7 +43,7 @@ The following settings are available:
4343

4444
### Limitations
4545

46-
- The `match_once` setting is only supported when using the `resource` context. If any routes use `span`, `metric`, `log` or `request` context, `match_once` must be set to `true`.
46+
- The `match_once` setting is only supported when using the `resource` context. If any routes use `span`, `metric`, `datapoint`, `log` or `request` context, `match_once` must be set to `true`.
4747
- The `request` context requires use of the `condition` setting, and relies on a very limited grammar. Conditions must be in the form of `request["key"] == "value"` or `request["key"] != "value"`. (In the future, this grammar may be expanded to support more complex conditions.)
4848

4949
### Supported [OTTL] functions

connector/routingconnector/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func (c *Config) Validate() error {
7777
return err
7878
}
7979
fallthrough
80-
case "span", "metric", "log": // ok
80+
case "span", "metric", "datapoint", "log": // ok
8181
if !c.MatchOnce {
8282
return fmt.Errorf(`%q context is not supported with "match_once: false"`, item.Context)
8383
}

connector/routingconnector/config_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,22 @@ func TestValidateConfig(t *testing.T) {
250250
},
251251
error: `"metric" context is not supported with "match_once: false"`,
252252
},
253+
{
254+
name: "datapoint context with match_once false",
255+
config: &Config{
256+
MatchOnce: false,
257+
Table: []RoutingTableItem{
258+
{
259+
Context: "datapoint",
260+
Statement: `route() where attributes["attr"] == "acme"`,
261+
Pipelines: []pipeline.ID{
262+
pipeline.NewIDWithName(pipeline.SignalTraces, "otlp"),
263+
},
264+
},
265+
},
266+
},
267+
error: `"datapoint" context is not supported with "match_once: false"`,
268+
},
253269
{
254270
name: "log context with match_once false",
255271
config: &Config{

connector/routingconnector/internal/pmetricutil/metrics.go

+193
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,196 @@ func MoveMetricsWithContextIf(from, to pmetric.Metrics, f func(pmetric.ResourceM
5959
return rm.ScopeMetrics().Len() == 0
6060
})
6161
}
62+
63+
// MoveDataPointsWithContextIf calls f sequentially for each DataPoint present in the first pmetric.Metrics.
64+
// If f returns true, the element is removed from the first pmetric.Metrics and added to the second pmetric.Metrics.
65+
// Notably, the Resource, Scope, and Metric associated with the DataPoint are created in the second pmetric.Metrics only once.
66+
// Resources, Scopes, or Metrics are removed from the original if they become empty. All ordering is preserved.
67+
func MoveDataPointsWithContextIf(from, to pmetric.Metrics, f func(pmetric.ResourceMetrics, pmetric.ScopeMetrics, pmetric.Metric, any) bool) {
68+
rms := from.ResourceMetrics()
69+
for i := 0; i < rms.Len(); i++ {
70+
rm := rms.At(i)
71+
sms := rm.ScopeMetrics()
72+
var rmCopy *pmetric.ResourceMetrics
73+
for j := 0; j < sms.Len(); j++ {
74+
sm := sms.At(j)
75+
ms := sm.Metrics()
76+
var smCopy *pmetric.ScopeMetrics
77+
for k := 0; k < ms.Len(); k++ {
78+
m := ms.At(k)
79+
var mCopy *pmetric.Metric
80+
81+
// TODO condense this code
82+
switch m.Type() {
83+
case pmetric.MetricTypeGauge:
84+
dps := m.Gauge().DataPoints()
85+
dps.RemoveIf(func(dp pmetric.NumberDataPoint) bool {
86+
if !f(rm, sm, m, dp) {
87+
return false
88+
}
89+
if rmCopy == nil {
90+
rmc := to.ResourceMetrics().AppendEmpty()
91+
rmCopy = &rmc
92+
rm.Resource().CopyTo(rmCopy.Resource())
93+
rmCopy.SetSchemaUrl(rm.SchemaUrl())
94+
}
95+
if smCopy == nil {
96+
smc := rmCopy.ScopeMetrics().AppendEmpty()
97+
smCopy = &smc
98+
sm.Scope().CopyTo(smCopy.Scope())
99+
smCopy.SetSchemaUrl(sm.SchemaUrl())
100+
}
101+
if mCopy == nil {
102+
mc := smCopy.Metrics().AppendEmpty()
103+
mCopy = &mc
104+
mCopy.SetName(m.Name())
105+
mCopy.SetDescription(m.Description())
106+
mCopy.SetUnit(m.Unit())
107+
mCopy.SetEmptyGauge()
108+
}
109+
dp.CopyTo(mCopy.Gauge().DataPoints().AppendEmpty())
110+
return true
111+
})
112+
case pmetric.MetricTypeSum:
113+
dps := m.Sum().DataPoints()
114+
dps.RemoveIf(func(dp pmetric.NumberDataPoint) bool {
115+
if !f(rm, sm, m, dp) {
116+
return false
117+
}
118+
if rmCopy == nil {
119+
rmc := to.ResourceMetrics().AppendEmpty()
120+
rmCopy = &rmc
121+
rm.Resource().CopyTo(rmCopy.Resource())
122+
rmCopy.SetSchemaUrl(rm.SchemaUrl())
123+
}
124+
if smCopy == nil {
125+
smc := rmCopy.ScopeMetrics().AppendEmpty()
126+
smCopy = &smc
127+
sm.Scope().CopyTo(smCopy.Scope())
128+
smCopy.SetSchemaUrl(sm.SchemaUrl())
129+
}
130+
if mCopy == nil {
131+
mc := smCopy.Metrics().AppendEmpty()
132+
mCopy = &mc
133+
mCopy.SetName(m.Name())
134+
mCopy.SetDescription(m.Description())
135+
mCopy.SetUnit(m.Unit())
136+
mCopy.SetEmptySum()
137+
}
138+
dp.CopyTo(mCopy.Sum().DataPoints().AppendEmpty())
139+
return true
140+
})
141+
case pmetric.MetricTypeHistogram:
142+
dps := m.Histogram().DataPoints()
143+
dps.RemoveIf(func(dp pmetric.HistogramDataPoint) bool {
144+
if !f(rm, sm, m, dp) {
145+
return false
146+
}
147+
if rmCopy == nil {
148+
rmc := to.ResourceMetrics().AppendEmpty()
149+
rmCopy = &rmc
150+
rm.Resource().CopyTo(rmCopy.Resource())
151+
rmCopy.SetSchemaUrl(rm.SchemaUrl())
152+
}
153+
if smCopy == nil {
154+
smc := rmCopy.ScopeMetrics().AppendEmpty()
155+
smCopy = &smc
156+
sm.Scope().CopyTo(smCopy.Scope())
157+
smCopy.SetSchemaUrl(sm.SchemaUrl())
158+
}
159+
if mCopy == nil {
160+
mc := smCopy.Metrics().AppendEmpty()
161+
mCopy = &mc
162+
mCopy.SetName(m.Name())
163+
mCopy.SetDescription(m.Description())
164+
mCopy.SetUnit(m.Unit())
165+
mCopy.SetEmptyHistogram()
166+
}
167+
dp.CopyTo(mCopy.Histogram().DataPoints().AppendEmpty())
168+
return true
169+
})
170+
case pmetric.MetricTypeExponentialHistogram:
171+
dps := m.ExponentialHistogram().DataPoints()
172+
dps.RemoveIf(func(dp pmetric.ExponentialHistogramDataPoint) bool {
173+
if !f(rm, sm, m, dp) {
174+
return false
175+
}
176+
if rmCopy == nil {
177+
rmc := to.ResourceMetrics().AppendEmpty()
178+
rmCopy = &rmc
179+
rm.Resource().CopyTo(rmCopy.Resource())
180+
rmCopy.SetSchemaUrl(rm.SchemaUrl())
181+
}
182+
if smCopy == nil {
183+
smc := rmCopy.ScopeMetrics().AppendEmpty()
184+
smCopy = &smc
185+
sm.Scope().CopyTo(smCopy.Scope())
186+
smCopy.SetSchemaUrl(sm.SchemaUrl())
187+
}
188+
if mCopy == nil {
189+
mc := smCopy.Metrics().AppendEmpty()
190+
mCopy = &mc
191+
mCopy.SetName(m.Name())
192+
mCopy.SetDescription(m.Description())
193+
mCopy.SetUnit(m.Unit())
194+
mCopy.SetEmptyExponentialHistogram()
195+
}
196+
dp.CopyTo(mCopy.ExponentialHistogram().DataPoints().AppendEmpty())
197+
return true
198+
})
199+
case pmetric.MetricTypeSummary:
200+
dps := m.Summary().DataPoints()
201+
dps.RemoveIf(func(dp pmetric.SummaryDataPoint) bool {
202+
if !f(rm, sm, m, dp) {
203+
return false
204+
}
205+
if rmCopy == nil {
206+
rmc := to.ResourceMetrics().AppendEmpty()
207+
rmCopy = &rmc
208+
rm.Resource().CopyTo(rmCopy.Resource())
209+
rmCopy.SetSchemaUrl(rm.SchemaUrl())
210+
}
211+
if smCopy == nil {
212+
smc := rmCopy.ScopeMetrics().AppendEmpty()
213+
smCopy = &smc
214+
sm.Scope().CopyTo(smCopy.Scope())
215+
smCopy.SetSchemaUrl(sm.SchemaUrl())
216+
}
217+
if mCopy == nil {
218+
mc := smCopy.Metrics().AppendEmpty()
219+
mCopy = &mc
220+
mCopy.SetName(m.Name())
221+
mCopy.SetDescription(m.Description())
222+
mCopy.SetUnit(m.Unit())
223+
mCopy.SetEmptySummary()
224+
}
225+
dp.CopyTo(mCopy.Summary().DataPoints().AppendEmpty())
226+
return true
227+
})
228+
}
229+
}
230+
ms.RemoveIf(func(m pmetric.Metric) bool {
231+
var numDPs int
232+
switch m.Type() {
233+
case pmetric.MetricTypeGauge:
234+
numDPs = m.Gauge().DataPoints().Len()
235+
case pmetric.MetricTypeSum:
236+
numDPs = m.Sum().DataPoints().Len()
237+
case pmetric.MetricTypeHistogram:
238+
numDPs = m.Histogram().DataPoints().Len()
239+
case pmetric.MetricTypeExponentialHistogram:
240+
numDPs = m.ExponentialHistogram().DataPoints().Len()
241+
case pmetric.MetricTypeSummary:
242+
numDPs = m.Summary().DataPoints().Len()
243+
}
244+
return numDPs == 0
245+
})
246+
}
247+
sms.RemoveIf(func(sm pmetric.ScopeMetrics) bool {
248+
return sm.Metrics().Len() == 0
249+
})
250+
}
251+
rms.RemoveIf(func(rm pmetric.ResourceMetrics) bool {
252+
return rm.ScopeMetrics().Len() == 0
253+
})
254+
}

0 commit comments

Comments
 (0)