LCOV - code coverage report
Current view: top level - lib/src/export - otlp_json.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 93.3 % 15 14
Test Date: 2026-07-11 13:35:19 Functions: - 0 0

            Line data    Source code
       1              : // Copyright The OpenTelemetry Authors
       2              : // SPDX-License-Identifier: Apache-2.0
       3              : 
       4              : import 'dart:convert';
       5              : 
       6              : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart'
       7              :     show IdGenerator;
       8              : import 'package:protobuf/protobuf.dart';
       9              : 
      10              : /// OTLP/JSON encoding of an OTLP request message.
      11              : ///
      12              : /// OTLP/JSON is proto3-JSON **with explicit deviations** — the one that bites
      13              : /// is ID encoding: the OTLP specification requires `traceId`, `spanId`, and
      14              : /// `parentSpanId` to be **hex-encoded** strings (case-insensitive), NOT the
      15              : /// proto3-JSON default base64 for `bytes` fields. See
      16              : /// opentelemetry-proto's JSON Protobuf Encoding notes.
      17              : ///
      18              : /// `toProto3Json()` alone therefore produces payloads that strict OTLP
      19              : /// receivers reject — the OpenTelemetry Collector enforces hex from
      20              : /// contrib ~0.15x ("ID.UnmarshalJSONIter: length mismatch", HTTP 400); older
      21              : /// collectors happened to tolerate base64, which is how this survived in the
      22              : /// wild until the first strict endpoint (Dartastic Cloud, 2026-07-10).
      23              : ///
      24              : /// This helper converts the proto3-JSON tree, hex-encoding every ID field
      25              : /// wherever it appears (spans, span links, log records, metric exemplars).
      26            4 : Object? otlpProto3JsonWithHexIds(GeneratedMessage request) =>
      27            8 :     _hexifyIds(request.toProto3Json());
      28              : 
      29              : const _idKeys = {'traceId', 'spanId', 'parentSpanId'};
      30              : 
      31            4 : Object? _hexifyIds(Object? node) {
      32            4 :   if (node is Map) {
      33            4 :     return <String, Object?>{
      34            4 :       for (final entry in node.entries)
      35            8 :         entry.key as String:
      36           12 :             _idKeys.contains(entry.key) && entry.value is String
      37            4 :                 ? _base64ToHex(entry.value as String)
      38            8 :                 : _hexifyIds(entry.value),
      39              :     };
      40              :   }
      41            4 :   if (node is List) {
      42           12 :     return <Object?>[for (final item in node) _hexifyIds(item)];
      43              :   }
      44              :   return node;
      45              : }
      46              : 
      47              : /// Base64 → lowercase hex, via the same codec that formats
      48              : /// `TraceId.hexString`/`SpanId.hexString`. Defensive: a value that doesn't
      49              : /// parse as base64 is returned unchanged (never corrupt a payload we don't
      50              : /// understand).
      51            2 : String _base64ToHex(String b64) {
      52              :   try {
      53            4 :     return IdGenerator.bytesToHex(base64.decode(b64));
      54            0 :   } on FormatException {
      55              :     return b64;
      56              :   }
      57              : }
        

Generated by: LCOV version 2.0-1