Skip to content

Commit 6febfb0

Browse files
authored
fix: otel metric semantic convs (open-feature#475)
Signed-off-by: Todd Baert <[email protected]>
1 parent 5b434fb commit 6febfb0

File tree

5 files changed

+59
-46
lines changed

5 files changed

+59
-46
lines changed

libs/hooks/open-telemetry/src/lib/constants.ts

-5
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// see: https://opentelemetry.io/docs/specs/otel/logs/semantic_conventions/feature-flags/
2+
export const FEATURE_FLAG = 'feature_flag';
3+
export const EXCEPTION_ATTR = 'exception'
4+
5+
export const ACTIVE_COUNT_NAME = `${FEATURE_FLAG}.evaluation_active_count`;
6+
export const REQUESTS_TOTAL_NAME = `${FEATURE_FLAG}.evaluation_requests_total`;
7+
export const SUCCESS_TOTAL_NAME = `${FEATURE_FLAG}.evaluation_success_total`;
8+
export const ERROR_TOTAL_NAME = `${FEATURE_FLAG}.evaluation_error_total`;
9+
10+
export type EvaluationAttributes = {[key: `${typeof FEATURE_FLAG}.${string}`]: string | undefined };
11+
export type ExceptionAttributes = { [EXCEPTION_ATTR]: string };
12+
13+
export const KEY_ATTR: keyof EvaluationAttributes = `${FEATURE_FLAG}.key`;
14+
export const PROVIDER_NAME_ATTR: keyof EvaluationAttributes = `${FEATURE_FLAG}.provider_name`;
15+
export const VARIANT_ATTR: keyof EvaluationAttributes = `${FEATURE_FLAG}.variant`;
16+
export const REASON_ATTR: keyof EvaluationAttributes = `${FEATURE_FLAG}.reason`;

libs/hooks/open-telemetry/src/lib/metrics/metrics-hook.spec.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
MetricReader,
77
ScopeMetrics,
88
} from '@opentelemetry/sdk-metrics';
9-
import { ACTIVE_COUNT_NAME, ERROR_TOTAL_NAME, REQUESTS_TOTAL_NAME, SUCCESS_TOTAL_NAME } from '../constants';
9+
import { ACTIVE_COUNT_NAME, ERROR_TOTAL_NAME, KEY_ATTR, PROVIDER_NAME_ATTR, REASON_ATTR, REQUESTS_TOTAL_NAME, SUCCESS_TOTAL_NAME, VARIANT_ATTR } from '../conventions';
1010
import { MetricsHook } from './metrics-hook';
1111

