LCOV - code coverage report
Current view: top level - lib/src/metrics/export/otlp - metric_transformer.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 99.1 % 108 107
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 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart'
       5              :     show OTelLog;
       6              : import 'package:fixnum/fixnum.dart';
       7              : 
       8              : import '../../../../proto/common/v1/common.pb.dart' as common_proto;
       9              : import '../../../../proto/metrics/v1/metrics.pb.dart' as proto;
      10              : import '../../../../proto/resource/v1/resource.pb.dart' as resource_proto;
      11              : import '../../../resource/resource.dart';
      12              : import '../../data/metric.dart';
      13              : import '../../data/metric_point.dart';
      14              : 
      15              : /// Utility class for transforming metric data to OTLP protobuf format.
      16              : class MetricTransformer {
      17              :   /// Transforms a Resource to an OTLP Resource proto.
      18            9 :   static resource_proto.Resource transformResource(Resource resource) {
      19            9 :     final resourceProto = resource_proto.Resource();
      20            9 :     final attributes = resource.attributes;
      21              : 
      22           18 :     resourceProto.attributes.addAll(
      23           27 :       attributes.toMap().entries.map(
      24           25 :             (entry) => _createKeyValue(entry.key, entry.value.value),
      25              :           ),
      26              :     );
      27              : 
      28              :     return resourceProto;
      29              :   }
      30              : 
      31              :   /// Transforms a Metric to an OTLP Metric proto.
      32            8 :   static proto.Metric transformMetric(Metric metric) {
      33            8 :     final metricProto = proto.Metric();
      34           16 :     metricProto.name = metric.name;
      35              : 
      36            8 :     if (metric.description != null) {
      37           10 :       metricProto.description = metric.description!;
      38              :     }
      39              : 
      40            8 :     if (metric.unit != null) {
      41           10 :       metricProto.unit = metric.unit!;
      42              :     }
      43              : 
      44            8 :     if (OTelLog.isLogMetrics()) {
      45            8 :       OTelLog.logMetric(
      46           24 :         'MetricTransformer: Transforming metric ${metric.name} of type ${metric.type}',
      47              :       );
      48              :     }
      49              : 
      50              :     // Set data based on metric type
      51            8 :     switch (metric.type) {
      52            8 :       case MetricType.histogram:
      53              :         // Histogram metric
      54            3 :         final histogramDataPoints = <proto.HistogramDataPoint>[];
      55            6 :         for (final point in metric.points) {
      56            6 :           if (point.value is HistogramValue) {
      57            3 :             final dataPoint = _createHistogramDataPoint(point);
      58            3 :             histogramDataPoints.add(dataPoint);
      59              :           }
      60              :         }
      61              : 
      62              :         // Create a new histogram with the correct temporality and data points
      63            3 :         final histogram = proto.Histogram(
      64            6 :           aggregationTemporality: metric.temporality ==
      65              :                   AggregationTemporality.delta
      66              :               ? proto.AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA
      67              :               : proto.AggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE,
      68              :           dataPoints: histogramDataPoints,
      69              :         );
      70              : 
      71            3 :         metricProto.histogram = histogram;
      72              :         break;
      73              : 
      74            8 :       case MetricType.sum:
      75              :         // Sum metric
      76            8 :         final numberDataPoints = <proto.NumberDataPoint>[];
      77           16 :         for (final point in metric.points) {
      78            8 :           final dataPoint = _createNumberDataPoint(point);
      79            8 :           numberDataPoints.add(dataPoint);
      80              :         }
      81              : 
      82              :         // Create a new sum with the correct temporality and data points
      83            8 :         final sum = proto.Sum(
      84            8 :           isMonotonic: metric.isMonotonic ??
      85              :               true, // Assuming sum metrics are monotonic by default
      86           16 :           aggregationTemporality: metric.temporality ==
      87              :                   AggregationTemporality.delta
      88              :               ? proto.AggregationTemporality.AGGREGATION_TEMPORALITY_DELTA
      89              :               : proto.AggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE,
      90              :           dataPoints: numberDataPoints,
      91              :         );
      92              : 
      93            8 :         metricProto.sum = sum;
      94              :         break;
      95              : 
      96            4 :       case MetricType.gauge:
      97              :         // Gauge metric
      98            4 :         final numberDataPoints = <proto.NumberDataPoint>[];
      99            8 :         for (final point in metric.points) {
     100            4 :           final dataPoint = _createNumberDataPoint(point);
     101            4 :           numberDataPoints.add(dataPoint);
     102              :         }
     103              : 
     104              :         // Create a new gauge with the data points
     105            4 :         final gauge = proto.Gauge(dataPoints: numberDataPoints);
     106            4 :         metricProto.gauge = gauge;
     107              :         break;
     108              :     }
     109              : 
     110              :     return metricProto;
     111              :   }
     112              : 
     113              :   /// Creates a histogram data point for the given MetricPoint.
     114            3 :   static proto.HistogramDataPoint _createHistogramDataPoint(
     115              :     MetricPoint<dynamic> point,
     116              :   ) {
     117            3 :     final histogramValue = point.value as HistogramValue;
     118              : 
     119              :     // Prepare attributes
     120            6 :     final attributes = point.attributes.toMap();
     121            3 :     final attributeKeyValues = attributes.entries
     122           13 :         .map((entry) => _createKeyValue(entry.key, entry.value.value))
     123            3 :         .toList();
     124              : 
     125              :     // Prepare exemplars if available
     126            3 :     final exemplars = <proto.Exemplar>[];
     127            3 :     if (point.hasExemplars) {
     128            3 :       for (final exemplar in point.exemplars!) {
     129            1 :         final exemplarProto = proto.Exemplar(
     130            4 :           timeUnixNano: Int64(exemplar.timestamp.microsecondsSinceEpoch * 1000),
     131            2 :           asDouble: exemplar.value.toDouble(),
     132              :         );
     133            1 :         exemplars.add(exemplarProto);
     134              :       }
     135              :     }
     136              : 
     137              :     // Create bucket counts as Int64 list
     138              :     final bucketCountsInt64 =
     139            9 :         histogramValue.bucketCounts.map(Int64.new).toList();
     140              : 
     141              :     // Create the HistogramDataPoint with all fields set
     142            3 :     return proto.HistogramDataPoint(
     143              :       attributes: attributeKeyValues,
     144           12 :       startTimeUnixNano: Int64(point.startTime.microsecondsSinceEpoch * 1000),
     145           12 :       timeUnixNano: Int64(point.endTime.microsecondsSinceEpoch * 1000),
     146            6 :       count: Int64(histogramValue.count),
     147            6 :       sum: histogramValue.sum.toDouble(),
     148              :       bucketCounts: bucketCountsInt64,
     149            6 :       explicitBounds: List<double>.from(histogramValue.boundaries),
     150              :       exemplars: exemplars,
     151            6 :       min: histogramValue.min?.toDouble(),
     152            6 :       max: histogramValue.max?.toDouble(),
     153              :     );
     154              :   }
     155              : 
     156              :   /// Creates a number data point for the given MetricPoint.
     157            8 :   static proto.NumberDataPoint _createNumberDataPoint(
     158              :     MetricPoint<dynamic> point,
     159              :   ) {
     160              :     // Prepare attributes
     161           16 :     final attributes = point.attributes.toMap();
     162            8 :     final attributeKeyValues = attributes.entries
     163           38 :         .map((entry) => _createKeyValue(entry.key, entry.value.value))
     164            8 :         .toList();
     165              : 
     166              :     // Prepare exemplars if available
     167            8 :     final exemplars = <proto.Exemplar>[];
     168            8 :     if (point.hasExemplars) {
     169            3 :       for (final exemplar in point.exemplars!) {
     170            1 :         final exemplarProto = proto.Exemplar(
     171            4 :           timeUnixNano: Int64(exemplar.timestamp.microsecondsSinceEpoch * 1000),
     172            2 :           asDouble: exemplar.value.toDouble(),
     173              :         );
     174            1 :         exemplars.add(exemplarProto);
     175              :       }
     176              :     }
     177              : 
     178              :     // Create the NumberDataPoint with all fields set
     179            8 :     return proto.NumberDataPoint(
     180              :       attributes: attributeKeyValues,
     181           32 :       startTimeUnixNano: Int64(point.startTime.microsecondsSinceEpoch * 1000),
     182           32 :       timeUnixNano: Int64(point.endTime.microsecondsSinceEpoch * 1000),
     183           16 :       asDouble: (point.value is num)
     184           16 :           ? (point.value as num).toDouble()
     185            3 :           : double.tryParse(point.value.toString()) ?? 0.0,
     186              :       exemplars: exemplars,
     187              :     );
     188              :   }
     189              : 
     190              :   /// Creates a KeyValue proto from a key and value.
     191            9 :   static common_proto.KeyValue _createKeyValue(String key, dynamic value) {
     192            9 :     final keyValue = common_proto.KeyValue();
     193            9 :     keyValue.key = key;
     194              : 
     195            9 :     if (value is String) {
     196           18 :       keyValue.value = common_proto.AnyValue(stringValue: value);
     197            2 :     } else if (value is bool) {
     198            4 :       keyValue.value = common_proto.AnyValue(boolValue: value);
     199            2 :     } else if (value is int) {
     200            6 :       keyValue.value = common_proto.AnyValue(intValue: Int64(value));
     201            2 :     } else if (value is double) {
     202            4 :       keyValue.value = common_proto.AnyValue(doubleValue: value);
     203            1 :     } else if (value is List) {
     204            1 :       final arrayValue = common_proto.ArrayValue();
     205            2 :       for (final item in value) {
     206            1 :         final anyValue = common_proto.AnyValue();
     207            1 :         if (item is String) {
     208            1 :           anyValue.stringValue = item;
     209            1 :         } else if (item is bool) {
     210            1 :           anyValue.boolValue = item;
     211            1 :         } else if (item is int) {
     212            2 :           anyValue.intValue = Int64(item);
     213            1 :         } else if (item is double) {
     214            1 :           anyValue.doubleValue = item;
     215              :         }
     216            2 :         arrayValue.values.add(anyValue);
     217              :       }
     218            2 :       keyValue.value = common_proto.AnyValue(arrayValue: arrayValue);
     219              :     } else {
     220              :       // Default to string representation for unsupported types
     221            0 :       keyValue.value = common_proto.AnyValue(stringValue: value.toString());
     222              :     }
     223              : 
     224              :     return keyValue;
     225              :   }
     226              : }
        

Generated by: LCOV version 2.0-1