index.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. const os = require('os')
  2. const ExpressCompression = require('compression')
  3. const promClient = require('prom-client')
  4. const promWrapper = require('./prom_wrapper')
  5. const DEFAULT_APP_NAME = 'unknown'
  6. const { collectDefaultMetrics } = promWrapper
  7. const destructors = []
  8. require('./uv_threadpool_size')
  9. /**
  10. * Configure the metrics module
  11. */
  12. function configure(opts = {}) {
  13. const appName = opts.appName || DEFAULT_APP_NAME
  14. const hostname = os.hostname()
  15. promClient.register.setDefaultLabels({ app: appName, host: hostname })
  16. if (opts.ttlInMinutes) {
  17. promWrapper.ttlInMinutes = opts.ttlInMinutes
  18. }
  19. }
  20. /**
  21. * Configure the metrics module and start the default metrics collectors and
  22. * profiling agents.
  23. */
  24. function initialize(appName, opts = {}) {
  25. appName = appName || DEFAULT_APP_NAME
  26. configure({ ...opts, appName })
  27. collectDefaultMetrics({ timeout: 5000, prefix: '' })
  28. promWrapper.setupSweeping()
  29. console.log(`ENABLE_TRACE_AGENT set to ${process.env.ENABLE_TRACE_AGENT}`)
  30. if (process.env.ENABLE_TRACE_AGENT === 'true') {
  31. console.log('starting google trace agent')
  32. const traceAgent = require('@google-cloud/trace-agent')
  33. const traceOpts = { ignoreUrls: [/^\/status/, /^\/health_check/] }
  34. traceAgent.start(traceOpts)
  35. }
  36. console.log(`ENABLE_DEBUG_AGENT set to ${process.env.ENABLE_DEBUG_AGENT}`)
  37. if (process.env.ENABLE_DEBUG_AGENT === 'true') {
  38. console.log('starting google debug agent')
  39. const debugAgent = require('@google-cloud/debug-agent')
  40. debugAgent.start({
  41. allowExpressions: true,
  42. serviceContext: {
  43. service: appName,
  44. version: process.env.BUILD_VERSION
  45. }
  46. })
  47. }
  48. console.log(`ENABLE_PROFILE_AGENT set to ${process.env.ENABLE_PROFILE_AGENT}`)
  49. if (process.env.ENABLE_PROFILE_AGENT === 'true') {
  50. console.log('starting google profile agent')
  51. const profiler = require('@google-cloud/profiler')
  52. profiler.start({
  53. serviceContext: {
  54. service: appName,
  55. version: process.env.BUILD_VERSION
  56. }
  57. })
  58. }
  59. inc('process_startup')
  60. }
  61. function registerDestructor(func) {
  62. destructors.push(func)
  63. }
  64. function injectMetricsRoute(app) {
  65. app.get(
  66. '/metrics',
  67. ExpressCompression({
  68. level: parseInt(process.env.METRICS_COMPRESSION_LEVEL || '1', 10)
  69. }),
  70. function(req, res) {
  71. res.set('Content-Type', promWrapper.registry.contentType)
  72. res.end(promWrapper.registry.metrics())
  73. }
  74. )
  75. }
  76. function buildPromKey(key) {
  77. return key.replace(/[^a-zA-Z0-9]/g, '_')
  78. }
  79. function sanitizeValue(value) {
  80. return parseFloat(value)
  81. }
  82. function set(key, value, sampleRate = 1) {
  83. console.log('counts are not currently supported')
  84. }
  85. function inc(key, sampleRate = 1, opts = {}) {
  86. key = buildPromKey(key)
  87. promWrapper.metric('counter', key).inc(opts)
  88. if (process.env.DEBUG_METRICS) {
  89. console.log('doing inc', key, opts)
  90. }
  91. }
  92. function count(key, count, sampleRate = 1, opts = {}) {
  93. key = buildPromKey(key)
  94. promWrapper.metric('counter', key).inc(opts, count)
  95. if (process.env.DEBUG_METRICS) {
  96. console.log('doing count/inc', key, opts)
  97. }
  98. }
  99. function summary(key, value, opts = {}) {
  100. key = buildPromKey(key)
  101. promWrapper.metric('summary', key).observe(opts, value)
  102. if (process.env.DEBUG_METRICS) {
  103. console.log('doing summary', key, value, opts)
  104. }
  105. }
  106. function timing(key, timeSpan, sampleRate = 1, opts = {}) {
  107. key = buildPromKey('timer_' + key)
  108. promWrapper.metric('summary', key).observe(opts, timeSpan)
  109. if (process.env.DEBUG_METRICS) {
  110. console.log('doing timing', key, opts)
  111. }
  112. }
  113. class Timer {
  114. constructor(key, sampleRate = 1, opts = {}) {
  115. this.start = new Date()
  116. key = buildPromKey(key)
  117. this.key = key
  118. this.sampleRate = sampleRate
  119. this.opts = opts
  120. }
  121. done() {
  122. const timeSpan = new Date() - this.start
  123. timing(this.key, timeSpan, this.sampleRate, this.opts)
  124. return timeSpan
  125. }
  126. }
  127. function gauge(key, value, sampleRate = 1, opts = {}) {
  128. key = buildPromKey(key)
  129. promWrapper
  130. .metric('gauge', key)
  131. .set({ status: opts.status }, sanitizeValue(value))
  132. if (process.env.DEBUG_METRICS) {
  133. console.log('doing gauge', key, opts)
  134. }
  135. }
  136. function globalGauge(key, value, sampleRate = 1, opts = {}) {
  137. key = buildPromKey(key)
  138. promWrapper
  139. .metric('gauge', key)
  140. .set({ host: 'global', status: opts.status }, sanitizeValue(value))
  141. }
  142. function close() {
  143. for (const func of destructors) {
  144. func()
  145. }
  146. }
  147. module.exports = {
  148. configure,
  149. initialize,
  150. registerDestructor,
  151. injectMetricsRoute,
  152. buildPromKey,
  153. sanitizeValue,
  154. set,
  155. inc,
  156. count,
  157. summary,
  158. timing,
  159. Timer,
  160. gauge,
  161. globalGauge,
  162. close,
  163. prom: promClient,
  164. register: promWrapper.registry,
  165. mongodb: require('./mongodb'),
  166. http: require('./http'),
  167. open_sockets: require('./open_sockets'),
  168. event_loop: require('./event_loop'),
  169. memory: require('./memory'),
  170. timeAsyncMethod: require('./timeAsyncMethod')
  171. }