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 : }
|