Skip to content

Instantly share code, notes, and snippets.

@i-like-robots
Last active June 6, 2023 13:18
Show Gist options
  • Save i-like-robots/ffce87d7330e51474b31fa0f0dec32a3 to your computer and use it in GitHub Desktop.
Save i-like-robots/ffce87d7330e51474b31fa0f0dec32a3 to your computer and use it in GitHub Desktop.
Instrument Node.js HTTP requests
// Based on <https://blog.bearer.sh/http-api-instrumentation-nodejs/>
const http = require('http')
const https = require('https')
const { URL } = require('url')
const Metrics = require('metrics')
const { performance } = require('perf_hooks')
const metrics = {}
const metricTypes = {
counter: () => new Metrics.Counter(),
histogram: () => Metrics.Histogram.createUniformHistogram(),
}
function getServiceName(url) {
const { host } = new URL(url)
return host.replace(/\./g, '-')
}
function getMetric(url, type, key) {
const service = getServiceName(url)
metrics[service] ??= {}
metrics[service][key] ??= metricTypes[type]()
return metrics[service][key]
}
function patchModule(module) {
const original = module.request
function newRequest(outgoing) {
// Using perf.now() instead of Date.now() not because we need the resolution
// but because we can avoid being bound to the system's time and its quirks.
const startTime = performance.now()
const request = original.apply(this, arguments)
const { emit } = request
request.emit = function (event, response) {
if (event === 'response') {
response.on('end', () => {
const endTime = performance.now()
const { statusCode } = response
const time = Math.ceil(endTime - startTime)
const host = outgoing.host || outgoing.hostname
getMetric(host, 'counter', `status_${statusCode}.count`).inc(1)
getMetric(host, 'histogram', `status_${statusCode}.response_time`).update(time)
})
}
return emit.apply(this, arguments)
}
return request
}
module.request = newRequest
}
function instrumentRequests() {
patchModule(http)
patchModule(https)
}
module.exports = instrumentRequests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment