Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2
### :house: Internal

* perf(sdk-metrics): defer allocation of HrTime to accumulation creation [#6839](https://github.com/open-telemetry/opentelemetry-js/pull/6839) @legendecas
* perf(sdk-metrics): optionally capture active context for sync instruments [#6848](https://github.com/open-telemetry/opentelemetry-js/pull/6848) @legendecas

## 2.8.0

Expand Down
4 changes: 2 additions & 2 deletions packages/sdk-metrics/src/Instruments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
ObservableGauge,
ObservableUpDownCounter,
} from '@opentelemetry/api';
import { context as contextApi, diag, ValueType } from '@opentelemetry/api';
import { diag, ValueType } from '@opentelemetry/api';
import type { InstrumentDescriptor } from './InstrumentDescriptor';
import type { ObservableRegistry } from './state/ObservableRegistry';
import type {
Expand All @@ -39,7 +39,7 @@ export class SyncInstrument {
protected _record(
value: number,
attributes: Attributes = {},
context: Context = contextApi.active()
context?: Context
) {
if (typeof value !== 'number') {
diag.warn(
Expand Down
8 changes: 6 additions & 2 deletions packages/sdk-metrics/src/state/AsyncMetricStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export class AsyncMetricStorage<T extends Maybe<Accumulation>>
private _aggregationCardinalityLimit?: number;
private _deltaMetricStorage: DeltaMetricProcessor<T>;
private _temporalMetricStorage: TemporalMetricProcessor<T>;
private _attributesProcessor: IAttributesProcessor;
private _attributesProcessor?: IAttributesProcessor;

constructor(
_instrumentDescriptor: InstrumentDescriptor,
aggregator: Aggregator<T>,
attributesProcessor: IAttributesProcessor,
attributesProcessor: IAttributesProcessor | undefined,
collectorHandles: MetricCollectorHandle[],
aggregationCardinalityLimit?: number
) {
Expand All @@ -51,6 +51,10 @@ export class AsyncMetricStorage<T extends Maybe<Accumulation>>
}

record(measurements: AttributeHashMap<number>, observationTime: HrTime) {
if (this._attributesProcessor === undefined) {
this._deltaMetricStorage.batchCumulate(measurements, observationTime);
return;
}
const processed = new AttributeHashMap<number>();
for (const [attributes, value] of measurements.entries()) {
processed.set(this._attributesProcessor.process(attributes), value);
Expand Down
9 changes: 2 additions & 7 deletions packages/sdk-metrics/src/state/DeltaMetricProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import type { Context, HrTime, Attributes } from '@opentelemetry/api';
import type { HrTime, Attributes } from '@opentelemetry/api';
import { millisToHrTime } from '@opentelemetry/core';
import type { Maybe } from '../utils';
import { hashAttributes } from '../utils';
Expand Down Expand Up @@ -33,12 +33,7 @@ export class DeltaMetricProcessor<T extends Maybe<Accumulation>> {
this._overflowHashCode = hashAttributes(this._overflowAttributes);
}

record(
value: number,
attributes: Attributes,
_context: Context,
collectionTime: number
) {
record(value: number, attributes: Attributes, collectionTime: number) {
let accumulation = this._activeCollectionStorage.get(attributes);

if (!accumulation) {
Expand Down
5 changes: 2 additions & 3 deletions packages/sdk-metrics/src/state/MeterSharedState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { ObservableRegistry } from './ObservableRegistry';
import { SyncMetricStorage } from './SyncMetricStorage';
import type { Accumulation, Aggregator } from '../aggregator/types';
import type { IAttributesProcessor } from '../view/AttributesProcessor';
import { createNoopAttributesProcessor } from '../view/AttributesProcessor';
import type { MetricStorage } from './MetricStorage';

/**
Expand Down Expand Up @@ -166,7 +165,7 @@ export class MeterSharedState {
const storage = new MetricStorageType(
descriptor,
aggregator,
createNoopAttributesProcessor(),
undefined,
[collector],
cardinalityLimit
) as R;
Expand All @@ -190,7 +189,7 @@ interface MetricStorageConstructor {
new (
instrumentDescriptor: InstrumentDescriptor,
aggregator: Aggregator<Maybe<Accumulation>>,
attributesProcessor: IAttributesProcessor,
attributesProcessor: IAttributesProcessor | undefined,
collectors: MetricCollectorHandle[],
aggregationCardinalityLimit?: number
): MetricStorage;
Expand Down
11 changes: 10 additions & 1 deletion packages/sdk-metrics/src/state/MultiWritableMetricStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,32 @@
*/

import type { Context, Attributes } from '@opentelemetry/api';
import { context as contextApi } from '@opentelemetry/api';
import type { WritableMetricStorage } from './WritableMetricStorage';

/**
* Internal interface.
*/
export class MultiMetricStorage implements WritableMetricStorage {
private readonly _backingStorages: WritableMetricStorage[];
readonly hasAttributeProcessor: boolean;

constructor(backingStorages: WritableMetricStorage[]) {
this._backingStorages = backingStorages;
this.hasAttributeProcessor = backingStorages.some(
s => s.hasAttributeProcessor
);
}

record(
value: number,
attributes: Attributes,
context: Context,
context: Context | undefined,
recordTime: number
) {
if (this.hasAttributeProcessor && context === undefined) {
context = contextApi.active();
}
const storages = this._backingStorages;
for (let i = 0; i < storages.length; i++) {
storages[i].record(value, attributes, context, recordTime);
Expand Down
19 changes: 14 additions & 5 deletions packages/sdk-metrics/src/state/SyncMetricStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import type { Context, HrTime, Attributes } from '@opentelemetry/api';
import { context as contextApi } from '@opentelemetry/api';
import type { WritableMetricStorage } from './WritableMetricStorage';
import type { Accumulation, Aggregator } from '../aggregator/types';
import type { InstrumentDescriptor } from '../InstrumentDescriptor';
Expand All @@ -27,12 +28,12 @@ export class SyncMetricStorage<T extends Maybe<Accumulation>>
private _aggregationCardinalityLimit?: number;
private _deltaMetricStorage: DeltaMetricProcessor<T>;
private _temporalMetricStorage: TemporalMetricProcessor<T>;
private _attributesProcessor: IAttributesProcessor;
private _attributesProcessor?: IAttributesProcessor;

constructor(
instrumentDescriptor: InstrumentDescriptor,
aggregator: Aggregator<T>,
attributesProcessor: IAttributesProcessor,
attributesProcessor: IAttributesProcessor | undefined,
collectorHandles: MetricCollectorHandle[],
aggregationCardinalityLimit?: number
) {
Expand All @@ -47,16 +48,24 @@ export class SyncMetricStorage<T extends Maybe<Accumulation>>
collectorHandles
);
this._attributesProcessor = attributesProcessor;
this.hasAttributeProcessor = attributesProcessor !== undefined;
}

readonly hasAttributeProcessor: boolean;

record(
value: number,
attributes: Attributes,
context: Context,
context: Context | undefined,
recordTime: number
) {
attributes = this._attributesProcessor.process(attributes, context);
this._deltaMetricStorage.record(value, attributes, context, recordTime);
if (this._attributesProcessor !== undefined) {
attributes = this._attributesProcessor.process(
attributes,
context ?? contextApi.active()
);
}
this._deltaMetricStorage.record(value, attributes, recordTime);
}

/**
Expand Down
5 changes: 4 additions & 1 deletion packages/sdk-metrics/src/state/WritableMetricStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import type { AttributeHashMap } from './HashMap';
* An interface representing SyncMetricStorage with type parameters removed.
*/
export interface WritableMetricStorage {
/** Whether this storage has an attribute processor that needs context. */
hasAttributeProcessor: boolean;

/** Records a measurement. */
record(
value: number,
attributes: Attributes,
context: Context,
context: Context | undefined,
recordTime: number
): void;
}
Expand Down
22 changes: 21 additions & 1 deletion packages/sdk-metrics/test/performance/benchmark/counter-add.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,28 @@
*/

const Benchmark = require('benchmark');
const { MeterProvider } = require('../../../build/src');
const {
MeterProvider,
createAllowListAttributesProcessor,
} = require('../../../build/src');

const provider = new MeterProvider();
const meter = provider.getMeter('bench');
const counter = meter.createCounter('bench.counter');

const viewProvider = new MeterProvider({
views: [
{
instrumentName: 'bench.counter.view',
attributesProcessors: [
createAllowListAttributesProcessor(['service', 'route']),
],
},
],
});
const viewMeter = viewProvider.getMeter('bench');
const viewCounter = viewMeter.createCounter('bench.counter.view');

const fixedAttributes = {
service: 'checkout',
route: '/api/cart',
Expand Down Expand Up @@ -42,4 +58,8 @@ suite.add('Counter.add (varied attributes, 100 combos)', () => {
counter.add(1, variedAttributes[variedIdx++ % 100]);
});

suite.add('Counter.add (with view attribute processor)', () => {
viewCounter.add(1, fixedAttributes);
});

suite.run();
11 changes: 5 additions & 6 deletions packages/sdk-metrics/test/state/DeltaMetricProcessor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

import * as api from '@opentelemetry/api';
import * as assert from 'assert';
import { DropAggregator, SumAggregator } from '../../src/aggregator';
import { DeltaMetricProcessor } from '../../src/state/DeltaMetricProcessor';
Expand All @@ -17,7 +16,7 @@ describe('DeltaMetricProcessor', () => {

for (const value of commonValues) {
for (const attributes of commonAttributes) {
metricProcessor.record(value, attributes, api.context.active(), 0);
metricProcessor.record(value, attributes, 0);
}
}
});
Expand All @@ -27,7 +26,7 @@ describe('DeltaMetricProcessor', () => {

for (const value of commonValues) {
for (const attributes of commonAttributes) {
metricProcessor.record(value, attributes, api.context.active(), 0);
metricProcessor.record(value, attributes, 0);
}
}
});
Expand Down Expand Up @@ -134,9 +133,9 @@ describe('DeltaMetricProcessor', () => {
it('should export', () => {
const metricProcessor = new DeltaMetricProcessor(new SumAggregator(true));

metricProcessor.record(1, { attribute: '1' }, api.ROOT_CONTEXT, 0);
metricProcessor.record(2, { attribute: '1' }, api.ROOT_CONTEXT, 1000);
metricProcessor.record(1, { attribute: '2' }, api.ROOT_CONTEXT, 2000);
metricProcessor.record(1, { attribute: '1' }, 0);
metricProcessor.record(2, { attribute: '1' }, 1000);
metricProcessor.record(1, { attribute: '2' }, 2000);

let accumulations = metricProcessor.collect();
assert.strictEqual(accumulations.size, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@
*/

import * as api from '@opentelemetry/api';
import type { Attributes } from '@opentelemetry/api';
import type { Attributes, Context } from '@opentelemetry/api';
import * as assert from 'assert';
import { SumAggregator } from '../../src/aggregator';
import { AggregationTemporality } from '../../src/export/AggregationTemporality';
import type { MetricCollectorHandle } from '../../src/state/MetricCollector';
import { MultiMetricStorage } from '../../src/state/MultiWritableMetricStorage';
import { SyncMetricStorage } from '../../src/state/SyncMetricStorage';
import type { WritableMetricStorage } from '../../src/state/WritableMetricStorage';
import type { IAttributesProcessor } from '../../src/view/AttributesProcessor';
import type { Measurement } from '../util';
import {
assertMeasurementEqual,
commonAttributes,
commonValues,
defaultInstrumentDescriptor,
} from '../util';

describe('MultiMetricStorage', () => {
Expand All @@ -22,13 +28,14 @@ describe('MultiMetricStorage', () => {

for (const value of commonValues) {
for (const attribute of commonAttributes) {
metricStorage.record(value, attribute, api.context.active(), 0);
metricStorage.record(value, attribute, undefined, 0);
}
}
});

it('record with multiple backing storages', () => {
class TestWritableMetricStorage implements WritableMetricStorage {
hasAttributeProcessor = false;
records: Measurement[] = [];
record(
value: number,
Expand Down Expand Up @@ -68,5 +75,69 @@ describe('MultiMetricStorage', () => {
assertMeasurementEqual(backingStorage2.records[idx], expected);
}
});

it('should resolve active context for attribute processors', () => {
const deltaCollector: MetricCollectorHandle = {
selectAggregationTemporality: () => AggregationTemporality.DELTA,
selectCardinalityLimit: () => 2000,
};

const processor: IAttributesProcessor = {
process(incoming: Attributes, context?: Context) {
assert.strictEqual(context, api.context.active());
return incoming;
},
};

const storage1 = new SyncMetricStorage(
defaultInstrumentDescriptor,
new SumAggregator(true),
processor,
[deltaCollector]
);
const storage2 = new SyncMetricStorage(
defaultInstrumentDescriptor,
new SumAggregator(true),
processor,
[deltaCollector]
);
const multi = new MultiMetricStorage([storage1, storage2]);

multi.record(1, {}, undefined, 0);
});

it('should pass provided context to attribute processor', () => {
const deltaCollector: MetricCollectorHandle = {
selectAggregationTemporality: () => AggregationTemporality.DELTA,
selectCardinalityLimit: () => 2000,
};

const expectedContext = api.ROOT_CONTEXT.setValue(
api.createContextKey('test'),
'value'
);
const processor: IAttributesProcessor = {
process(incoming: Attributes, context?: Context) {
assert.strictEqual(context, expectedContext);
return incoming;
},
};

const storage1 = new SyncMetricStorage(
defaultInstrumentDescriptor,
new SumAggregator(true),
processor,
[deltaCollector]
);
const storage2 = new SyncMetricStorage(
defaultInstrumentDescriptor,
new SumAggregator(true),
processor,
[deltaCollector]
);
const multi = new MultiMetricStorage([storage1, storage2]);

multi.record(1, {}, expectedContext, 0);
});
});
});
Loading