6565import collections
6666import logging
6767import re
68- from typing import Iterable , Optional , Sequence , Union
68+ from typing import Optional , Sequence , Tuple
6969
70- from prometheus_client .core import (
71- REGISTRY ,
72- CounterMetricFamily ,
73- GaugeMetricFamily ,
74- HistogramMetricFamily ,
75- )
70+ from prometheus_client import core
7671
7772from opentelemetry .sdk ._metrics .export import (
7873 MetricExporter ,
7974 MetricExportResult ,
8075)
81- from opentelemetry .sdk ._metrics .point import (
82- AggregationTemporality ,
83- Gauge ,
84- Histogram ,
85- Metric ,
86- Sum ,
87- )
76+ from opentelemetry .sdk ._metrics .point import Gauge , Histogram , Metric , Sum
8877
8978logger = logging .getLogger (__name__ )
9079
@@ -99,14 +88,14 @@ class PrometheusMetricExporter(MetricExporter):
9988
10089 def __init__ (self , prefix : str = "" ):
10190 self ._collector = CustomCollector (prefix )
102- REGISTRY .register (self ._collector )
91+ core . REGISTRY .register (self ._collector )
10392
10493 def export (self , export_records : Sequence [Metric ]) -> MetricExportResult :
10594 self ._collector .add_metrics_data (export_records )
10695 return MetricExportResult .SUCCESS
10796
10897 def shutdown (self ) -> None :
109- REGISTRY .unregister (self ._collector )
98+ core . REGISTRY .unregister (self ._collector )
11099
111100
112101class CustomCollector :
@@ -124,7 +113,7 @@ def __init__(self, prefix: str = ""):
124113 def add_metrics_data (self , export_records : Sequence [Metric ]) -> None :
125114 self ._metrics_to_export .append (export_records )
126115
127- def collect (self ):
116+ def collect (self ) -> None :
128117 """Collect fetches the metrics from OpenTelemetry
129118 and delivers them as Prometheus Metrics.
130119 Collect is invoked every time a prometheus.Gatherer is run
@@ -139,49 +128,71 @@ def collect(self):
139128 if prometheus_metric is not None :
140129 yield prometheus_metric
141130
142- def _translate_to_prometheus (self , export_record : Metric ):
131+ def _convert_buckets (self , metric : Metric ) -> Sequence [Tuple [str , int ]]:
132+ buckets = []
133+ total_count = 0
134+ for i in range (0 , len (metric .point .bucket_counts )):
135+ total_count += metric .point .bucket_counts [i ]
136+ buckets .append (
137+ (
138+ f"{ metric .point .explicit_bounds [i ]} " ,
139+ total_count ,
140+ )
141+ )
142+ return buckets
143+
144+ def _translate_to_prometheus (
145+ self , metric : Metric
146+ ) -> Optional [core .Metric ]:
143147 prometheus_metric = None
144148 label_values = []
145149 label_keys = []
146- for key , value in export_record .attributes .items ():
150+ for key , value in metric .attributes .items ():
147151 label_keys .append (self ._sanitize (key ))
148152 label_values .append (str (value ))
149153
150154 metric_name = ""
151155 if self ._prefix != "" :
152156 metric_name = self ._prefix + "_"
153- metric_name += self ._sanitize (export_record .name )
154-
155- description = export_record .description or ""
156- if isinstance (export_record .point , Sum ):
157- prometheus_metric = CounterMetricFamily (
158- name = metric_name , documentation = description , labels = label_keys
157+ metric_name += self ._sanitize (metric .name )
158+
159+ description = metric .description or ""
160+ if isinstance (metric .point , Sum ):
161+ prometheus_metric = core .CounterMetricFamily (
162+ name = metric_name ,
163+ documentation = description ,
164+ labels = label_keys ,
165+ unit = metric .unit ,
159166 )
160167 prometheus_metric .add_metric (
161- labels = label_values , value = export_record .point .value
168+ labels = label_values , value = metric .point .value
162169 )
163- elif isinstance (export_record .point , Gauge ):
164- prometheus_metric = GaugeMetricFamily (
165- name = metric_name , documentation = description , labels = label_keys
170+ elif isinstance (metric .point , Gauge ):
171+ prometheus_metric = core .GaugeMetricFamily (
172+ name = metric_name ,
173+ documentation = description ,
174+ labels = label_keys ,
175+ unit = metric .unit ,
166176 )
167177 prometheus_metric .add_metric (
168- labels = label_values , value = export_record .point .value
178+ labels = label_values , value = metric .point .value
179+ )
180+ elif isinstance (metric .point , Histogram ):
181+ value = metric .point .sum
182+ prometheus_metric = core .HistogramMetricFamily (
183+ name = metric_name ,
184+ documentation = description ,
185+ labels = label_keys ,
186+ unit = metric .unit ,
187+ )
188+ buckets = self ._convert_buckets (metric )
189+ prometheus_metric .add_metric (
190+ labels = label_values , buckets = buckets , sum_value = value
169191 )
170- # TODO: Add support for histograms when supported in OT
171- # elif isinstance(export_record.point, Histogram):
172- # value = export_record.point.sum
173- # prometheus_metric = HistogramMetricFamily(
174- # name=metric_name,
175- # documentation=description,
176- # labels=label_keys,
177- # )
178- # prometheus_metric.add_metric(labels=label_values, buckets=export_record.point.explicit_bounds, sum_value=value)
179192 # TODO: add support for Summary once implemented
180193 # elif isinstance(export_record.point, Summary):
181194 else :
182- logger .warning (
183- "Unsupported metric type. %s" , type (export_record .point )
184- )
195+ logger .warning ("Unsupported metric type. %s" , type (metric .point ))
185196 return prometheus_metric
186197
187198 def _sanitize (self , key : str ) -> str :
0 commit comments