Skip to content

otlp-transformer: malformed metric export if valueType is not specified #6110

@constb

Description

@constb

What happened?

Steps to Reproduce

I'm making a custom MetricProducer which is added to the Opentelemetry Node.js SDK (@opentelemtry/sdk-node) by adding it to metric reader: new PeriodicExportingMetricReader({ exporter, metricProducers: [customMetricsProducer] });

In my specific case, MetricDescriptor type was not correctly applied, when creating metric datapoints for export (simplified for clarity):

function mapMetricData(metric: MyMetricData): OtelMetricData {
  const descriptor = {
    name: metric.name,
    unit: '',
  };

  const dataPoints = metric.dataPoints.map((point: DataPoint) => ({
    startTime: bigintToHrTime(point.startTime),
    endTime: bigintToHrTime(point.endTime),
    attributes: point.attributes,
    // OTel SDK doesn't support BigInts, waiting for https://github.com/open-telemetry/opentelemetry-js/issues/3014
    value: typeof point.value === 'bigint' ? Number(point.value) : point.value,
  }));

  switch (metric.name) {
    case 'calls':
      return {
        descriptor,
        dataPointType: DataPointType.SUM,
        isMonotonic: true,
        aggregationTemporality: AggregationTemporality.DELTA,
        dataPoints,
      } as SumMetricData;
    // ... [skipped]
  }
}

Expected Result

I would prefer serializer to default valueType to ValueType.DOUBLE since metrics currently only allow number as data point values. After #3014 gets implemented, valueType should be inferred from value: DOUBLE for numbers and INT for bigints.

Another option is to produce an error in toSingularDataPoint:

This error should either be thrown or logged (skipping the malformed datapoint).

Actual Result

No errors thrown, value is missing in exported metric data.

OpenTelemetry Setup Code

Custom `register` script that mostly is a fork of https://github.com/elastic/elastic-otel-node

package.json

{
  "dependencies": {
    "@elastic/opamp-client-node": "0.2.0",
    "@fastify/otel": "0.9.4",
    "@opentelemetry/api": "1.9.0",
    "@opentelemetry/core": "2.0.1",
    "@opentelemetry/exporter-logs-otlp-grpc": "0.203.0",
    "@opentelemetry/exporter-logs-otlp-http": "0.203.0",
    "@opentelemetry/exporter-logs-otlp-proto": "0.203.0",
    "@opentelemetry/exporter-metrics-otlp-grpc": "0.203.0",
    "@opentelemetry/exporter-metrics-otlp-http": "0.203.0",
    "@opentelemetry/exporter-metrics-otlp-proto": "0.203.0",
    "@opentelemetry/exporter-trace-otlp-grpc": "0.203.0",
    "@opentelemetry/exporter-trace-otlp-http": "0.203.0",
    "@opentelemetry/exporter-trace-otlp-proto": "0.203.0",
    "@opentelemetry/host-metrics": "0.36.0",
    "@opentelemetry/instrumentation": "0.203.0",
    "@opentelemetry/instrumentation-amqplib": "0.50.0",
    "@opentelemetry/instrumentation-aws-sdk": "0.57.0",
    "@opentelemetry/instrumentation-express": "0.52.0",
    "@opentelemetry/instrumentation-graphql": "0.51.0",
    "@opentelemetry/instrumentation-grpc": "0.203.0",
    "@opentelemetry/instrumentation-http": "0.203.0",
    "@opentelemetry/instrumentation-ioredis": "0.51.0",
    "@opentelemetry/instrumentation-kafkajs": "0.13.0",
    "@opentelemetry/instrumentation-koa": "0.51.0",
    "@opentelemetry/instrumentation-mongodb": "0.56.0",
    "@opentelemetry/instrumentation-mysql": "0.49.0",
    "@opentelemetry/instrumentation-mysql2": "0.50.0",
    "@opentelemetry/instrumentation-nestjs-core": "0.49.0",
    "@opentelemetry/instrumentation-pg": "0.56.0",
    "@opentelemetry/instrumentation-pino": "0.50.0",
    "@opentelemetry/instrumentation-redis": "0.51.0",
    "@opentelemetry/instrumentation-runtime-node": "0.17.1",
    "@opentelemetry/instrumentation-socket.io": "0.50.0",
    "@opentelemetry/instrumentation-typeorm": "0.4.0",
    "@opentelemetry/instrumentation-undici": "0.14.0",
    "@opentelemetry/resource-detector-aws": "2.3.0",
    "@opentelemetry/resource-detector-container": "0.7.3",
    "@opentelemetry/sdk-metrics": "2.0.1",
    "@opentelemetry/sdk-node": "0.203.0",
    "@opentelemetry/semantic-conventions": "1.36.0",
    "import-in-the-middle": "1.14.2"
  }
}

Relevant log output

{
          "scope": {
            "name": "custom-metrics/nestjs.http.server",
            "version": "1.0.1-js"
          },
          "metrics": [
            {
              "name": "calls",
              "unit": "",
              "sum": {
                "aggregationTemporality": 1,
                "isMonotonic": true,
                "dataPoints": [
                  {
                    "attributes": [
                      {
                        "key": "http.method",
                        "value": {
                          "stringValue": "POST"
                        }
                      },
                      {
                        "key": "http.route",
                        "value": {
                          "stringValue": "/favorites/get"
                        }
                      },
                      {
                        "key": "http.status_code",
                        "value": {
                          "stringValue": "200"
                        }
                      },
                      {
                        "key": "error.type",
                        "value": {
                          "stringValue": "none"
                        }
                      },
                      {
                        "key": "operation",
                        "value": {
                          "stringValue": "request"
                        }
                      }
                    ],
                    "startTimeUnixNano": "1763050060442053467",
                    "timeUnixNano": "1763050064470163086"
                  }
                ]
              }
            },
...

Operating System and Version

macOS 15.7.2

Runtime and Version

Node.js v24.10.0

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtriage

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions