11const express = require ( 'express' ) ;
2- const Prometheus = require ( 'prom-client' ) ;
2+ const OtelApi = require ( '@opentelemetry/api' ) ;
3+ const Prometheus = require ( '/home/andrei/dev/prom-client' ) ;
34const ResponseTime = require ( 'response-time' ) ;
45
56const {
@@ -20,6 +21,8 @@ const defaultOptions = {
2021 authenticate : null ,
2122 collectDefaultMetrics : true ,
2223 collectGCMetrics : false ,
24+ enableExemplars : false ,
25+ contentType : Prometheus . Registry . contentType ,
2326 // buckets for response time from 0.05s to 2.5s
2427 // these are arbitrary values since i dont know any better ¯\_(ツ)_/¯
2528 requestDurationBuckets : Prometheus . exponentialBuckets ( 0.05 , 1.75 , 8 ) ,
@@ -41,24 +44,30 @@ module.exports = (userOptions = {}) => {
4144 const app = express ( ) ;
4245 app . disable ( 'x-powered-by' ) ;
4346
47+ Prometheus . register . setContentType ( options . contentType ) ;
48+
4449 const requestDuration = requestDurationGenerator (
4550 options . customLabels ,
4651 options . requestDurationBuckets ,
4752 options . prefix ,
53+ options . enableExemplars ,
4854 ) ;
4955 const requestCount = requestCountGenerator (
5056 options . customLabels ,
5157 options . prefix ,
58+ options . enableExemplars ,
5259 ) ;
5360 const requestLength = requestLengthGenerator (
5461 options . customLabels ,
5562 options . requestLengthBuckets ,
5663 options . prefix ,
64+ options . enableExemplars ,
5765 ) ;
5866 const responseLength = responseLengthGenerator (
5967 options . customLabels ,
6068 options . responseLengthBuckets ,
6169 options . prefix ,
70+ options . enableExemplars ,
6271 ) ;
6372
6473 /**
@@ -73,6 +82,18 @@ module.exports = (userOptions = {}) => {
7382 const route = normalizePath ( originalUrl , options . extraMasks ) ;
7483
7584 if ( route !== metricsPath ) {
85+ let exemplarLabels = null ;
86+ if ( options . enableExemplars ) {
87+ exemplarLabels = { } ;
88+ let current_span = OtelApi . trace . getSpan ( OtelApi . context . active ( ) ) ;
89+ if ( current_span ) {
90+ exemplarLabels = {
91+ 'traceId' : current_span . spanContext ( ) . traceId ,
92+ 'spanId' : current_span . spanContext ( ) . spanId
93+ } ;
94+ }
95+ }
96+
7697 const status = normalizeStatus
7798 ? normalizeStatusCode ( res . statusCode ) : res . statusCode . toString ( ) ;
7899
@@ -81,24 +102,40 @@ module.exports = (userOptions = {}) => {
81102 if ( typeof options . transformLabels === 'function' ) {
82103 options . transformLabels ( labels , req , res ) ;
83104 }
84- requestCount . inc ( labels ) ;
105+ if ( exemplarLabels != null ) {
106+ requestCount . inc ( { labels : labels , exemplarLabels : exemplarLabels } ) ;
107+ } else {
108+ requestCount . inc ( labels , exemplarLabels ) ;
109+ }
85110
86111 // observe normalizing to seconds
87- requestDuration . observe ( labels , time / 1000 ) ;
112+ if ( exemplarLabels != null ) {
113+ requestDuration . observe ( { labels : labels , value : ( time / 1000 ) , exemplarLabels : exemplarLabels } ) ;
114+ } else {
115+ requestDuration . observe ( labels , time / 1000 ) ;
116+ }
88117
89118 // observe request length
90119 if ( options . requestLengthBuckets . length ) {
91120 const reqLength = req . get ( 'Content-Length' ) ;
92121 if ( reqLength ) {
93- requestLength . observe ( labels , Number ( reqLength ) ) ;
122+ if ( exemplarLabels != null ) {
123+ requestLength . observe ( { labels : labels , value : Number ( reqLength ) , exemplarLabels : exemplarLabels } ) ;
124+ } else {
125+ requestLength . observe ( labels , Number ( reqLength ) ) ;
126+ }
94127 }
95128 }
96129
97130 // observe response length
98131 if ( options . responseLengthBuckets . length ) {
99132 const resLength = res . get ( 'Content-Length' ) ;
100133 if ( resLength ) {
101- responseLength . observe ( labels , Number ( resLength ) ) ;
134+ if ( exemplarLabels != null ) {
135+ responseLength . observe ( { labels : labels , value : Number ( resLength ) , exemplarLabels : exemplarLabels } ) ;
136+ } else {
137+ responseLength . observe ( labels , Number ( resLength ) ) ;
138+ }
102139 }
103140 }
104141 }
@@ -110,6 +147,7 @@ module.exports = (userOptions = {}) => {
110147 // used to calculate saturation of the service
111148 Prometheus . collectDefaultMetrics ( {
112149 prefix : options . prefix ,
150+ enableExemplars : options . enableExemplars ,
113151 } ) ;
114152 }
115153
@@ -125,6 +163,7 @@ module.exports = (userOptions = {}) => {
125163 /* eslint-enable global-require */
126164 const startGcStats = gcStats ( Prometheus . register , {
127165 prefix : options . prefix ,
166+ enableExemplars : options . enableExemplars
128167 } ) ;
129168 startGcStats ( ) ;
130169 } catch ( err ) {
@@ -154,7 +193,7 @@ module.exports = (userOptions = {}) => {
154193 }
155194 }
156195
157- res . set ( 'Content-Type' , Prometheus . register . contentType ) ;
196+ res . set ( 'Content-Type' , options . contentType ) ;
158197 return res . end ( await Prometheus . register . metrics ( ) ) ;
159198 } ) ;
160199
0 commit comments