Skip to content

Commit 6d31a18

Browse files
feat(opentelemetry-sdk-node): automatically configure metrics exporter based on environment variables (#5168)
Co-authored-by: Marc Pichler <[email protected]>
1 parent e03b6e7 commit 6d31a18

File tree

7 files changed

+384
-4
lines changed

7 files changed

+384
-4
lines changed

experimental/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ All notable changes to experimental packages in this project will be documented
99

1010
### :rocket: (Enhancement)
1111

12+
* feat(opentelemetry-sdk-node): automatically configure metrics exporter based on environment variables [#5168](https://github.com/open-telemetry/opentelemetry-js/pull/5168) @bhaskarbanerjee
13+
1214
### :bug: (Bug Fix)
1315

1416
### :books: (Refine Doc)

experimental/packages/opentelemetry-sdk-node/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ This is an alternative to programmatically configuring an exporter or span proce
212212
| OTEL_EXPORTER_OTLP_TRACES_PROTOCOL | The transport protocol to use on OTLP trace requests. Options include `grpc`, `http/protobuf`, and `http/json`. Default is `http/protobuf`. |
213213
| OTEL_EXPORTER_OTLP_METRICS_PROTOCOL | The transport protocol to use on OTLP metric requests. Options include `grpc`, `http/protobuf`, and `http/json`. Default is `http/protobuf`. |
214214
| OTEL_EXPORTER_OTLP_LOGS_PROTOCOL | The transport protocol to use on OTLP log requests. Options include `grpc`, `http/protobuf`, and `http/json`. Default is `http/protobuf`. |
215+
| OTEL_METRICS_EXPORTER | Metrics exporter to be used. options are `otlp`, `prometheus`, `console` or `none`. |
216+
| OTEL_METRIC_EXPORT_INTERVAL | The export interval when using a push Metric Reader. Default is `60000`. |
217+
| OTEL_METRIC_EXPORT_TIMEOUT | The export timeout when using a push Metric Reader. Default is `30000`. |
215218

216219
Additionally, you can specify other applicable environment variables that apply to each exporter such as the following:
217220

experimental/packages/opentelemetry-sdk-node/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@
5454
"@opentelemetry/exporter-trace-otlp-proto": "0.56.0",
5555
"@opentelemetry/exporter-zipkin": "1.29.0",
5656
"@opentelemetry/instrumentation": "0.56.0",
57+
"@opentelemetry/exporter-metrics-otlp-grpc": "0.56.0",
58+
"@opentelemetry/exporter-metrics-otlp-proto": "0.56.0",
59+
"@opentelemetry/exporter-metrics-otlp-http": "0.56.0",
60+
"@opentelemetry/exporter-prometheus": "0.56.0",
5761
"@opentelemetry/resources": "1.29.0",
5862
"@opentelemetry/sdk-logs": "0.56.0",
5963
"@opentelemetry/sdk-metrics": "1.29.0",

experimental/packages/opentelemetry-sdk-node/src/sdk.ts

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,17 @@ import {
4848
import { OTLPLogExporter as OTLPHttpLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
4949
import { OTLPLogExporter as OTLPGrpcLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
5050
import { OTLPLogExporter as OTLPProtoLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';
51-
import { MeterProvider, MetricReader, View } from '@opentelemetry/sdk-metrics';
51+
import { OTLPMetricExporter as OTLPGrpcMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
52+
import { OTLPMetricExporter as OTLPProtoMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto';
53+
import { OTLPMetricExporter as OTLPHttpMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
54+
import { PrometheusExporter as PrometheusMetricExporter } from '@opentelemetry/exporter-prometheus';
55+
import {
56+
MeterProvider,
57+
MetricReader,
58+
View,
59+
ConsoleMetricExporter,
60+
PeriodicExportingMetricReader,
61+
} from '@opentelemetry/sdk-metrics';
5262
import {
5363
BatchSpanProcessor,
5464
SpanProcessor,
@@ -86,6 +96,107 @@ export type LoggerProviderConfig = {
8696
logRecordProcessors: LogRecordProcessor[];
8797
};
8898

99+
/**
100+
* @Returns param value, if set else returns the default value
101+
*/
102+
function getValueInMillis(envName: string, defaultValue: number): number {
103+
return parseInt(process.env[envName] || '') || defaultValue;
104+
}
105+
106+
/**
107+
*
108+
* @returns MetricReader[] if appropriate environment variables are configured
109+
*/
110+
function configureMetricProviderFromEnv(): MetricReader[] {
111+
const metricReaders: MetricReader[] = [];
112+
const metricsExporterList = process.env.OTEL_METRICS_EXPORTER?.trim();
113+
if (!metricsExporterList) {
114+
return metricReaders;
115+
}
116+
const enabledExporters = filterBlanksAndNulls(metricsExporterList.split(','));
117+
118+
if (enabledExporters.length === 0) {
119+
diag.info('OTEL_METRICS_EXPORTER is empty. Using default otlp exporter.');
120+
}
121+
122+
if (enabledExporters.includes('none')) {
123+
diag.info(
124+
`OTEL_METRICS_EXPORTER contains "none". Metric provider will not be initialized.`
125+
);
126+
return metricReaders;
127+
}
128+
enabledExporters.forEach(exporter => {
129+
if (exporter === 'otlp') {
130+
const protocol =
131+
process.env.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL?.trim() ||
132+
process.env.OTEL_EXPORTER_OTLP_PROTOCOL?.trim();
133+
134+
const exportIntervalMillis = getValueInMillis(
135+
'OTEL_METRIC_EXPORT_INTERVAL',
136+
60000
137+
);
138+
const exportTimeoutMillis = getValueInMillis(
139+
'OTEL_METRIC_EXPORT_TIMEOUT',
140+
30000
141+
);
142+
143+
switch (protocol) {
144+
case 'grpc':
145+
metricReaders.push(
146+
new PeriodicExportingMetricReader({
147+
exporter: new OTLPGrpcMetricExporter(),
148+
exportIntervalMillis: exportIntervalMillis,
149+
exportTimeoutMillis: exportTimeoutMillis,
150+
})
151+
);
152+
break;
153+
case 'http/json':
154+
metricReaders.push(
155+
new PeriodicExportingMetricReader({
156+
exporter: new OTLPHttpMetricExporter(),
157+
exportIntervalMillis: exportIntervalMillis,
158+
exportTimeoutMillis: exportTimeoutMillis,
159+
})
160+
);
161+
break;
162+
case 'http/protobuf':
163+
metricReaders.push(
164+
new PeriodicExportingMetricReader({
165+
exporter: new OTLPProtoMetricExporter(),
166+
exportIntervalMillis: exportIntervalMillis,
167+
exportTimeoutMillis: exportTimeoutMillis,
168+
})
169+
);
170+
break;
171+
default:
172+
diag.warn(
173+
`Unsupported OTLP metrics protocol: "${protocol}". Using http/protobuf.`
174+
);
175+
metricReaders.push(
176+
new PeriodicExportingMetricReader({
177+
exporter: new OTLPProtoMetricExporter(),
178+
exportIntervalMillis: exportIntervalMillis,
179+
exportTimeoutMillis: exportTimeoutMillis,
180+
})
181+
);
182+
}
183+
} else if (exporter === 'console') {
184+
metricReaders.push(
185+
new PeriodicExportingMetricReader({
186+
exporter: new ConsoleMetricExporter(),
187+
})
188+
);
189+
} else if (exporter === 'prometheus') {
190+
metricReaders.push(new PrometheusMetricExporter());
191+
} else {
192+
diag.warn(
193+
`Unsupported OTEL_METRICS_EXPORTER value: "${exporter}". Supported values are: otlp, console, prometheus, none.`
194+
);
195+
}
196+
});
197+
198+
return metricReaders;
199+
}
89200
export class NodeSDK {
90201
private _tracerProviderConfig?: {
91202
tracerConfig: NodeTracerConfig;
@@ -290,11 +401,18 @@ export class NodeSDK {
290401
logs.setGlobalLoggerProvider(loggerProvider);
291402
}
292403

293-
if (this._meterProviderConfig) {
404+
const metricReadersFromEnv: MetricReader[] =
405+
configureMetricProviderFromEnv();
406+
if (this._meterProviderConfig || metricReadersFromEnv.length > 0) {
294407
const readers: MetricReader[] = [];
295-
if (this._meterProviderConfig.reader) {
408+
if (this._meterProviderConfig?.reader) {
296409
readers.push(this._meterProviderConfig.reader);
297410
}
411+
412+
if (readers.length === 0) {
413+
metricReadersFromEnv.forEach((r: MetricReader) => readers.push(r));
414+
}
415+
298416
const meterProvider = new MeterProvider({
299417
resource: this._resource,
300418
views: this._meterProviderConfig?.views ?? [],
@@ -303,7 +421,6 @@ export class NodeSDK {
303421
});
304422

305423
this._meterProvider = meterProvider;
306-
307424
metrics.setGlobalMeterProvider(meterProvider);
308425

309426
// TODO: This is a workaround to fix https://github.com/open-telemetry/opentelemetry-js/issues/3609

0 commit comments

Comments
 (0)