17
17
import { ExportResult } from '@opentelemetry/base' ;
18
18
import { NoopLogger } from '@opentelemetry/core' ;
19
19
import {
20
- LabelValue ,
21
- MetricDescriptor ,
22
- MetricDescriptorType ,
23
20
MetricExporter ,
24
- ReadableMetric ,
21
+ MetricRecord ,
22
+ MetricDescriptor ,
23
+ LastValue ,
24
+ MetricKind ,
25
+ Sum ,
25
26
} from '@opentelemetry/metrics' ;
26
27
import * as types from '@opentelemetry/api' ;
27
28
import { createServer , IncomingMessage , Server , ServerResponse } from 'http' ;
28
29
import { Counter , Gauge , labelValues , Metric , Registry } from 'prom-client' ;
29
30
import * as url from 'url' ;
30
31
import { ExporterConfig } from './export/types' ;
32
+ import { LabelSet } from '@opentelemetry/metrics/build/src/LabelSet' ;
33
+ import { CounterSumAggregator } from '@opentelemetry/metrics/build/src/export/Aggregator' ;
31
34
32
35
export class PrometheusExporter implements MetricExporter {
33
36
static readonly DEFAULT_OPTIONS = {
@@ -81,13 +84,10 @@ export class PrometheusExporter implements MetricExporter {
81
84
* be a no-op and the exporter should reach into the metrics when the export endpoint is
82
85
* called. As there is currently no interface to do this, this is our only option.
83
86
*
84
- * @param readableMetrics Metrics to be sent to the prometheus backend
87
+ * @param records Metrics to be sent to the prometheus backend
85
88
* @param cb result callback to be called on finish
86
89
*/
87
- export (
88
- readableMetrics : ReadableMetric [ ] ,
89
- cb : ( result : ExportResult ) => void
90
- ) {
90
+ export ( records : MetricRecord [ ] , cb : ( result : ExportResult ) => void ) {
91
91
if ( ! this . _server ) {
92
92
// It is conceivable that the _server may not be started as it is an async startup
93
93
// However unlikely, if this happens the caller may retry the export
@@ -97,8 +97,8 @@ export class PrometheusExporter implements MetricExporter {
97
97
98
98
this . _logger . debug ( 'Prometheus exporter export' ) ;
99
99
100
- for ( const readableMetric of readableMetrics ) {
101
- this . _updateMetric ( readableMetric ) ;
100
+ for ( const record of records ) {
101
+ this . _updateMetric ( record ) ;
102
102
}
103
103
104
104
cb ( ExportResult . SUCCESS ) ;
@@ -117,51 +117,53 @@ export class PrometheusExporter implements MetricExporter {
117
117
/**
118
118
* Updates the value of a single metric in the registry
119
119
*
120
- * @param readableMetric Metric value to be saved
120
+ * @param record Metric value to be saved
121
121
*/
122
- private _updateMetric ( readableMetric : ReadableMetric ) {
123
- const metric = this . _registerMetric ( readableMetric ) ;
122
+ private _updateMetric ( record : MetricRecord ) {
123
+ const metric = this . _registerMetric ( record ) ;
124
124
if ( ! metric ) return ;
125
125
126
- const labelKeys = readableMetric . descriptor . labelKeys ;
126
+ const labelKeys = record . descriptor . labelKeys ;
127
+ const value = record . aggregator . value ( ) ;
127
128
128
129
if ( metric instanceof Counter ) {
129
- for ( const ts of readableMetric . timeseries ) {
130
- // Prometheus counter saves internal state and increments by given value.
131
- // ReadableMetric value is the current state, not the delta to be incremented by.
132
- // Currently, _registerMetric creates a new counter every time the value changes,
133
- // so the increment here behaves as a set value (increment from 0)
134
- metric . inc (
135
- this . _getLabelValues ( labelKeys , ts . labelValues ) ,
136
- ts . points [ 0 ] . value as number
137
- ) ;
138
- }
130
+ // Prometheus counter saves internal state and increments by given value.
131
+ // ReadableMetric value is the current state, not the delta to be incremented by.
132
+ // Currently, _registerMetric creates a new counter every time the value changes,
133
+ // so the increment here behaves as a set value (increment from 0)
134
+ metric . inc ( this . _getLabelValues ( labelKeys , record . labels ) , value as Sum ) ;
139
135
}
140
136
141
137
if ( metric instanceof Gauge ) {
142
- for ( const ts of readableMetric . timeseries ) {
138
+ if ( record . aggregator instanceof CounterSumAggregator ) {
139
+ metric . set (
140
+ this . _getLabelValues ( labelKeys , record . labels ) ,
141
+ value as Sum
142
+ ) ;
143
+ } else {
143
144
metric . set (
144
- this . _getLabelValues ( labelKeys , ts . labelValues ) ,
145
- ts . points [ 0 ] . value as number
145
+ this . _getLabelValues ( labelKeys , record . labels ) ,
146
+ ( value as LastValue ) . value
146
147
) ;
147
148
}
148
149
}
149
150
150
151
// TODO: only counter and gauge are implemented in metrics so far
151
152
}
152
153
153
- private _getLabelValues ( keys : string [ ] , values : LabelValue [ ] ) {
154
+ private _getLabelValues ( keys : string [ ] , values : LabelSet ) {
154
155
const labelValues : labelValues = { } ;
156
+ const labels = values . labels ;
155
157
for ( let i = 0 ; i < keys . length ; i ++ ) {
156
- if ( values [ i ] . value !== null ) {
157
- labelValues [ keys [ i ] ] = values [ i ] . value ! ;
158
+ if ( labels [ keys [ i ] ] !== null ) {
159
+ labelValues [ keys [ i ] ] = labels [ keys [ i ] ] ;
158
160
}
159
161
}
160
162
return labelValues ;
161
163
}
162
164
163
- private _registerMetric ( readableMetric : ReadableMetric ) : Metric | undefined {
164
- const metricName = this . _getPrometheusMetricName ( readableMetric . descriptor ) ;
165
+ private _registerMetric ( record : MetricRecord ) : Metric | undefined {
166
+ const metricName = this . _getPrometheusMetricName ( record . descriptor ) ;
165
167
const metric = this . _registry . getSingleMetric ( metricName ) ;
166
168
167
169
/**
@@ -177,31 +179,26 @@ export class PrometheusExporter implements MetricExporter {
177
179
this . _registry . removeSingleMetric ( metricName ) ;
178
180
} else if ( metric ) return metric ;
179
181
180
- return this . _newMetric ( readableMetric , metricName ) ;
182
+ return this . _newMetric ( record , metricName ) ;
181
183
}
182
184
183
- private _newMetric (
184
- readableMetric : ReadableMetric ,
185
- name : string
186
- ) : Metric | undefined {
185
+ private _newMetric ( record : MetricRecord , name : string ) : Metric | undefined {
187
186
const metricObject = {
188
187
name,
189
188
// prom-client throws with empty description which is our default
190
- help : readableMetric . descriptor . description || 'description missing' ,
191
- labelNames : readableMetric . descriptor . labelKeys ,
189
+ help : record . descriptor . description || 'description missing' ,
190
+ labelNames : record . descriptor . labelKeys ,
192
191
// list of registries to register the newly created metric
193
192
registers : [ this . _registry ] ,
194
193
} ;
195
194
196
- switch ( readableMetric . descriptor . type ) {
197
- case MetricDescriptorType . COUNTER_DOUBLE :
198
- case MetricDescriptorType . COUNTER_INT64 :
195
+ switch ( record . descriptor . metricKind ) {
196
+ case MetricKind . COUNTER :
199
197
// there is no such thing as a non-monotonic counter in prometheus
200
- return readableMetric . descriptor . monotonic
198
+ return record . descriptor . monotonic
201
199
? new Counter ( metricObject )
202
200
: new Gauge ( metricObject ) ;
203
- case MetricDescriptorType . GAUGE_DOUBLE :
204
- case MetricDescriptorType . GAUGE_INT64 :
201
+ case MetricKind . GAUGE :
205
202
return new Gauge ( metricObject ) ;
206
203
default :
207
204
// Other metric types are currently unimplemented
0 commit comments