@@ -35,57 +35,72 @@ pub const OTLPExporter = struct {
35
35
allocator : std.mem.Allocator ,
36
36
exporter : ExporterImpl ,
37
37
38
- temporailty : view.TemporalitySelector ,
38
+ temporality : view.TemporalitySelector ,
39
+ config : * otlp.ConfigOptions ,
40
+
41
+ pub fn init (allocator : std.mem.Allocator , config : * otlp.ConfigOptions , temporality : view.TemporalitySelector ) ! * Self {
42
+ const s = try allocator .create (Self );
43
+ s .* = Self {
44
+ .allocator = allocator ,
45
+ .exporter = ExporterImpl {
46
+ .exportFn = exportBatch ,
47
+ },
48
+ .temporality = temporality ,
49
+ .config = config ,
50
+ };
51
+ return s ;
52
+ }
39
53
40
- config : otlp.ConfigOptions ,
54
+ pub fn deinit (self : * Self ) void {
55
+ self .allocator .destroy (self );
56
+ }
41
57
42
58
pub fn exportBatch (iface : * ExporterImpl , data : []Measurements ) MetricReadError ! void {
43
59
// Get a pointer to the instance of the struct that implements the interface.
44
60
const self : * Self = @fieldParentPtr ("exporter" , iface );
45
-
46
- // TODO: implement the OTLP exporter.
47
- // Processing pipeline:
48
- // 1. convert the measurements to protobuf format (clear up the datapoints after reading them).
49
- // 2. create an HTTP or gRPC client.
50
- // 3. send the data to the endpoint.
51
- // 4. handle the response and potential retries (exp backoff).
52
-
61
+ // Cleanup the data after use, it is mandatory for all exporters as they own the data argument.
53
62
defer {
54
63
for (data ) | * m | {
55
64
m .deinit (self .allocator );
56
65
}
57
66
self .allocator .free (data );
58
67
}
59
- var resource_metrics = try self .allocator .alloc (pbmetrics .ResourceMetrics , 1 );
68
+ var resource_metrics = self .allocator .alloc (pbmetrics .ResourceMetrics , 1 ) catch | err | {
69
+ std .debug .print ("OTLP export failed to allocate memory for resource metrics: {s}\n " , .{@errorName (err )});
70
+ return MetricReadError .OutOfMemory ;
71
+ };
60
72
61
73
var scope_metrics = try self .allocator .alloc (pbmetrics .ScopeMetrics , data .len );
62
74
for (data , 0.. ) | measurement , i | {
75
+ const metrics = std .ArrayList (pbmetrics .Metric ).initCapacity (self .allocator , 1 ) catch | err | {
76
+ std .debug .print ("OTLP export failed to allocate memory for metrics: {s}\n " , .{@errorName (err )});
77
+ return MetricReadError .OutOfMemory ;
78
+ };
79
+ metrics .items [0 ] = try toProtobufMetric (self .allocator , measurement , self .temporality );
80
+ const attributes = try attributesToProtobufKeyValueList (self .allocator , measurement .meterAttributes );
63
81
scope_metrics [i ] = pbmetrics.ScopeMetrics {
64
82
.scope = pbcommon.InstrumentationScope {
65
83
.name = ManagedString .managed (measurement .meterName ),
66
84
.version = if (measurement .meterVersion ) | version | ManagedString .managed (version ) else .Empty ,
67
- // .schema_url = if (measurement.meterSchemaUrl) |s| ManagedString.managed(s) else .Empty,
68
- .attributes = try attributesToProtobufKeyValueList (self .allocator , measurement .meterAttributes ),
85
+ .attributes = attributes .values ,
69
86
},
70
87
.schema_url = if (measurement .meterSchemaUrl ) | s | ManagedString .managed (s ) else .Empty ,
71
- .metrics = try toProtobufMetric ( self . allocator , measurement , self . temporailty ( measurement . instrumentKind )) ,
88
+ .metrics = metrics ,
72
89
};
73
90
}
74
91
resource_metrics [0 ] = pbmetrics.ResourceMetrics {
75
92
.resource = null , //FIXME support resource attributes
76
- .scope_metrics = scope_metrics ,
93
+ .scope_metrics = std . ArrayList ( pbmetrics . ScopeMetrics ). fromOwnedSlice ( self . allocator , scope_metrics ) ,
77
94
.schema_url = .Empty ,
78
95
};
79
96
80
97
const metrics_data = pbmetrics.MetricsData {
81
98
.resource_metrics = std .ArrayList (pbmetrics .ResourceMetrics ).fromOwnedSlice (self .allocator , resource_metrics ),
82
99
};
83
- defer metrics_data .deinit (self . allocator );
100
+ defer metrics_data .deinit ();
84
101
85
- // TODO: offload the data the the OTLP transport.
86
- // Problem is: the OTLP transport should be the one in charge of detecting the encoding via the configuration.
87
- otlp .Export (self .config , otlp.Signal { .metrics = metrics_data }) catch | err | {
88
- std .debug .print ("OTLP export failed: {s}" , .{@tagName (err )});
102
+ otlp .Export (self .allocator , self .config , otlp.Signal.Data { .metrics = metrics_data }) catch | err | {
103
+ std .debug .print ("OTLP export failed: {s}" , .{@errorName (err )});
89
104
return MetricReadError .ExportFailed ;
90
105
};
91
106
}
@@ -298,3 +313,12 @@ test "exporters/otlp conversion for HistogramDataPoint" {
298
313
metric .data .? .histogram .data_points .items [0 ].attributes .items [0 ].value .? .value .? .string_value ,
299
314
});
300
315
}
316
+
317
+ test "exporters/otlp init/deinit" {
318
+ const allocator = std .testing .allocator ;
319
+ const config = try otlp .ConfigOptions .init (allocator );
320
+ defer config .deinit ();
321
+
322
+ var exporter = try OTLPExporter .init (allocator , config , view .DefaultTemporality );
323
+ defer exporter .deinit ();
324
+ }
0 commit comments