Skip to content

Commit a279256

Browse files
committed
feat(sdk-metrics)!: replace attributeKeys with an option to add custom processors
1 parent c379326 commit a279256

12 files changed

+215
-66
lines changed

CHANGELOG_NEXT.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* chore(otel-core): replace deprecated SpanAttributes [#4408](https://github.com/open-telemetry/opentelemetry-js/pull/4408) @JamieDanielson
77
* feat(sdk-metrics)!: remove MeterProvider.addMetricReader() in favor of constructor option [#4419](https://github.com/open-telemetry/opentelemetry-js/pull/4419) @pichlermarc
88
* chore(otel-resources): replace deprecated SpanAttributes [#4428](https://github.com/open-telemetry/opentelemetry-js/pull/4428) @JamieDanielson
9+
* feat(sdk-metrics)!: remove MeterProvider.addMetricReader() in favor of constructor option [#4419](https://github.com/open-telemetry/opentelemetry-js/pull/4419) @pichlermarc
10+
* feat(sdk-metrics)!: replace attributeKeys with custom processors option [#4532](https://github.com/open-telemetry/opentelemetry-js/pull/4532) @pichlermarc
911

1012
### :rocket: (Enhancement)
1113

packages/sdk-metrics/src/index.ts

+6
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,10 @@ export {
8080

8181
export { View, ViewOptions } from './view/View';
8282

83+
export {
84+
IAttributesProcessor,
85+
createAllowListAttributesProcessor,
86+
createDenyListAttributesProcessor,
87+
} from './view/AttributesProcessor';
88+
8389
export { TimeoutError } from './utils';

packages/sdk-metrics/src/state/AsyncMetricStorage.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import { HrTime } from '@opentelemetry/api';
1818
import { Accumulation, Aggregator } from '../aggregator/types';
1919
import { InstrumentDescriptor } from '../InstrumentDescriptor';
20-
import { AttributesProcessor } from '../view/AttributesProcessor';
2120
import { MetricStorage } from './MetricStorage';
2221
import { MetricData } from '../export/MetricData';
2322
import { DeltaMetricProcessor } from './DeltaMetricProcessor';
@@ -26,6 +25,7 @@ import { Maybe } from '../utils';
2625
import { MetricCollectorHandle } from './MetricCollector';
2726
import { AttributeHashMap } from './HashMap';
2827
import { AsyncWritableMetricStorage } from './WritableMetricStorage';
28+
import { IAttributesProcessor } from '../view/AttributesProcessor';
2929

3030
/**
3131
* Internal interface.
@@ -42,7 +42,7 @@ export class AsyncMetricStorage<T extends Maybe<Accumulation>>
4242
constructor(
4343
_instrumentDescriptor: InstrumentDescriptor,
4444
aggregator: Aggregator<T>,
45-
private _attributesProcessor: AttributesProcessor,
45+
private _attributesProcessor: IAttributesProcessor,
4646
collectorHandles: MetricCollectorHandle[]
4747
) {
4848
super(_instrumentDescriptor);

packages/sdk-metrics/src/state/MeterSharedState.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ import { MultiMetricStorage } from './MultiWritableMetricStorage';
3232
import { ObservableRegistry } from './ObservableRegistry';
3333
import { SyncMetricStorage } from './SyncMetricStorage';
3434
import { Accumulation, Aggregator } from '../aggregator/types';
35-
import { AttributesProcessor } from '../view/AttributesProcessor';
35+
import {
36+
createNoopAttributesProcessor,
37+
IAttributesProcessor,
38+
} from '../view/AttributesProcessor';
3639
import { MetricStorage } from './MetricStorage';
3740

3841
/**
@@ -166,7 +169,7 @@ export class MeterSharedState {
166169
const storage = new MetricStorageType(
167170
descriptor,
168171
aggregator,
169-
AttributesProcessor.Noop(),
172+
createNoopAttributesProcessor(),
170173
[collector]
171174
) as R;
172175
this.metricStorageRegistry.registerForCollector(collector, storage);
@@ -189,7 +192,7 @@ interface MetricStorageConstructor {
189192
new (
190193
instrumentDescriptor: InstrumentDescriptor,
191194
aggregator: Aggregator<Maybe<Accumulation>>,
192-
attributesProcessor: AttributesProcessor,
195+
attributesProcessor: IAttributesProcessor,
193196
collectors: MetricCollectorHandle[]
194197
): MetricStorage;
195198
}

packages/sdk-metrics/src/state/SyncMetricStorage.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { Context, HrTime, MetricAttributes } from '@opentelemetry/api';
1818
import { WritableMetricStorage } from './WritableMetricStorage';
1919
import { Accumulation, Aggregator } from '../aggregator/types';
2020
import { InstrumentDescriptor } from '../InstrumentDescriptor';
21-
import { AttributesProcessor } from '../view/AttributesProcessor';
21+
import { IAttributesProcessor } from '../view/AttributesProcessor';
2222
import { MetricStorage } from './MetricStorage';
2323
import { MetricData } from '../export/MetricData';
2424
import { DeltaMetricProcessor } from './DeltaMetricProcessor';
@@ -41,7 +41,7 @@ export class SyncMetricStorage<T extends Maybe<Accumulation>>
4141
constructor(
4242
instrumentDescriptor: InstrumentDescriptor,
4343
aggregator: Aggregator<T>,
44-
private _attributesProcessor: AttributesProcessor,
44+
private _attributesProcessor: IAttributesProcessor,
4545
collectorHandles: MetricCollectorHandle[]
4646
) {
4747
super(instrumentDescriptor);

packages/sdk-metrics/src/view/AttributesProcessor.ts

+77-21
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,46 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Context, MetricAttributes } from '@opentelemetry/api';
17+
import { Context, Attributes } from '@opentelemetry/api';
1818

1919
/**
2020
* The {@link AttributesProcessor} is responsible for customizing which
2121
* attribute(s) are to be reported as metrics dimension(s) and adding
2222
* additional dimension(s) from the {@link Context}.
2323
*/
24-
export abstract class AttributesProcessor {
24+
export interface IAttributesProcessor {
2525
/**
2626
* Process the metric instrument attributes.
2727
*
2828
* @param incoming The metric instrument attributes.
2929
* @param context The active context when the instrument is synchronous.
3030
* `undefined` otherwise.
3131
*/
32-
abstract process(
33-
incoming: MetricAttributes,
34-
context?: Context
35-
): MetricAttributes;
36-
37-
static Noop() {
38-
return NOOP;
39-
}
32+
process: (incoming: Attributes, context?: Context) => Attributes;
4033
}
4134

42-
export class NoopAttributesProcessor extends AttributesProcessor {
43-
process(incoming: MetricAttributes, _context?: Context) {
35+
class NoopAttributesProcessor implements IAttributesProcessor {
36+
process(incoming: Attributes, _context?: Context) {
4437
return incoming;
4538
}
4639
}
4740

48-
/**
49-
* {@link AttributesProcessor} that filters by allowed attribute names and drops any names that are not in the
50-
* allow list.
51-
*/
52-
export class FilteringAttributesProcessor extends AttributesProcessor {
53-
constructor(private _allowedAttributeNames: string[]) {
54-
super();
41+
class MultiAttributesProcessor implements IAttributesProcessor {
42+
constructor(private readonly _processors: IAttributesProcessor[]) {}
43+
process(incoming: Attributes, context?: Context): Attributes {
44+
let filteredAttributes = incoming;
45+
for (const processor of this._processors) {
46+
filteredAttributes = processor.process(filteredAttributes, context);
47+
}
48+
return filteredAttributes;
5549
}
50+
}
51+
52+
class AllowListProcessor implements IAttributesProcessor {
53+
constructor(private _allowedAttributeNames: string[]) {}
5654

57-
process(incoming: MetricAttributes, _context: Context): MetricAttributes {
58-
const filteredAttributes: MetricAttributes = {};
55+
process(incoming: Attributes, _context?: Context): Attributes {
56+
const filteredAttributes: Attributes = {};
5957
Object.keys(incoming)
6058
.filter(attributeName =>
6159
this._allowedAttributeNames.includes(attributeName)
@@ -68,4 +66,62 @@ export class FilteringAttributesProcessor extends AttributesProcessor {
6866
}
6967
}
7068

69+
class DenyListProcessor implements IAttributesProcessor {
70+
constructor(private _deniedAttributeNames: string[]) {}
71+
72+
process(incoming: Attributes, _context?: Context): Attributes {
73+
const filteredAttributes: Attributes = {};
74+
Object.keys(incoming)
75+
.filter(
76+
attributeName => !this._deniedAttributeNames.includes(attributeName)
77+
)
78+
.forEach(
79+
attributeName =>
80+
(filteredAttributes[attributeName] = incoming[attributeName])
81+
);
82+
return filteredAttributes;
83+
}
84+
}
85+
86+
/**
87+
* @internal
88+
*
89+
* Create an {@link IAttributesProcessor} that acts as a simple pass-through for attributes.
90+
*/
91+
export function createNoopAttributesProcessor(): IAttributesProcessor {
92+
return NOOP;
93+
}
94+
95+
/**
96+
* @internal
97+
*
98+
* Create an {@link IAttributesProcessor} that applies all processors from the provided list in order.
99+
*
100+
* @param processors Processors to apply in order.
101+
*/
102+
export function createMultiAttributesProcessor(
103+
processors: IAttributesProcessor[]
104+
): IAttributesProcessor {
105+
return new MultiAttributesProcessor(processors);
106+
}
107+
108+
/**
109+
* Create an {@link IAttributesProcessor} that filters by allowed attribute names and drops any names that are not in the
110+
* allow list.
111+
*/
112+
export function createAllowListAttributesProcessor(
113+
attributeAllowList: string[]
114+
): IAttributesProcessor {
115+
return new AllowListProcessor(attributeAllowList);
116+
}
117+
118+
/**
119+
* Create an {@link IAttributesProcessor} that drops attributes based on the names provided in the deny list
120+
*/
121+
export function createDenyListAttributesProcessor(
122+
attributeDenyList: string[]
123+
): IAttributesProcessor {
124+
return new DenyListProcessor(attributeDenyList);
125+
}
126+
71127
const NOOP = new NoopAttributesProcessor();

packages/sdk-metrics/src/view/View.ts

+18-14
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
import { PatternPredicate } from './Predicate';
1818
import {
19-
AttributesProcessor,
20-
FilteringAttributesProcessor,
19+
createMultiAttributesProcessor,
20+
createNoopAttributesProcessor,
21+
IAttributesProcessor,
2122
} from './AttributesProcessor';
2223
import { InstrumentSelector } from './InstrumentSelector';
2324
import { MeterSelector } from './MeterSelector';
@@ -42,15 +43,18 @@ export type ViewOptions = {
4243
description?: string;
4344
/**
4445
* Alters the metric stream:
45-
* If provided, the attributes that are not in the list will be ignored.
46+
* If provided, the attributes will be modified as defined by the processors in the list. Processors are applied
47+
* in the order they're provided.
4648
* If not provided, all attribute keys will be used by default.
4749
*
4850
* @example <caption>drops all attributes with top-level keys except for 'myAttr' and 'myOtherAttr'</caption>
49-
* attributeKeys: ['myAttr', 'myOtherAttr']
51+
* attributesProcessors: [createAllowListProcessor(['myAttr', 'myOtherAttr'])]
5052
* @example <caption>drops all attributes</caption>
51-
* attributeKeys: []
53+
* attributesProcessors: [createAllowListProcessor([])]
54+
* @example <caption>allows all attributes except for 'myAttr'</caption>
55+
* attributesProcessors: [createDenyListProcessor(['myAttr']]
5256
*/
53-
attributeKeys?: string[];
57+
attributesProcessors?: IAttributesProcessor[];
5458
/**
5559
* Alters the metric stream:
5660
* Alters the {@link Aggregation} of the metric stream.
@@ -135,7 +139,7 @@ export class View {
135139
readonly name?: string;
136140
readonly description?: string;
137141
readonly aggregation: Aggregation;
138-
readonly attributesProcessor: AttributesProcessor;
142+
readonly attributesProcessor: IAttributesProcessor;
139143
readonly instrumentSelector: InstrumentSelector;
140144
readonly meterSelector: MeterSelector;
141145

@@ -157,9 +161,9 @@ export class View {
157161
* Alters the metric stream:
158162
* This will be used as the description of the metrics stream.
159163
* If not provided, the original Instrument description will be used by default.
160-
* @param viewOptions.attributeKeys
164+
* @param viewOptions.attributesProcessors
161165
* Alters the metric stream:
162-
* If provided, the attributes that are not in the list will be ignored.
166+
* If provided, the attributes will be modified as defined by the added processors.
163167
* If not provided, all attribute keys will be used by default.
164168
* @param viewOptions.aggregation
165169
* Alters the metric stream:
@@ -210,13 +214,13 @@ export class View {
210214
);
211215
}
212216

213-
// Create AttributesProcessor if attributeKeys are defined set.
214-
if (viewOptions.attributeKeys != null) {
215-
this.attributesProcessor = new FilteringAttributesProcessor(
216-
viewOptions.attributeKeys
217+
// Create multi-processor if attributesProcessors are defined.
218+
if (viewOptions.attributesProcessors != null) {
219+
this.attributesProcessor = createMultiAttributesProcessor(
220+
viewOptions.attributesProcessors
217221
);
218222
} else {
219-
this.attributesProcessor = AttributesProcessor.Noop();
223+
this.attributesProcessor = createNoopAttributesProcessor();
220224
}
221225

222226
this.name = viewOptions.name;

packages/sdk-metrics/test/MeterProvider.test.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { TestMetricReader } from './export/TestMetricReader';
3232
import * as sinon from 'sinon';
3333
import { View } from '../src/view/View';
3434
import { Meter } from '../src/Meter';
35+
import { createAllowListAttributesProcessor } from '../src/view/AttributesProcessor';
3536

3637
describe('MeterProvider', () => {
3738
afterEach(() => {
@@ -200,15 +201,17 @@ describe('MeterProvider', () => {
200201
);
201202
});
202203

203-
it('with attributeKeys should drop non-listed attributes', async () => {
204+
it('with allowListProcessor should drop non-listed attributes', async () => {
204205
const reader = new TestMetricReader();
205206

206207
// Add view to drop all attributes except 'attrib1'
207208
const meterProvider = new MeterProvider({
208209
resource: defaultResource,
209210
views: [
210211
new View({
211-
attributeKeys: ['attrib1'],
212+
attributesProcessors: [
213+
createAllowListAttributesProcessor(['attrib1']),
214+
],
212215
instrumentName: 'non-renamed-instrument',
213216
}),
214217
],

packages/sdk-metrics/test/state/AsyncMetricStorage.test.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { AggregationTemporality } from '../../src/export/AggregationTemporality'
2121
import { DataPointType } from '../../src/export/MetricData';
2222
import { MetricCollectorHandle } from '../../src/state/MetricCollector';
2323
import { AsyncMetricStorage } from '../../src/state/AsyncMetricStorage';
24-
import { NoopAttributesProcessor } from '../../src/view/AttributesProcessor';
24+
import { createNoopAttributesProcessor } from '../../src/view/AttributesProcessor';
2525
import { ObservableRegistry } from '../../src/state/ObservableRegistry';
2626
import {
2727
assertMetricData,
@@ -49,7 +49,7 @@ describe('AsyncMetricStorage', () => {
4949
const metricStorage = new AsyncMetricStorage(
5050
defaultInstrumentDescriptor,
5151
new SumAggregator(true),
52-
new NoopAttributesProcessor(),
52+
createNoopAttributesProcessor(),
5353
[deltaCollector]
5454
);
5555

@@ -149,7 +149,7 @@ describe('AsyncMetricStorage', () => {
149149
const metricStorage = new AsyncMetricStorage(
150150
defaultInstrumentDescriptor,
151151
new SumAggregator(true),
152-
new NoopAttributesProcessor(),
152+
createNoopAttributesProcessor(),
153153
[deltaCollector]
154154
);
155155

@@ -233,7 +233,7 @@ describe('AsyncMetricStorage', () => {
233233
const metricStorage = new AsyncMetricStorage(
234234
defaultInstrumentDescriptor,
235235
new SumAggregator(false),
236-
new NoopAttributesProcessor(),
236+
createNoopAttributesProcessor(),
237237
[deltaCollector]
238238
);
239239

@@ -319,7 +319,7 @@ describe('AsyncMetricStorage', () => {
319319
const metricStorage = new AsyncMetricStorage(
320320
defaultInstrumentDescriptor,
321321
new SumAggregator(true),
322-
new NoopAttributesProcessor(),
322+
createNoopAttributesProcessor(),
323323
[cumulativeCollector]
324324
);
325325

@@ -451,7 +451,7 @@ describe('AsyncMetricStorage', () => {
451451
const metricStorage = new AsyncMetricStorage(
452452
defaultInstrumentDescriptor,
453453
new SumAggregator(true),
454-
new NoopAttributesProcessor(),
454+
createNoopAttributesProcessor(),
455455
[cumulativeCollector]
456456
);
457457

@@ -545,7 +545,7 @@ describe('AsyncMetricStorage', () => {
545545
const metricStorage = new AsyncMetricStorage(
546546
defaultInstrumentDescriptor,
547547
new SumAggregator(false),
548-
new NoopAttributesProcessor(),
548+
createNoopAttributesProcessor(),
549549
[cumulativeCollector]
550550
);
551551

0 commit comments

Comments
 (0)