-
Notifications
You must be signed in to change notification settings - Fork 351
feat(otel): add support for otel metrics api via protobuf and json #6783
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 47 commits
a851ce1
181762c
e98ba66
9787d56
52a81ee
00a859b
2144f7c
73b2127
9a1b0b2
5c9bd42
2267339
8ed35ff
2e7062f
029155e
272ea27
f859b4b
3923982
463baf1
11e76ac
e60a8a3
9841b8a
1dda3b4
8d7c684
fec1a7d
a0e2878
af5d2e1
c94d94e
24241c6
d743d5c
c431bd7
790835e
c7c1fb3
e6d8332
6201c19
83a943b
394f598
cf1b8e6
d24e282
76ee397
408bfd7
1722dec
c191762
c6fb9af
f995693
880c8e1
4d79bd5
822e4dc
f908778
dbd3d72
7eace72
a0063f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -476,6 +476,7 @@ class Config { | |
| DD_INSTRUMENTATION_CONFIG_ID, | ||
| DD_LOGS_INJECTION, | ||
| DD_LOGS_OTEL_ENABLED, | ||
| DD_METRICS_OTEL_ENABLED, | ||
| DD_LANGCHAIN_SPAN_CHAR_LIMIT, | ||
| DD_LANGCHAIN_SPAN_PROMPT_COMPLETION_SAMPLE_RATE, | ||
| DD_LLMOBS_AGENTLESS_ENABLED, | ||
|
|
@@ -570,12 +571,19 @@ class Config { | |
| OTEL_EXPORTER_OTLP_LOGS_HEADERS, | ||
| OTEL_EXPORTER_OTLP_LOGS_PROTOCOL, | ||
| OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, | ||
| OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, | ||
| OTEL_EXPORTER_OTLP_METRICS_HEADERS, | ||
| OTEL_EXPORTER_OTLP_METRICS_PROTOCOL, | ||
| OTEL_EXPORTER_OTLP_METRICS_TIMEOUT, | ||
| OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, | ||
| OTEL_METRIC_EXPORT_TIMEOUT, | ||
| OTEL_EXPORTER_OTLP_PROTOCOL, | ||
| OTEL_EXPORTER_OTLP_ENDPOINT, | ||
| OTEL_EXPORTER_OTLP_HEADERS, | ||
| OTEL_EXPORTER_OTLP_TIMEOUT, | ||
| OTEL_BSP_SCHEDULE_DELAY, | ||
| OTEL_BSP_MAX_EXPORT_BATCH_SIZE | ||
| OTEL_BSP_MAX_EXPORT_BATCH_SIZE, | ||
| OTEL_METRIC_EXPORT_INTERVAL | ||
| } = source | ||
|
|
||
| const tags = {} | ||
|
|
@@ -606,6 +614,26 @@ class Config { | |
| target.otelLogsTimeout = maybeInt(OTEL_EXPORTER_OTLP_LOGS_TIMEOUT) || target.otelTimeout | ||
| target.otelLogsBatchTimeout = maybeInt(OTEL_BSP_SCHEDULE_DELAY) | ||
| target.otelLogsMaxExportBatchSize = maybeInt(OTEL_BSP_MAX_EXPORT_BATCH_SIZE) | ||
|
|
||
| const otelMetricsExporter = String(OTEL_METRICS_EXPORTER).toLowerCase() !== 'none' | ||
mabdinur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.#setBoolean(target, 'otelMetricsEnabled', DD_METRICS_OTEL_ENABLED && otelMetricsExporter) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have some documentation about the none exporter deactivating the metrics? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's documented here but we can definetly do a better job here: https://docs.datadoghq.com/opentelemetry/config/environment_variable_support/ 😅 |
||
| // Set OpenTelemetry metrics configuration with specific _METRICS_ vars | ||
| // taking precedence over generic _EXPORTERS_ vars | ||
| if (OTEL_EXPORTER_OTLP_ENDPOINT || OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) { | ||
| this.#setString(target, 'otelMetricsUrl', OTEL_EXPORTER_OTLP_METRICS_ENDPOINT || target.otelUrl) | ||
| } | ||
| this.#setString(target, 'otelMetricsHeaders', OTEL_EXPORTER_OTLP_METRICS_HEADERS || target.otelHeaders) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: these will mess with telemetry values for now, while that is an issue in lots of places and it will be resolved in another PR where we fix the telemetry (the issue is that the property will be defined by either another property or the env and that can not be differentiated for the telemetry being defined like that). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll keep an eye on this |
||
| this.#setString(target, 'otelMetricsProtocol', OTEL_EXPORTER_OTLP_METRICS_PROTOCOL || target.otelProtocol) | ||
| target.otelMetricsTimeout = maybeInt(OTEL_EXPORTER_OTLP_METRICS_TIMEOUT) || target.otelTimeout | ||
| target.otelMetricsExportTimeout = maybeInt(OTEL_METRIC_EXPORT_TIMEOUT) | ||
| target.otelMetricsExportInterval = maybeInt(OTEL_METRIC_EXPORT_INTERVAL) | ||
|
||
| // Parse temporality preference (default to DELTA for Datadog) | ||
| if (OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE) { | ||
| const temporalityPref = OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE.toUpperCase() | ||
| if (['DELTA', 'CUMULATIVE', 'LOWMEMORY'].includes(temporalityPref)) { | ||
| this.#setString(target, 'otelMetricsTemporalityPreference', temporalityPref) | ||
| } | ||
| } | ||
| this.#setBoolean( | ||
| target, | ||
| 'apmTracingEnabled', | ||
|
|
@@ -1200,9 +1228,10 @@ class Config { | |
|
|
||
| calc['dogstatsd.hostname'] = this.#getHostname() | ||
|
|
||
| // Compute OTLP logs URL to send payloads to the active Datadog Agent | ||
| // Compute OTLP logs and metrics URLs to send payloads to the active Datadog Agent | ||
| const agentHostname = this.#getHostname() | ||
| calc.otelLogsUrl = `http://${agentHostname}:${DEFAULT_OTLP_PORT}` | ||
| calc.otelMetricsUrl = `http://${agentHostname}:${DEFAULT_OTLP_PORT}/v1/metrics` | ||
mabdinur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| calc.otelUrl = `http://${agentHostname}:${DEFAULT_OTLP_PORT}` | ||
|
|
||
| this.#setBoolean(calc, 'isGitUploadEnabled', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,6 +140,14 @@ module.exports = { | |
| otelTimeout: 10_000, | ||
| otelLogsBatchTimeout: 5000, | ||
| otelLogsMaxExportBatchSize: 512, | ||
| otelMetricsEnabled: false, | ||
| otelMetricsUrl: undefined, // Will be computed using agent host | ||
| otelMetricsHeaders: '', | ||
| otelMetricsProtocol: 'http/protobuf', | ||
| otelMetricsTimeout: 10_000, | ||
| otelMetricsExportTimeout: 7500, | ||
| otelMetricsExportInterval: 10_000, | ||
| otelMetricsTemporalityPreference: 'DELTA', // DELTA, CUMULATIVE, or LOWMEMORY | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These values are not documented as far as I know. Should we list them in the API.md? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The config is documented in api docs but it's very high level. I will update the api docs to include this link: https://opentelemetry.io/docs/specs/otel/metrics/sdk_exporters/otlp/#additional-environment-variable-configuration |
||
| lookup: undefined, | ||
| inferredProxyServicesEnabled: false, | ||
| memcachedCommandEnabled: false, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| 'use strict' | ||
|
|
||
| // Metric type constants | ||
| const METRIC_TYPES = { | ||
| HISTOGRAM: 'histogram', | ||
| COUNTER: 'counter', | ||
| UPDOWNCOUNTER: 'updowncounter', | ||
| OBSERVABLECOUNTER: 'observable-counter', | ||
| OBSERVABLEUPDOWNCOUNTER: 'observable-updowncounter', | ||
| GAUGE: 'gauge' | ||
| } | ||
|
|
||
| // Temporality constants | ||
| const TEMPORALITY = { | ||
| DELTA: 'DELTA', | ||
| CUMULATIVE: 'CUMULATIVE', | ||
| GAUGE: 'GAUGE', | ||
| LOWMEMORY: 'LOWMEMORY' | ||
| } | ||
|
|
||
| // Default histogram bucket boundaries (in milliseconds for latency metrics) | ||
| const DEFAULT_HISTOGRAM_BUCKETS = [0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10_000] | ||
|
|
||
| module.exports = { | ||
| METRIC_TYPES, | ||
| TEMPORALITY, | ||
| DEFAULT_HISTOGRAM_BUCKETS | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| 'use strict' | ||
|
|
||
| const os = require('os') | ||
|
|
||
| /** | ||
| * @typedef {import('../../config')} Config | ||
| */ | ||
|
|
||
| /** | ||
| * @fileoverview OpenTelemetry Metrics Implementation for dd-trace-js | ||
| * | ||
| * This package provides a custom OpenTelemetry Metrics implementation that integrates | ||
| * with the Datadog tracing library. It includes all necessary components for | ||
| * creating instruments, recording measurements, and exporting metrics via OTLP. | ||
| * | ||
| * Key Components: | ||
| * - MeterProvider: Main entry point for creating meters | ||
| * - Meter: Provides methods to create metric instruments | ||
| * - Instruments: Gauge, Counter, UpDownCounter, ObservableGauge, ObservableCounter, ObservableUpDownCounter, Histogram | ||
| * - PeriodicMetricReader: Collects and exports instruments (metrics) at regular intervals | ||
| * - OtlpHttpMetricExporter: Exports instruments (metrics) via OTLP over HTTP | ||
| * - OtlpTransformer: Transforms instruments (metrics) to OTLP format | ||
| * | ||
| * This is a custom implementation to avoid pulling in the full OpenTelemetry SDK, | ||
| * based on OTLP Protocol v1.7.0. It supports both protobuf and JSON serialization | ||
| * formats and integrates with Datadog's configuration system. | ||
| * | ||
| * @package | ||
| */ | ||
|
|
||
| const MeterProvider = require('./meter_provider') | ||
| const PeriodicMetricReader = require('./periodic_metric_reader') | ||
| const OtlpHttpMetricExporter = require('./otlp_http_metric_exporter') | ||
|
|
||
| /** | ||
| * Initializes OpenTelemetry Metrics support | ||
| * @param {Config} config - Tracer configuration instance | ||
| */ | ||
| function initializeOpenTelemetryMetrics (config) { | ||
| // Build resource attributes | ||
| const resourceAttributes = { | ||
| 'service.name': config.service, | ||
| 'service.version': config.version, | ||
| 'deployment.environment': config.env | ||
| } | ||
|
|
||
| if (config.tags) { | ||
| const filteredTags = { ...config.tags } | ||
| delete filteredTags.service | ||
| delete filteredTags.version | ||
| delete filteredTags.env | ||
| Object.assign(resourceAttributes, filteredTags) | ||
| } | ||
|
|
||
| if (config.reportHostname) { | ||
| resourceAttributes['host.name'] = os.hostname() | ||
| } | ||
|
|
||
| const exporter = new OtlpHttpMetricExporter( | ||
| config.otelMetricsUrl, | ||
| config.otelMetricsHeaders, | ||
| config.otelMetricsTimeout, | ||
| config.otelMetricsProtocol, | ||
| resourceAttributes | ||
| ) | ||
|
|
||
| const reader = new PeriodicMetricReader( | ||
| exporter, | ||
| config.otelMetricsExportInterval, | ||
| config.otelMetricsTemporalityPreference | ||
| ) | ||
|
|
||
| const meterProvider = new MeterProvider({ reader }) | ||
|
|
||
| meterProvider.register() | ||
| } | ||
|
|
||
| module.exports = { | ||
| MeterProvider, | ||
| initializeOpenTelemetryMetrics | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The text is totally fine, I think it would just be more straight forward in case the individual configuration has all entries listed right away instead of having a separate section with a longer text that describes that.
I would therefore just inline this content into the above variables besides the parts that apply across multiple envs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I got rid of this paragraph and inlined it with the configs.