1212
// no-op "in-memory" reader
@@ -53,7 +53,7 @@ describe(MetricsHook.name, () => {
5353
ACTIVE_COUNT_NAME,
5454
0,
5555
(point) =>
56-
point.value === 1 && point.attributes.key === FLAG_KEY && point.attributes.provider === PROVIDER_NAME
56+
point.value === 1 && point.attributes[KEY_ATTR] === FLAG_KEY && point.attributes[PROVIDER_NAME_ATTR] === PROVIDER_NAME
5757
)
5858
).toBeTruthy();
5959
expect(
@@ -62,7 +62,7 @@ describe(MetricsHook.name, () => {
6262
REQUESTS_TOTAL_NAME,
6363
0,
6464
(point) =>
65-
point.value === 1 && point.attributes.key === FLAG_KEY && point.attributes.provider === PROVIDER_NAME
65+
point.value === 1 && point.attributes[KEY_ATTR] === FLAG_KEY && point.attributes[PROVIDER_NAME_ATTR] === PROVIDER_NAME
6666
)
6767
).toBeTruthy();
6868
});
@@ -97,10 +97,10 @@ describe(MetricsHook.name, () => {
9797
0,
9898
(point) =>
9999
point.value === 1 &&
100-
point.attributes.key === FLAG_KEY &&
101-
point.attributes.provider === PROVIDER_NAME &&
102-
point.attributes.variant === VARIANT &&
103-
point.attributes.reason === StandardResolutionReasons.STATIC
100+
point.attributes[KEY_ATTR] === FLAG_KEY &&
101+
point.attributes[PROVIDER_NAME_ATTR] === PROVIDER_NAME &&
102+
point.attributes[VARIANT_ATTR] === VARIANT &&
103+
point.attributes[REASON_ATTR] === StandardResolutionReasons.STATIC
104104
)
105105
).toBeTruthy();
106106
});
@@ -130,10 +130,10 @@ describe(MetricsHook.name, () => {
130130
1,
131131
(point) =>
132132
point.value === 1 &&
133-
point.attributes.key === FLAG_KEY &&
134-
point.attributes.provider === PROVIDER_NAME &&
135-
point.attributes.variant === VALUE.toString() &&
136-
point.attributes.reason === StandardResolutionReasons.STATIC
133+
point.attributes[KEY_ATTR] === FLAG_KEY &&
134+
point.attributes[PROVIDER_NAME_ATTR] === PROVIDER_NAME &&
135+
point.attributes[VARIANT_ATTR] === VALUE.toString() &&
136+
point.attributes[REASON_ATTR] === StandardResolutionReasons.STATIC
137137
)
138138
).toBeTruthy();
139139
});
@@ -160,7 +160,7 @@ describe(MetricsHook.name, () => {
160160
ACTIVE_COUNT_NAME,
161161
1,
162162
(point) =>
163-
point.value === -1 && point.attributes.key === FLAG_KEY && point.attributes.provider === PROVIDER_NAME
163+
point.value === -1 && point.attributes[KEY_ATTR] === FLAG_KEY && point.attributes[PROVIDER_NAME_ATTR] === PROVIDER_NAME
164164
)
165165
).toBeTruthy();
166166
});
@@ -188,7 +188,7 @@ describe(MetricsHook.name, () => {
188188
ERROR_TOTAL_NAME,
189189
0,
190190
(point) =>
191-
point.value === 1 && point.attributes.key === FLAG_KEY && point.attributes.provider === PROVIDER_NAME
191+
point.value === 1 && point.attributes[KEY_ATTR] === FLAG_KEY && point.attributes[PROVIDER_NAME_ATTR] === PROVIDER_NAME
192192
)
193193
).toBeTruthy();
194194
});

libs/hooks/open-telemetry/src/lib/metrics/metrics-hook.ts

+26-18
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@ import {
77
type HookContext,
88
} from '@openfeature/js-sdk';
99
import { Counter, UpDownCounter, ValueType, metrics } from '@opentelemetry/api';
10-
import { ACTIVE_COUNT_NAME, ERROR_TOTAL_NAME, REQUESTS_TOTAL_NAME, SUCCESS_TOTAL_NAME } from '../constants';
10+
import {
11+
ACTIVE_COUNT_NAME,
12+
EXCEPTION_ATTR,
13+
ERROR_TOTAL_NAME,
14+
EvaluationAttributes,
15+
ExceptionAttributes,
16+
KEY_ATTR,
17+
PROVIDER_NAME_ATTR,
18+
REASON_ATTR,
19+
REQUESTS_TOTAL_NAME,
20+
SUCCESS_TOTAL_NAME,
21+
VARIANT_ATTR
22+
} from '../conventions';
1123

12-
type EvaluationAttributes = Pick<EvaluationDetails<FlagValue>, 'variant' | 'reason'> & {
13-
key: string;
14-
provider: string;
15-
};
16-
type ErrorEvaluationAttributes = EvaluationAttributes & { exception: string };
24+
type ErrorEvaluationAttributes = EvaluationAttributes & ExceptionAttributes;
1725

1826
const METER_NAME = 'js.openfeature.dev';
1927

@@ -49,35 +57,35 @@ export class MetricsHook implements Hook {
4957
}
5058

