LCOV - code coverage report
Current view: top level - src/metrics/export/otlp - metric_transformer.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 76.1 % 109 83
Test Date: 2025-11-15 13:23:01 Functions: - 0 0

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

Generated by: LCOV version 2.0-1