Skip to content

Commit 7287831

Browse files
authored
[Monitor OpenTelemetry] Fix OTLP Metric Exporter Creation (#35879)
### Packages impacted by this PR @azure/monitor-opentelemtry ### Describe the problem that is addressed by this PR This pull request introduces improvements to the Azure Monitor OpenTelemetry SDK, focusing on enhanced support for metrics, dependency updates, and better type safety for log processing. The most important changes are grouped below: **Metrics Support Improvements** * Added support for multiple metric exporters by introducing a `metricReaders` option to `AzureMonitorOpenTelemetryOptions`, allowing users to supply additional `MetricReader` instances. The SDK now always includes the Azure Monitor metric reader and any user-provided readers. (`sdk/monitor/monitor-opentelemetry/src/index.ts`, `sdk/monitor/monitor-opentelemetry/src/types.ts`, `sdk/monitor/monitor-opentelemetry/review/monitor-opentelemetry-node.api.md`) [[1]](diffhunk://#diff-e7e1dcf8f0ac5c3aea3811994ab032c757ad020b9ca8855dec69d18bb5343ef3R85-R94) [[2]](diffhunk://#diff-48c64776365220d515750e313877e3ff6c92347670e2e5892c0df15179561028R7) [[3]](diffhunk://#diff-053e8b2a667e27bc937e196833f522a1e6b137f1c1f4e64763144730dd4b149fR25) * Updated the SDK initialization to use the new `metricReaders` array instead of the deprecated single `metricReader` property. (`sdk/monitor/monitor-opentelemetry/src/index.ts`) **Dependency Updates** * Upgraded multiple OpenTelemetry dependencies to their latest versions in `package.json`, including exporters, instrumentations, and core packages, ensuring compatibility and access to new features. (`sdk/monitor/monitor-opentelemetry/package.json`) [[1]](diffhunk://#diff-a55bcbd4eeed188b4872d7378e7bc7111e3a04059f36f5a6a4b52a74b29a971eL75-R95) [[2]](diffhunk://#diff-a55bcbd4eeed188b4872d7378e7bc7111e3a04059f36f5a6a4b52a74b29a971eR59) * Added the `@opentelemetry/exporter-metrics-otlp-http` dependency for OTLP metric exporter support. (`sdk/monitor/monitor-opentelemetry/package.json`) **Log Processing Type Safety** * Refactored all usages of the `LogRecord` type to the more accurate `SdkLogRecord` type throughout the codebase, improving type safety and compatibility with newer OpenTelemetry versions. [[1]](diffhunk://#diff-40c7c732674ada132349217c585300c9623317b0d76d6af9354fcc05ace24c8bL5-R5) [[2]](diffhunk://#diff-40c7c732674ada132349217c585300c9623317b0d76d6af9354fcc05ace24c8bL23-R23) [[3]](diffhunk://#diff-6cb4c7dc3729862669d8e91910d3d2f4f7ea029135b0ed71a0c2bb9910a35c10L5-R5) [[4]](diffhunk://#diff-6cb4c7dc3729862669d8e91910d3d2f4f7ea029135b0ed71a0c2bb9910a35c10L19-R19) [[5]](diffhunk://#diff-01b317e2f20b9cfc18a7b7fbb904993e21fbe2240f20412387fc6453556f8663R7-L10) [[6]](diffhunk://#diff-01b317e2f20b9cfc18a7b7fbb904993e21fbe2240f20412387fc6453556f8663L97-R97) [[7]](diffhunk://#diff-89a783818735d150a51da07f52c0523bb4a68a241a174379fe0439d6489dc203R12) [[8]](diffhunk://#diff-89a783818735d150a51da07f52c0523bb4a68a241a174379fe0439d6489dc203L23) [[9]](diffhunk://#diff-89a783818735d150a51da07f52c0523bb4a68a241a174379fe0439d6489dc203L217-R217) [[10]](diffhunk://#diff-e7fb1f5cf2a3336ec774595f29f7916a1d5df80a44162e4bd081a70422cd0a25R9-L14) [[11]](diffhunk://#diff-e7fb1f5cf2a3336ec774595f29f7916a1d5df80a44162e4bd081a70422cd0a25L517-R517) [[12]](diffhunk://#diff-eb7dc54dc1601d5790cfdb864a0976f71c58660fc5ad8258465d588fb268db01L7-R7) [[13]](diffhunk://#diff-eb7dc54dc1601d5790cfdb864a0976f71c58660fc5ad8258465d588fb268db01L395-R395) [[14]](diffhunk://#diff-46220be157c675f1ae424657fc5aaa60bebcf7c034e5790a926a1b5ead3d4a95R9-L14) [[15]](diffhunk://#diff-46220be157c675f1ae424657fc5aaa60bebcf7c034e5790a926a1b5ead3d4a95L148-R148) [[16]](diffhunk://#diff-0d388292857d67eba5aa26a7c3079cfeaa39ada1538619af33d94d486615a7eeR6) [[17]](diffhunk://#diff-0d388292857d67eba5aa26a7c3079cfeaa39ada1538619af33d94d486615a7eeL37) [[18]](diffhunk://#diff-0d388292857d67eba5aa26a7c3079cfeaa39ada1538619af33d94d486615a7eeL131-R131) [[19]](diffhunk://#diff-0d388292857d67eba5aa26a7c3079cfeaa39ada1538619af33d94d486615a7eeL145-R145) [[20]](diffhunk://#diff-0d388292857d67eba5aa26a7c3079cfeaa39ada1538619af33d94d486615a7eeL159-R159) **Internal SDK Access for Testing** * Added an internal `_getSdkInstance()` method to expose the SDK instance for testing purposes. (`sdk/monitor/monitor-opentelemetry/src/index.ts`, `sdk/monitor/monitor-opentelemetry/review/monitor-opentelemetry-node.api.md`) [[1]](diffhunk://#diff-e7e1dcf8f0ac5c3aea3811994ab032c757ad020b9ca8855dec69d18bb5343ef3R124-R131) [[2]](diffhunk://#diff-053e8b2a667e27bc937e196833f522a1e6b137f1c1f4e64763144730dd4b149fR38-R40) **Documentation** * Updated the `CHANGELOG.md` to reflect bug fixes for metric exporter support and dependency updates. (`sdk/monitor/monitor-opentelemetry/CHANGELOG.md`) ### Are there test cases added in this PR? _(If not, why?)_ Yes ### Checklists - [x] Added impacted package name to the issue description - [ ] Does this PR needs any fixes in the SDK Generator?** _(If so, create an Issue in the [Autorest/typescript](https://github.com/Azure/autorest.typescript) repository and link it here)_ - [x] Added a changelog (if necessary)
1 parent d24df88 commit 7287831

24 files changed

+619
-381
lines changed

pnpm-lock.yaml

Lines changed: 296 additions & 259 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/monitor/monitor-opentelemetry/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Release History
22

3+
### 1.13.1 ()
4+
5+
### Bugs Fixed
6+
7+
- Fix support for multiple metric exporters and OTLP metric exporter creation.
8+
9+
### Other Changes
10+
11+
- Update OpenTelemetry dependencies.
12+
313
### 1.13.0 (2025-09-05)
414

515
### Features Added

sdk/monitor/monitor-opentelemetry/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ Further information on usage of the browser SDK loader can be found [here](https
254254
You might set the Cloud Role Name and the Cloud Role Instance via [OpenTelemetry Resource](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md#resource-sdk) attributes.
255255

256256
```ts snippet:ReadmeSampleSetRoleNameAndInstance
257+
import { emptyResource } from "@opentelemetry/resources";
257258
import {
258259
ATTR_SERVICE_NAME,
259260
SEMRESATTRS_SERVICE_NAMESPACE,
@@ -264,7 +265,7 @@ import { AzureMonitorOpenTelemetryOptions, useAzureMonitor } from "@azure/monito
264265
// ----------------------------------------
265266
// Setting role name and role instance
266267
// ----------------------------------------
267-
const customResource = Resource.EMPTY;
268+
const customResource = emptyResource();
268269
customResource.attributes[ATTR_SERVICE_NAME] = "my-helloworld-service";
269270
customResource.attributes[SEMRESATTRS_SERVICE_NAMESPACE] = "my-namespace";
270271
customResource.attributes[SEMRESATTRS_SERVICE_INSTANCE_ID] = "my-instance";
@@ -334,7 +335,7 @@ Use a custom span processor and log record processor in order to attach and corr
334335
import { SpanProcessor, ReadableSpan } from "@opentelemetry/sdk-trace-base";
335336
import { Span, Context, trace } from "@opentelemetry/api";
336337
import { AI_OPERATION_NAME } from "@azure/monitor-opentelemetry-exporter";
337-
import { LogRecordProcessor, LogRecord } from "@opentelemetry/sdk-logs";
338+
import { LogRecordProcessor, SdkLogRecord } from "@opentelemetry/sdk-logs";
338339
import { AzureMonitorOpenTelemetryOptions, useAzureMonitor } from "@azure/monitor-opentelemetry";
339340

340341
class SpanEnrichingProcessor implements SpanProcessor {
@@ -361,7 +362,7 @@ class LogRecordEnrichingProcessor implements LogRecordProcessor {
361362
async shutdown(): Promise<void> {
362363
// shutdown code here
363364
}
364-
onEmit(_logRecord: LogRecord, _context: Context): void {
365+
onEmit(_logRecord: SdkLogRecord, _context: Context): void {
365366
const parentSpan = trace.getSpan(_context);
366367
if (parentSpan && "name" in parentSpan) {
367368
// If the parent span has a name we can assume it is a ReadableSpan and cast it.

sdk/monitor/monitor-opentelemetry/package.json

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@azure/eslint-plugin-azure-sdk": "workspace:^",
5757
"@azure/functions": "^4.5.0",
5858
"@azure/functions-old": "npm:@azure/[email protected]",
59+
"@opentelemetry/exporter-metrics-otlp-http": "^0.204.0",
5960
"@types/node": "catalog:",
6061
"@vitest/coverage-istanbul": "catalog:testing",
6162
"dotenv": "catalog:testing",
@@ -72,26 +73,26 @@
7273
"@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.7",
7374
"@microsoft/applicationinsights-web-snippet": "^1.2.1",
7475
"@opentelemetry/api": "^1.9.0",
75-
"@opentelemetry/api-logs": "^0.200.0",
76-
"@opentelemetry/core": "^2.0.0",
77-
"@opentelemetry/instrumentation": "^0.200.0",
78-
"@opentelemetry/instrumentation-bunyan": "^0.46.0",
79-
"@opentelemetry/instrumentation-http": "^0.200.0",
80-
"@opentelemetry/instrumentation-mongodb": "^0.53.0",
81-
"@opentelemetry/instrumentation-mysql": "^0.46.0",
82-
"@opentelemetry/instrumentation-pg": "^0.52.0",
83-
"@opentelemetry/instrumentation-redis": "^0.47.0",
84-
"@opentelemetry/instrumentation-redis-4": "^0.47.0",
85-
"@opentelemetry/instrumentation-winston": "^0.45.0",
76+
"@opentelemetry/api-logs": "^0.204.0",
77+
"@opentelemetry/core": "^2.1.0",
78+
"@opentelemetry/instrumentation": "^0.204.0",
79+
"@opentelemetry/instrumentation-bunyan": "^0.50.0",
80+
"@opentelemetry/instrumentation-http": "^0.204.0",
81+
"@opentelemetry/instrumentation-mongodb": "^0.57.0",
82+
"@opentelemetry/instrumentation-mysql": "^0.50.0",
83+
"@opentelemetry/instrumentation-pg": "^0.57.0",
84+
"@opentelemetry/instrumentation-redis": "^0.53.0",
85+
"@opentelemetry/instrumentation-redis-4": "^0.49.0",
86+
"@opentelemetry/instrumentation-winston": "^0.49.0",
8687
"@opentelemetry/resource-detector-azure": "^0.7.0",
87-
"@opentelemetry/resources": "^2.0.0",
88-
"@opentelemetry/sdk-logs": "^0.200.0",
89-
"@opentelemetry/sdk-metrics": "^2.0.0",
90-
"@opentelemetry/sdk-node": "^0.200.0",
91-
"@opentelemetry/sdk-trace-base": "^2.0.0",
92-
"@opentelemetry/sdk-trace-node": "^2.0.0",
88+
"@opentelemetry/resources": "^2.1.0",
89+
"@opentelemetry/sdk-logs": "^0.204.0",
90+
"@opentelemetry/sdk-metrics": "^2.1.0",
91+
"@opentelemetry/sdk-node": "^0.204.0",
92+
"@opentelemetry/sdk-trace-base": "^2.1.0",
93+
"@opentelemetry/sdk-trace-node": "^2.1.0",
9394
"@opentelemetry/semantic-conventions": "^1.32.0",
94-
"@opentelemetry/winston-transport": "^0.11.0",
95+
"@opentelemetry/winston-transport": "^0.15.0",
9596
"tslib": "^2.8.1"
9697
},
9798
"sideEffects": false,

sdk/monitor/monitor-opentelemetry/review/monitor-opentelemetry-node.api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import type { AzureMonitorExporterOptions } from '@azure/monitor-opentelemetry-exporter';
88
import type { InstrumentationConfig } from '@opentelemetry/instrumentation';
99
import type { LogRecordProcessor } from '@opentelemetry/sdk-logs';
10+
import type { MetricReader } from '@opentelemetry/sdk-metrics';
11+
import { NodeSDK } from '@opentelemetry/sdk-node';
1012
import type { Resource } from '@opentelemetry/resources';
1113
import type { SpanProcessor } from '@opentelemetry/sdk-trace-base';
1214

@@ -20,6 +22,7 @@ export interface AzureMonitorOpenTelemetryOptions {
2022
enableTraceBasedSamplingForLogs?: boolean;
2123
instrumentationOptions?: InstrumentationOptions;
2224
logRecordProcessors?: LogRecordProcessor[];
25+
metricReaders?: MetricReader[];
2326
resource?: Resource;
2427
samplingRatio?: number;
2528
spanProcessors?: SpanProcessor[];
@@ -32,6 +35,9 @@ export interface BrowserSdkLoaderOptions {
3235
enabled?: boolean;
3336
}
3437

38+
// @internal
39+
export function _getSdkInstance(): NodeSDK | undefined;
40+
3541
// @public
3642
export interface InstrumentationOptions {
3743
azureSdk?: InstrumentationConfig;

sdk/monitor/monitor-opentelemetry/src/index.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { metrics, trace } from "@opentelemetry/api";
55
import { logs } from "@opentelemetry/api-logs";
66
import type { NodeSDKConfiguration } from "@opentelemetry/sdk-node";
77
import { NodeSDK } from "@opentelemetry/sdk-node";
8+
import type { MetricReader } from "@opentelemetry/sdk-metrics";
89
import { InternalConfig } from "./shared/config.js";
910
import { MetricHandler } from "./metrics/index.js";
1011
import { TraceHandler } from "./traces/handler.js";
@@ -81,10 +82,16 @@ export function useAzureMonitor(options?: AzureMonitorOpenTelemetryOptions): voi
8182
const spanProcessors: SpanProcessor[] = options?.spanProcessors || [];
8283
const logRecordProcessors: LogRecordProcessor[] = options?.logRecordProcessors || [];
8384

85+
// Prepare metric readers - always include Azure Monitor
86+
const metricReaders: MetricReader[] = [
87+
metricHandler.getMetricReader(),
88+
...(options?.metricReaders || []),
89+
];
90+
8491
// Initialize OpenTelemetry SDK
8592
const sdkConfig: Partial<NodeSDKConfiguration> = {
8693
autoDetectResources: true,
87-
metricReader: metricHandler.getMetricReader(),
94+
metricReaders: metricReaders,
8895
views: metricHandler.getViews(),
8996
instrumentations: instrumentations,
9097
logRecordProcessors: [
@@ -114,3 +121,12 @@ export function shutdownAzureMonitor(): Promise<void> {
114121
browserSdkLoader?.dispose();
115122
return sdk?.shutdown();
116123
}
124+
125+
/**
126+
* Get the internal SDK instance for testing purposes
127+
* @internal
128+
*/
129+
// eslint-disable-next-line no-underscore-dangle
130+
export function _getSdkInstance(): NodeSDK | undefined {
131+
return sdk;
132+
}

sdk/monitor/monitor-opentelemetry/src/logs/batchLogRecordProcessor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

44
import { TraceFlags } from "@opentelemetry/api";
5-
import type { LogRecord, LogRecordExporter } from "@opentelemetry/sdk-logs";
5+
import type { LogRecordExporter, SdkLogRecord } from "@opentelemetry/sdk-logs";
66
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
77

88
/**
@@ -20,7 +20,7 @@ export class AzureBatchLogRecordProcessor extends BatchLogRecordProcessor {
2020
this._options = options;
2121
}
2222

23-
public onEmit(logRecord: LogRecord): void {
23+
public onEmit(logRecord: SdkLogRecord): void {
2424
// Trace based sampling for logs
2525
if (this._options.enableTraceBasedSamplingForLogs) {
2626
if (logRecord.spanContext && logRecord.spanContext.spanId) {

sdk/monitor/monitor-opentelemetry/src/logs/logRecordProcessor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

44
import type { MetricHandler } from "../metrics/handler.js";
5-
import type { LogRecord, LogRecordProcessor } from "@opentelemetry/sdk-logs";
5+
import type { LogRecordProcessor, SdkLogRecord } from "@opentelemetry/sdk-logs";
66
import { Logger } from "../shared/logging/index.js";
77

88
/**
@@ -16,7 +16,7 @@ export class AzureLogRecordProcessor implements LogRecordProcessor {
1616
this._metricHandler = metricHandler;
1717
}
1818

19-
public onEmit(logRecord: LogRecord): void {
19+
public onEmit(logRecord: SdkLogRecord): void {
2020
try {
2121
this._metricHandler.recordLog(logRecord);
2222
} catch (error) {

sdk/monitor/monitor-opentelemetry/src/metrics/handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
import { AzureMonitorMetricExporter } from "@azure/monitor-opentelemetry-exporter";
55
import type { PeriodicExportingMetricReaderOptions, ViewOptions } from "@opentelemetry/sdk-metrics";
66
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
7+
import type { SdkLogRecord } from "@opentelemetry/sdk-logs";
78
import type { InternalConfig } from "../shared/config.js";
89
import { StandardMetrics } from "./standardMetrics.js";
910
import type { ReadableSpan, Span } from "@opentelemetry/sdk-trace-base";
10-
import type { LogRecord } from "@opentelemetry/sdk-logs";
1111
import { APPLICATION_INSIGHTS_NO_STANDARD_METRICS } from "./types.js";
1212
import { LiveMetrics } from "./quickpulse/liveMetrics.js";
1313
import { PerformanceCounterMetrics } from "./performanceCounters.js";
@@ -94,7 +94,7 @@ export class MetricHandler {
9494
this._performanceCounters?.recordSpan(span);
9595
}
9696

97-
public recordLog(logRecord: LogRecord): void {
97+
public recordLog(logRecord: SdkLogRecord): void {
9898
this._standardMetrics?.recordLog(logRecord);
9999
this._liveMetrics?.recordLog(logRecord);
100100
this._performanceCounters?.recordLog(logRecord);

sdk/monitor/monitor-opentelemetry/src/metrics/performanceCounters.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
ObservableResult,
1010
} from "@opentelemetry/api";
1111
import { SpanKind, ValueType } from "@opentelemetry/api";
12+
import type { SdkLogRecord } from "@opentelemetry/sdk-logs";
1213
import { AzureMonitorMetricExporter } from "@azure/monitor-opentelemetry-exporter";
1314
import type {
1415
MeterProviderOptions,
@@ -20,7 +21,6 @@ import { PerformanceCounterMetricNames } from "./types.js";
2021
import type { AzureMonitorOpenTelemetryOptions } from "../types.js";
2122
import { getLogData, isExceptionData } from "./quickpulse/utils.js";
2223
import type { ExceptionData, TraceData } from "./quickpulse/types.js";
23-
import type { LogRecord } from "@opentelemetry/sdk-logs";
2424
import { Logger } from "../shared/logging/logger.js";
2525
import process from "node:process";
2626

@@ -214,7 +214,7 @@ export class PerformanceCounterMetrics {
214214
* Record Log metrics
215215
* @internal
216216
*/
217-
public recordLog(logRecord: LogRecord): void {
217+
public recordLog(logRecord: SdkLogRecord): void {
218218
const columns: TraceData | ExceptionData = getLogData(logRecord);
219219
if (isExceptionData(columns)) {
220220
this.totalExceptionCount++;

0 commit comments

Comments
 (0)