1/**
2 * Exports Effect metrics over OTLP/HTTP.
3 *
4 * This module periodically snapshots metrics from the current Effect context,
5 * serializes them as OTLP resource metrics, and posts them to a metrics
6 * endpoint such as an OpenTelemetry Collector or vendor intake. It is meant for
7 * long-running services that already update `Metric` counters, gauges,
8 * histograms, frequencies, or summaries. The exporter supports cumulative
9 * reporting from a fixed start time and delta reporting from the previous
10 * export.
11 *
12 * @stability unstable
13 * @since 4.0.0
14 */
15import * as Arr from "../Array.ts"
16import { Clock } from "../Clock.ts"
17import * as Config from "../Config.ts"
18import * as Duration from "../Duration.ts"
19import * as Effect from "../Effect.ts"
20import type * as Headers from "../http/Headers.ts"
21import type { HttpBody } from "../http/HttpBody.ts"
22import type * as HttpClient from "../http/HttpClient.ts"
23import * as Layer from "../Layer.ts"
24import * as Metric from "../Metric.ts"
25import * as Option from "../Option.ts"
26import type * as Scope from "../Scope.ts"
27import * as OtlpEnv from "./internal/otlpEnv.ts"
28import * as Exporter from "./OtlpExporter.ts"
29import type { Fixed64, KeyValue } from "./OtlpResource.ts"
30import * as OtlpResource from "./OtlpResource.ts"
31import { OtlpSerialization } from "./OtlpSerialization.ts"
32
33/**
34 * Determines how metric values relate to the time interval over which they are aggregated.
35 *
36 * **Details**
37 *
38 * `"cumulative"` reports total since a fixed start time. Each data point depends on all previous measurements. This is the default behavior.
39 *
40 * `"delta"` reports changes since the last export. Each interval is independent with no dependency on previous measurements.
41 *
42 * **Example** (Configuring aggregation temporality)
43 *
44 * ```ts import.meta.vitest
45 * import { Layer } from "effect"
46 * import { OtlpMetrics } from "effect/observability"
47 *
48 * // Use delta temporality for backends that prefer it (e.g., Datadog, Dynatrace)
49 * const metricsLayer = OtlpMetrics.layer({
50 * url: "http://localhost:4318/v1/metrics",
51 * temporality: "delta"
52 * })
53 *
54 * // Use cumulative temporality for backends like Prometheus
55 * const cumulativeLayer = OtlpMetrics.layer({
56 * url: "http://localhost:4318/v1/metrics",
57 * temporality: "cumulative" // This is the default
58 * })
59 *
60 * const result = [Layer.isLayer(metricsLayer), Layer.isLayer(cumulativeLayer)] // => [true, true]
61 * ```
62 *
63 * @stability unstable
64 * @category models
65 * @since 4.0.0
66 */
67export type AggregationTemporality = "cumulative" | "delta"
68
69/**
70 * Starts a scoped OTLP metrics exporter.
71 *
72 * **Details**
73 *
74 * The exporter snapshots registered Effect metrics on the configured interval, serializes them with the selected aggregation temporality, and flushes during scope finalization up to `shutdownTimeout`.
75 * Manual flushing also triggers a snapshot. With delta temporality, each
76 * successful export advances the previous-export state, so frequent successful
77 * flushes narrow the delta aggregation windows.
78 *
79 * @stability unstable
80 * @category constructors
81 * @since 4.0.0
82 */
83export const make: (options: {
84 readonly url: string
85 readonly resource?: {
86 readonly serviceName?: string | undefined
87 readonly serviceVersion?: string | undefined
88 readonly attributes?: Record<string, unknown>
89 } | undefined
90 readonly headers?: Headers.Input | undefined
91 readonly exportInterval?: Duration.Input | undefined
92 readonly shutdownTimeout?: Duration.Input | undefined
93 readonly temporality?: AggregationTemporality | undefined
94}) => Effect.Effect<
95 void,
96 never,
97 Exporter.Flusher | HttpClient.HttpClient | OtlpSerialization | Scope.Scope
98> = Effect.fnUntraced(function*(options) {
99 const clock = yield* Clock
100 const serialization = yield* OtlpSerialization
101 const startTimeNanos = yield* clock.currentTimeNanos
102 const startTime = String(startTimeNanos)
103 const temporality = options.temporality ?? "cumulative"
104
105 const resource = yield* OtlpResource.fromConfig(options.resource)
106 const metricsScope: IInstrumentationScope = {
107 name: OtlpResource.serviceNameUnsafe(resource)
108 }
109
110 const services = yield* Effect.context<never>()
111
112 // State for delta temporality tracking
113 let previousExportTimeNanos: bigint = startTimeNanos
114 let previousCounterState = new Map<string, number | bigint>()
115 let previousHistogramState = new Map<string, PreviousHistogramState>()
116 let previousFrequencyState = new Map<string, Map<string, number>>()
117 let previousSummaryState = new Map<string, PreviousSummaryState>()
118 let snapshotSequence = 0
119 let committedSnapshotSequence = -1
120
121 const snapshot = (): readonly [body: HttpBody, onSuccess: Effect.Effect<void>] => {
122 const snapshot = Metric.snapshotUnsafe(services)
123 const currentSnapshotSequence = snapshotSequence++
124 const nowNanos = clock.currentTimeNanosUnsafe()
125 const nowTime = String(nowNanos)
126 const nextCounterState = new Map(previousCounterState)
127 const nextHistogramState = new Map(previousHistogramState)
128 const nextFrequencyState = new Map(previousFrequencyState)
129 const nextSummaryState = new Map(previousSummaryState)
130 const metricData: Array<IMetric> = []
131 const metricDataByName = new Map<string, IMetric>()
132 const addMetricData = (data: IMetric) => {
133 metricData.push(data)
134 metricDataByName.set(data.name, data)
135 }
136
137 const isDelta = temporality === "delta"
138 const aggregationTemporalityEnum = isDelta
139 ? EAggregationTemporality.AGGREGATION_TEMPORALITY_DELTA
140 : EAggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE
141 const intervalStartTime = isDelta
142 ? String(previousExportTimeNanos)
143 : startTime
144
145 for (let i = 0, len = snapshot.length; i < len; i++) {
146 const state = snapshot[i]
147 const unit = state.attributes?.unit ?? state.attributes?.time_unit ?? "1"
148 const attributes = state.attributes ? OtlpResource.entriesToAttributes(Object.entries(state.attributes)) : []
149 const metricKey = makeMetricKey(state.id, state.attributes)
150
151 switch (state.type) {
152 case "Counter": {
153 const currentCount = state.state.count
154 let reportValue: number | bigint = currentCount
155
156 if (isDelta) {
157 const previousCount = previousCounterState.get(metricKey)
158 if (previousCount !== undefined) {
159 if (typeof currentCount === "bigint" && typeof previousCount === "bigint") {
160 reportValue = currentCount - previousCount
161 // Handle reset: if current < previous, report current value
162 if (state.state.incremental && reportValue < BigInt(0)) {
163 reportValue = currentCount
164 }
165 } else {
166 const curr = Number(currentCount)
167 const prev = Number(previousCount)
168 reportValue = curr - prev
169 // Handle reset
170 if (state.state.incremental && reportValue < 0) {
171 reportValue = curr
172 }
173 }
174 }
175 nextCounterState.set(metricKey, currentCount)
176 }
177
178 const dataPoint: INumberDataPoint = {
179 attributes,
180 startTimeUnixNano: intervalStartTime,
181 timeUnixNano: nowTime
182 }
183 if (typeof reportValue === "bigint") {
184 dataPoint.asInt = Number(reportValue)
185 } else {
186 dataPoint.asDouble = reportValue
187 }
188 if (metricDataByName.has(state.id)) {
189 metricDataByName.get(state.id)!.sum!.dataPoints.push(dataPoint)
190 } else {
191 addMetricData({
192 name: state.id,
193 description: state.description!,
194 unit,
195 sum: {
196 aggregationTemporality: aggregationTemporalityEnum,
197 isMonotonic: state.state.incremental,
198 dataPoints: [dataPoint]
199 }
200 })
201 }
202 break
203 }
204 case "Gauge": {
205 // Gauges don't have temporality - they always report current value
206 const dataPoint: INumberDataPoint = {
207 attributes,
208 startTimeUnixNano: startTime,
209 timeUnixNano: nowTime
210 }
211 if (typeof state.state.value === "bigint") {
212 dataPoint.asInt = Number(state.state.value)
213 } else {
214 dataPoint.asDouble = state.state.value
215 }
216 if (metricDataByName.has(state.id)) {
217 metricDataByName.get(state.id)!.gauge!.dataPoints.push(dataPoint)
218 } else {
219 addMetricData({
220 name: state.id,
221 description: state.description!,
222 unit,
223 gauge: {
224 dataPoints: [dataPoint]
225 }
226 })
227 }
228 break
229 }
230 case "Histogram": {
231 const size = state.state.buckets.length
232 const currentBuckets = {
233 boundaries: Arr.allocate(size - 1) as Array<number>,
234 counts: Arr.allocate(size) as Array<number>
235 }
236 let idx = 0
237 let prev = 0
238 for (const [boundary, value] of state.state.buckets) {
239 if (idx < size - 1) {
240 currentBuckets.boundaries[idx] = boundary
241 }
242 currentBuckets.counts[idx] = value - prev
243 prev = value
244 idx++
245 }
246
247 let reportCount = state.state.count
248 let reportSum = state.state.sum
249 let reportBucketCounts = currentBuckets.counts
250 const reportMin = state.state.min
251 const reportMax = state.state.max
252
253 if (isDelta) {
254 const previousState = previousHistogramState.get(metricKey)
255 if (previousState !== undefined) {
256 reportCount = state.state.count - previousState.count
257 reportSum = state.state.sum - previousState.sum
258 reportBucketCounts = currentBuckets.counts.map((c, i) =>
259 Math.max(0, c - (previousState.bucketCounts[i] ?? 0))
260 )
261 // For delta, min/max represent the interval's min/max
262 // We can't compute these from cumulative state, so we use current values
263 // Note: This is a limitation - true delta min/max would require tracking
264 // observations within each interval
265 }
266 nextHistogramState.set(metricKey, {
267 count: state.state.count,
268 sum: state.state.sum,
269 bucketCounts: currentBuckets.counts.slice(),
270 min: state.state.min,
271 max: state.state.max
272 })
273 }
274
275 const dataPoint: IHistogramDataPoint = {
276 attributes,
277 startTimeUnixNano: intervalStartTime,
278 timeUnixNano: nowTime,
279 count: reportCount,
280 min: reportMin,
281 max: reportMax,
282 sum: reportSum,
283 bucketCounts: reportBucketCounts,
284 explicitBounds: currentBuckets.boundaries
285 }
286
287 if (metricDataByName.has(state.id)) {
288 metricDataByName.get(state.id)!.histogram!.dataPoints.push(dataPoint)
289 } else {
290 addMetricData({
291 name: state.id,
292 description: state.description!,
293 unit,
294 histogram: {
295 aggregationTemporality: aggregationTemporalityEnum,
296 dataPoints: [dataPoint]
297 }
298 })
299 }
300 break
301 }
302 case "Frequency": {
303 const dataPoints: Array<INumberDataPoint> = []
304 const currentOccurrences = new Map<string, number>()
305
306 for (const [freqKey, value] of state.state.occurrences) {
307 currentOccurrences.set(freqKey, value)
308 let reportValue = value
309
310 if (isDelta) {
311 const previousOccurrences = previousFrequencyState.get(metricKey)
312 if (previousOccurrences !== undefined) {
313 const previousValue = previousOccurrences.get(freqKey) ?? 0
314 reportValue = Math.max(0, value - previousValue)
315 }
316 }
317
318 dataPoints.push({
319 attributes: [...attributes, { key: "key", value: { stringValue: freqKey } }],
320 startTimeUnixNano: intervalStartTime,
321 timeUnixNano: nowTime,
322 asInt: reportValue
323 })
324 }
325
326 if (isDelta) {
327 nextFrequencyState.set(metricKey, currentOccurrences)
328 }
329
330 if (metricDataByName.has(state.id)) {
331 metricDataByName.get(state.id)!.sum!.dataPoints.push(...dataPoints)
332 } else {
333 addMetricData({
334 name: state.id,
335 description: state.description!,
336 unit,
337 sum: {
338 aggregationTemporality: aggregationTemporalityEnum,
339 isMonotonic: true,
340 dataPoints
341 }
342 })
343 }
344 break
345 }
346 case "Summary": {
347 // Quantiles are always computed fresh from the sliding window
348 // They don't have temporality in the traditional sense
349 const dataPoints: Array<INumberDataPoint> = [{
350 attributes: [...attributes, { key: "quantile", value: { stringValue: "min" } }],
351 startTimeUnixNano: intervalStartTime,
352 timeUnixNano: nowTime,
353 asDouble: state.state.min
354 }]
355 for (const [quantile, value] of state.state.quantiles) {
356 dataPoints.push({
357 attributes: [...attributes, { key: "quantile", value: { stringValue: quantile.toString() } }],
358 startTimeUnixNano: intervalStartTime,
359 timeUnixNano: nowTime,
360 asDouble: value ?? 0
361 })
362 }
363 dataPoints.push({
364 attributes: [...attributes, { key: "quantile", value: { stringValue: "max" } }],
365 startTimeUnixNano: intervalStartTime,
366 timeUnixNano: nowTime,
367 asDouble: state.state.max
368 })
369
370 let reportCount = state.state.count
371 let reportSum = state.state.sum
372
373 if (isDelta) {
374 const previousState = previousSummaryState.get(metricKey)
375 if (previousState !== undefined) {
376 reportCount = state.state.count - previousState.count
377 reportSum = state.state.sum - previousState.sum
378 }
379 nextSummaryState.set(metricKey, {
380 count: state.state.count,
381 sum: state.state.sum
382 })
383 }
384
385 const countDataPoint: INumberDataPoint = {
386 attributes,
387 startTimeUnixNano: intervalStartTime,
388 timeUnixNano: nowTime,
389 asInt: reportCount
390 }
391 const sumDataPoint: INumberDataPoint = {
392 attributes,
393 startTimeUnixNano: intervalStartTime,
394 timeUnixNano: nowTime,
395 asDouble: reportSum
396 }
397
398 if (metricDataByName.has(`${state.id}_quantiles`)) {
399 metricDataByName.get(`${state.id}_quantiles`)!.sum!.dataPoints.push(...dataPoints)
400 metricDataByName.get(`${state.id}_count`)!.sum!.dataPoints.push(countDataPoint)
401 metricDataByName.get(`${state.id}_sum`)!.sum!.dataPoints.push(sumDataPoint)
402 } else {
403 addMetricData({
404 name: `${state.id}_quantiles`,
405 description: state.description!,
406 unit,
407 sum: {
408 aggregationTemporality: aggregationTemporalityEnum,
409 isMonotonic: false,
410 dataPoints
411 }
412 })
413 addMetricData({
414 name: `${state.id}_count`,
415 description: state.description!,
416 unit: "1",
417 sum: {
418 aggregationTemporality: aggregationTemporalityEnum,
419 isMonotonic: true,
420 dataPoints: [countDataPoint]
421 }
422 })
423 addMetricData({
424 name: `${state.id}_sum`,
425 description: state.description!,
426 unit: "1",
427 sum: {
428 aggregationTemporality: aggregationTemporalityEnum,
429 isMonotonic: true,
430 dataPoints: [sumDataPoint]
431 }
432 })
433 }
434 break
435 }
436 }
437 }
438
439 const body = serialization.metrics({
440 resourceMetrics: [{
441 resource,
442 scopeMetrics: [{
443 scope: metricsScope,
444 metrics: metricData
445 }]
446 }]
447 })
448 const onSuccess = isDelta
449 ? Effect.sync(() => {
450 if (currentSnapshotSequence < committedSnapshotSequence) return
451 previousCounterState = nextCounterState
452 previousHistogramState = nextHistogramState
453 previousFrequencyState = nextFrequencyState
454 previousSummaryState = nextSummaryState
455 previousExportTimeNanos = nowNanos
456 committedSnapshotSequence = currentSnapshotSequence
457 })
458 : Effect.void
459 return [body, onSuccess]
460 }
461
462 yield* Exporter.make({
463 label: "OtlpMetrics",
464 url: options.url,
465 headers: options.headers,
466 maxBatchSize: "disabled",
467 exportEmpty: true,
468 exportInterval: options.exportInterval ?? Duration.seconds(10),
469 body: snapshot,
470 shutdownTimeout: options.shutdownTimeout ?? Duration.seconds(3)
471 })
472})
473
474/**
475 * Layer that starts the OTLP metrics exporter created by `make`.
476 *
477 * @stability unstable
478 * @category layers
479 * @since 4.0.0
480 */
481export const layer = (options: {
482 readonly url: string
483 readonly resource?: {
484 readonly serviceName?: string | undefined
485 readonly serviceVersion?: string | undefined
486 readonly attributes?: Record<string, unknown>
487 } | undefined
488 readonly headers?: Headers.Input | undefined
489 readonly exportInterval?: Duration.Input | undefined
490 readonly shutdownTimeout?: Duration.Input | undefined
491 readonly temporality?: AggregationTemporality | undefined
492}): Layer.Layer<Exporter.Flusher, never, HttpClient.HttpClient | OtlpSerialization> =>
493 Layer.effectDiscard(make(options)).pipe(Layer.provideMerge(Exporter.layerFlusher))
494
495/**
496 * Creates an OTLP metrics layer from OpenTelemetry configuration.
497 *
498 * @stability unstable
499 * @category layers
500 * @since 4.0.0
501 */
502export const layerFromConfig = (options?: {
503 readonly resource?: {
504 readonly serviceName?: string | undefined
505 readonly serviceVersion?: string | undefined
506 readonly attributes?: Record<string, unknown>
507 } | undefined
508 readonly headers?: Headers.Input | undefined
509}): Layer.Layer<Exporter.Flusher, never, HttpClient.HttpClient | OtlpSerialization> =>
510 Effect.gen(function*() {
511 const { disabled, endpoint, exporters } = yield* Config.all({
512 disabled: Config.Boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false)),
513 endpoint: OtlpEnv.endpoint("METRICS"),
514 exporters: OtlpEnv.exporters("METRICS")
515 })
516 if (disabled || !endpoint || !exporters.includes("otlp")) {
517 return Exporter.layerFlusher
518 }
519
520 const { baseTimeout, metricsTimeout, exportTimeout, exportInterval, temporalityPreference } = yield* Config.all({
521 baseTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_TIMEOUT")),
522 metricsTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_METRICS_TIMEOUT")),
523 exportTimeout: Config.option(Config.Int("OTEL_METRIC_EXPORT_TIMEOUT")),
524 exportInterval: Config.option(
525 Config.Int("OTEL_METRIC_EXPORT_INTERVAL").pipe(
526 Config.map(Duration.millis)
527 )
528 ),
529 temporalityPreference: Config.option(
530 Config.Literals(["delta", "cumulative"], "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE")
531 )
532 })
533
534 const shutdownTimeout = Option.firstSomeOf([metricsTimeout, baseTimeout, exportTimeout]).pipe(
535 Option.map((_) => Duration.millis(_))
536 )
537
538 return layer({
539 url: endpoint.toString(),
540 resource: options?.resource,
541 headers: options?.headers ?? (yield* OtlpEnv.headers("METRICS")),
542 exportInterval: Option.getOrUndefined(exportInterval),
543 shutdownTimeout: Option.getOrUndefined(shutdownTimeout),
544 temporality: Option.getOrUndefined(temporalityPreference)
545 })
546 }).pipe(Effect.orDie, Layer.unwrap)
547
548/**
549 * OTLP metrics payload serialized by `OtlpMetrics`.
550 *
551 * @stability unstable
552 * @category models
553 * @since 4.0.0
554 */
555export interface MetricsData {
556 readonly resourceMetrics: ReadonlyArray<IResourceMetrics>
557}
558
559// internal
560
561/** Creates a unique key for a metric including its attributes */
562const makeMetricKey = (id: string, attributes: Metric.Metric.AttributeSet | undefined): string => {
563 if (attributes === undefined || Object.keys(attributes).length === 0) {
564 return id
565 }
566 const sortedEntries = Object.entries(attributes).sort((a, b) => a[0].localeCompare(b[0]))
567 return `${id}:${JSON.stringify(sortedEntries)}`
568}
569
570/** Previous state for histogram delta computation */
571interface PreviousHistogramState {
572 readonly count: number
573 readonly sum: number
574 readonly bucketCounts: Array<number>
575 readonly min: number
576 readonly max: number
577}
578
579/** Previous state for summary delta computation */
580interface PreviousSummaryState {
581 readonly count: number
582 readonly sum: number
583}
584
585/** Properties of an InstrumentationScope. */
586interface IInstrumentationScope {
587 /** InstrumentationScope name */
588 name: string
589 /** InstrumentationScope version */
590 version?: string
591 /** InstrumentationScope attributes */
592 attributes?: Array<KeyValue>
593 /** InstrumentationScope droppedAttributesCount */
594 droppedAttributesCount?: number
595}
596
597/** Properties of a ResourceMetrics. */
598interface IResourceMetrics {
599 /** ResourceMetrics resource */
600 resource: OtlpResource.Resource
601 /** ResourceMetrics scopeMetrics */
602 scopeMetrics: Array<IScopeMetrics>
603 /** ResourceMetrics schemaUrl */
604 schemaUrl?: string | undefined
605}
606/** Properties of an IScopeMetrics. */
607interface IScopeMetrics {
608 /** ScopeMetrics scope */
609 scope: IInstrumentationScope
610 /** ScopeMetrics metrics */
611 metrics: Array<IMetric>
612 /** ScopeMetrics schemaUrl */
613 schemaUrl?: string | undefined
614}
615/** Properties of a Metric. */
616interface IMetric {
617 /** Metric name */
618 name: string
619 /** Metric description */
620 description?: string
621 /** Metric unit */
622 unit?: string
623 /** Metric gauge */
624 gauge?: IGauge
625 /** Metric sum */
626 sum?: ISum
627 /** Metric histogram */
628 histogram?: IHistogram
629 /** Metric exponentialHistogram */
630 exponentialHistogram?: IExponentialHistogram
631 /** Metric summary */
632 summary?: ISummary
633}
634/** Properties of a Gauge. */
635interface IGauge {
636 /** Gauge dataPoints */
637 dataPoints: Array<INumberDataPoint>
638}
639/** Properties of a Sum. */
640interface ISum {
641 /** Sum dataPoints */
642 dataPoints: Array<INumberDataPoint>
643 /** Sum aggregationTemporality */
644 aggregationTemporality: EAggregationTemporality
645 /** Sum isMonotonic */
646 isMonotonic: boolean
647}
648/** Properties of a Histogram. */
649interface IHistogram {
650 /** Histogram dataPoints */
651 dataPoints: Array<IHistogramDataPoint>
652 /** Histogram aggregationTemporality */
653 aggregationTemporality?: EAggregationTemporality
654}
655/** Properties of an ExponentialHistogram. */
656interface IExponentialHistogram {
657 /** ExponentialHistogram dataPoints */
658 dataPoints: Array<IExponentialHistogramDataPoint>
659 /** ExponentialHistogram aggregationTemporality */
660 aggregationTemporality?: EAggregationTemporality
661}
662/** Properties of a Summary. */
663interface ISummary {
664 /** Summary dataPoints */
665 dataPoints: Array<ISummaryDataPoint>
666}
667/** Properties of a NumberDataPoint. */
668interface INumberDataPoint {
669 /** NumberDataPoint attributes */
670 attributes: Array<KeyValue>
671 /** NumberDataPoint startTimeUnixNano */
672 startTimeUnixNano?: Fixed64
673 /** NumberDataPoint timeUnixNano */
674 timeUnixNano?: Fixed64
675 /** NumberDataPoint asDouble */
676 asDouble?: number | null
677 /** NumberDataPoint asInt */
678 asInt?: number
679 /** NumberDataPoint exemplars */
680 exemplars?: Array<IExemplar>
681 /** NumberDataPoint flags */
682 flags?: number
683}
684/** Properties of a HistogramDataPoint. */
685interface IHistogramDataPoint {
686 /** HistogramDataPoint attributes */
687 attributes?: Array<KeyValue>
688 /** HistogramDataPoint startTimeUnixNano */
689 startTimeUnixNano?: Fixed64
690 /** HistogramDataPoint timeUnixNano */
691 timeUnixNano?: Fixed64
692 /** HistogramDataPoint count */
693 count?: number
694 /** HistogramDataPoint sum */
695 sum?: number
696 /** HistogramDataPoint bucketCounts */
697 bucketCounts?: Array<number>
698 /** HistogramDataPoint explicitBounds */
699 explicitBounds?: Array<number>
700 /** HistogramDataPoint exemplars */
701 exemplars?: Array<IExemplar>
702 /** HistogramDataPoint flags */
703 flags?: number
704 /** HistogramDataPoint min */
705 min?: number
706 /** HistogramDataPoint max */
707 max?: number
708}
709/** Properties of an ExponentialHistogramDataPoint. */
710interface IExponentialHistogramDataPoint {
711 /** ExponentialHistogramDataPoint attributes */
712 attributes?: Array<KeyValue>
713 /** ExponentialHistogramDataPoint startTimeUnixNano */
714 startTimeUnixNano?: Fixed64
715 /** ExponentialHistogramDataPoint timeUnixNano */
716 timeUnixNano?: Fixed64
717 /** ExponentialHistogramDataPoint count */
718 count?: number
719 /** ExponentialHistogramDataPoint sum */
720 sum?: number
721 /** ExponentialHistogramDataPoint scale */
722 scale?: number
723 /** ExponentialHistogramDataPoint zeroCount */
724 zeroCount?: number
725 /** ExponentialHistogramDataPoint positive */
726 positive?: IBuckets
727 /** ExponentialHistogramDataPoint negative */
728 negative?: IBuckets
729 /** ExponentialHistogramDataPoint flags */
730 flags?: number
731 /** ExponentialHistogramDataPoint exemplars */
732 exemplars?: Array<IExemplar>
733 /** ExponentialHistogramDataPoint min */
734 min?: number
735 /** ExponentialHistogramDataPoint max */
736 max?: number
737}
738/** Properties of a SummaryDataPoint. */
739interface ISummaryDataPoint {
740 /** SummaryDataPoint attributes */
741 attributes?: Array<KeyValue>
742 /** SummaryDataPoint startTimeUnixNano */
743 startTimeUnixNano?: number
744 /** SummaryDataPoint timeUnixNano */
745 timeUnixNano?: string
746 /** SummaryDataPoint count */
747 count?: number
748 /** SummaryDataPoint sum */
749 sum?: number
750 /** SummaryDataPoint quantileValues */
751 quantileValues?: Array<IValueAtQuantile>
752 /** SummaryDataPoint flags */
753 flags?: number
754}
755/** Properties of a ValueAtQuantile. */
756interface IValueAtQuantile {
757 /** ValueAtQuantile quantile */
758 quantile?: number
759 /** ValueAtQuantile value */
760 value?: number
761}
762/** Properties of a Buckets. */
763interface IBuckets {
764 /** Buckets offset */
765 offset?: number
766 /** Buckets bucketCounts */
767 bucketCounts?: Array<number>
768}
769/** Properties of an Exemplar. */
770interface IExemplar {
771 /** Exemplar filteredAttributes */
772 filteredAttributes?: Array<KeyValue>
773 /** Exemplar timeUnixNano */
774 timeUnixNano?: string
775 /** Exemplar asDouble */
776 asDouble?: number
777 /** Exemplar asInt */
778 asInt?: number
779 /** Exemplar spanId */
780 spanId?: string | Uint8Array
781 /** Exemplar traceId */
782 traceId?: string | Uint8Array
783}
784/**
785 * AggregationTemporality defines how a metric aggregator reports aggregated
786 * values. It describes how those values relate to the time interval over
787 * which they are aggregated.
788 */
789const EAggregationTemporality = {
790 AGGREGATION_TEMPORALITY_UNSPECIFIED: 0,
791 /** DELTA is an AggregationTemporality for a metric aggregator which reports
792 changes since last report time. Successive metrics contain aggregation of
793 values from continuous and non-overlapping intervals.
794
795 The values for a DELTA metric are based only on the time interval
796 associated with one measurement cycle. There is no dependency on
797 previous measurements like is the case for CUMULATIVE metrics.
798
799 For example, consider a system measuring the number of requests that
800 it receives and reports the sum of these requests every second as a
801 DELTA metric:
802
803 1. The system starts receiving at time=t_0.
804 2. A request is received, the system measures 1 request.
805 3. A request is received, the system measures 1 request.
806 4. A request is received, the system measures 1 request.
807 5. The 1 second collection cycle ends. A metric is exported for the
808 number of requests received over the interval of time t_0 to
809 t_0+1 with a value of 3.
810 6. A request is received, the system measures 1 request.
811 7. A request is received, the system measures 1 request.
812 8. The 1 second collection cycle ends. A metric is exported for the
813 number of requests received over the interval of time t_0+1 to
814 t_0+2 with a value of 2. */
815 AGGREGATION_TEMPORALITY_DELTA: 1,
816 /** CUMULATIVE is an AggregationTemporality for a metric aggregator which
817 reports changes since a fixed start time. This means that current values
818 of a CUMULATIVE metric depend on all previous measurements since the
819 start time. Because of this, the sender is required to retain this state
820 in some form. If this state is lost or invalidated, the CUMULATIVE metric
821 values MUST be reset and a new fixed start time following the last
822 reported measurement time sent MUST be used.
823
824 For example, consider a system measuring the number of requests that
825 it receives and reports the sum of these requests every second as a
826 CUMULATIVE metric:
827
828 1. The system starts receiving at time=t_0.
829 2. A request is received, the system measures 1 request.
830 3. A request is received, the system measures 1 request.
831 4. A request is received, the system measures 1 request.
832 5. The 1 second collection cycle ends. A metric is exported for the
833 number of requests received over the interval of time t_0 to
834 t_0+1 with a value of 3.
835 6. A request is received, the system measures 1 request.
836 7. A request is received, the system measures 1 request.
837 8. The 1 second collection cycle ends. A metric is exported for the
838 number of requests received over the interval of time t_0 to
839 t_0+2 with a value of 5.
840 9. The system experiences a fault and loses state.
841 10. The system recovers and resumes receiving at time=t_1.
842 11. A request is received, the system measures 1 request.
843 12. The 1 second collection cycle ends. A metric is exported for the
844 number of requests received over the interval of time t_1 to
845 t_0+1 with a value of 1.
846
847 Note: Even though, when reporting changes since last report time, using
848 CUMULATIVE is valid, it is not recommended. This may cause problems for
849 systems that do not use start_time to determine when the aggregation
850 value was reset (e.g. Prometheus). */
851 AGGREGATION_TEMPORALITY_CUMULATIVE: 2
852} as const
853
854type EAggregationTemporality = typeof EAggregationTemporality[keyof typeof EAggregationTemporality]