5159
before(hookContext: BeforeHookContext) {
52-
const attributes = {
53-
key: hookContext.flagKey,
54-
provider: hookContext.providerMetadata.name,
60+
const attributes: EvaluationAttributes = {
61+
[KEY_ATTR]: hookContext.flagKey,
62+
[PROVIDER_NAME_ATTR]: hookContext.providerMetadata.name,
5563
};
5664
this.evaluationActiveUpDownCounter.add(1, attributes);
5765
this.evaluationRequestCounter.add(1, attributes);
5866
}
5967

6068
after(hookContext: Readonly<HookContext<FlagValue>>, evaluationDetails: EvaluationDetails<FlagValue>) {
6169
this.evaluationSuccessCounter.add(1, {
62-
key: hookContext.flagKey,
63-
provider: hookContext.providerMetadata.name,
64-
variant: evaluationDetails.variant ?? evaluationDetails.value?.toString(),
65-
reason: evaluationDetails.reason ?? StandardResolutionReasons.UNKNOWN,
70+
[KEY_ATTR]: hookContext.flagKey,
71+
[PROVIDER_NAME_ATTR]: hookContext.providerMetadata.name,
72+
[VARIANT_ATTR]: evaluationDetails.variant ?? evaluationDetails.value?.toString(),
73+
[REASON_ATTR]: evaluationDetails.reason ?? StandardResolutionReasons.UNKNOWN,
6674
});
6775
}
6876

6977
error(hookContext: Readonly<HookContext<FlagValue>>, error: unknown) {
7078
this.evaluationErrorCounter.add(1, {
71-
key: hookContext.flagKey,
72-
provider: hookContext.providerMetadata.name,
73-
exception: (error as Error)?.message || 'Unknown error',
79+
[KEY_ATTR]: hookContext.flagKey,
80+
[PROVIDER_NAME_ATTR]: hookContext.providerMetadata.name,
81+
[EXCEPTION_ATTR]: (error as Error)?.message || 'Unknown error',
7482
});
7583
}
7684

7785
finally(hookContext: Readonly<HookContext<FlagValue>>) {
7886
this.evaluationActiveUpDownCounter.add(-1, {
79-
key: hookContext.flagKey,
80-
provider: hookContext.providerMetadata.name,
87+
[KEY_ATTR]: hookContext.flagKey,
88+
[PROVIDER_NAME_ATTR]: hookContext.providerMetadata.name,
8189
});
8290
}
8391
}

libs/hooks/open-telemetry/src/lib/traces/tracing-hook.ts

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { Hook, HookContext, EvaluationDetails, FlagValue } from '@openfeature/js-sdk';
22
import { trace } from '@opentelemetry/api';
3-
import { FEATURE_FLAG } from '../constants';
4-
5-
const spanEventProperties = Object.freeze({
6-
FLAG_KEY: `${FEATURE_FLAG}.key`,
7-
PROVIDER_NAME: `${FEATURE_FLAG}.provider_name`,
8-
VARIANT: `${FEATURE_FLAG}.variant`,
9-
});
3+
import { FEATURE_FLAG, KEY_ATTR, PROVIDER_NAME_ATTR, VARIANT_ATTR } from '../conventions';
104

115
export class TracingHook implements Hook {
126
after(hookContext: HookContext, evaluationDetails: EvaluationDetails<FlagValue>) {
@@ -23,9 +17,9 @@ export class TracingHook implements Hook {
2317
}
2418

2519
currentTrace.addEvent(FEATURE_FLAG, {
26-
[spanEventProperties.FLAG_KEY]: hookContext.flagKey,
27-
[spanEventProperties.PROVIDER_NAME]: hookContext.providerMetadata.name,
28-
[spanEventProperties.VARIANT]: variant,
20+
[KEY_ATTR]: hookContext.flagKey,
21+
[PROVIDER_NAME_ATTR]: hookContext.providerMetadata.name,
22+
[VARIANT_ATTR]: variant,
2923
});
3024
}
3125
}

0 commit comments

Comments
 (0)