UNPKG

signalfx-tracing

Version:

Provides auto-instrumentation for JavaScript libraries and frameworks

265 lines (208 loc) 7.23 kB
'use strict' // TODO: capture every second and flush every 10 seconds const v8 = require('v8') const path = require('path') const Client = require('./dogstatsd') const log = require('../../log') const INTERVAL = 10 * 1000 let nativeMetrics = null let metrics let interval let client let time let cpuUsage let counters reset() module.exports = function () { return metrics || (metrics = { // cache the metrics instance start: (options) => { const tags = [ `service:${this._config.service}`, `runtime-id:${this.runtime().id()}` ] if (this._config.env) { tags.push(`env:${this._config.env}`) } options = options || {} try { nativeMetrics = require('node-gyp-build')(path.join(__dirname, '..', '..', '..')) nativeMetrics.start() } catch (e) { log.error('Unable to load native metrics module. Some metrics will not be available.') } client = new Client({ host: this._config.hostname, port: this._config.dogstatsd.port, prefix: 'runtime.node.', tags }) time = process.hrtime() if (nativeMetrics) { interval = setInterval(() => { captureCommonMetrics() captureNativeMetrics() client.flush() }, INTERVAL) } else { cpuUsage = process.cpuUsage() interval = setInterval(() => { captureCommonMetrics() captureCpuUsage() captureHeapSpace() client.flush() }, INTERVAL) } interval.unref() }, stop: () => { if (nativeMetrics) { nativeMetrics.stop() } clearInterval(interval) reset() }, track (span) { if (nativeMetrics) { const handle = nativeMetrics.track(span) return { finish: () => nativeMetrics.finish(handle) } } return { finish: () => {} } }, count (name, count, tag) { if (!client) return if (!counters[name]) { counters[name] = tag ? Object.create(null) : 0 } if (tag) { counters[name][tag] = (counters[name][tag] || 0) + count } else { counters[name] = (counters[name] || 0) + count } }, increment (name, tag) { this.count(name, 1, tag) }, decrement (name, tag) { this.count(name, -1, tag) } }) } function reset () { interval = null client = null time = null cpuUsage = null counters = {} } function captureCpuUsage () { if (!process.cpuUsage) return const elapsedTime = process.hrtime(time) const elapsedUsage = process.cpuUsage(cpuUsage) time = process.hrtime() cpuUsage = process.cpuUsage() const elapsedMs = elapsedTime[0] * 1000 + elapsedTime[1] / 1000000 const userPercent = 100 * elapsedUsage.user / 1000 / elapsedMs const systemPercent = 100 * elapsedUsage.system / 1000 / elapsedMs const totalPercent = userPercent + systemPercent client.gauge('cpu.system', systemPercent.toFixed(2)) client.gauge('cpu.user', userPercent.toFixed(2)) client.gauge('cpu.total', totalPercent.toFixed(2)) } function captureMemoryUsage () { const stats = process.memoryUsage() client.gauge('mem.heap_total', stats.heapTotal) client.gauge('mem.heap_used', stats.heapUsed) client.gauge('mem.rss', stats.rss) stats.external && client.gauge('mem.external', stats.external) } function captureProcess () { client.gauge('process.uptime', Math.round(process.uptime())) } function captureHeapStats () { const stats = v8.getHeapStatistics() client.gauge('heap.total_heap_size', stats.total_heap_size) client.gauge('heap.total_heap_size_executable', stats.total_heap_size_executable) client.gauge('heap.total_physical_size', stats.total_physical_size) client.gauge('heap.total_available_size', stats.total_available_size) client.gauge('heap.heap_size_limit', stats.heap_size_limit) stats.malloced_memory && client.gauge('heap.malloced_memory', stats.malloced_memory) stats.peak_malloced_memory && client.gauge('heap.peak_malloced_memory', stats.peak_malloced_memory) } function captureHeapSpace () { if (!v8.getHeapSpaceStatistics) return const stats = v8.getHeapSpaceStatistics() for (let i = 0, l = stats.length; i < l; i++) { const tags = [`space:${stats[i].space_name}`] client.gauge('heap.size.by.space', stats[i].space_size, tags) client.gauge('heap.used_size.by.space', stats[i].space_used_size, tags) client.gauge('heap.available_size.by.space', stats[i].space_available_size, tags) client.gauge('heap.physical_size.by.space', stats[i].physical_space_size, tags) } } function captureCounters () { Object.keys(counters).forEach(name => { if (typeof counters[name] === 'object') { Object.keys(counters[name]).forEach(tag => { client.gauge(name, counters[name][tag], [tag]) }) } else { client.gauge(name, counters[name]) } }) } function captureCommonMetrics () { captureMemoryUsage() captureProcess() captureHeapStats() captureCounters() } function captureNativeMetrics () { const stats = nativeMetrics.stats() const spaces = stats.heap.spaces const elapsedTime = process.hrtime(time) time = process.hrtime() const elapsedUs = elapsedTime[0] * 1e6 + elapsedTime[1] / 1e3 const userPercent = 100 * stats.cpu.user / elapsedUs const systemPercent = 100 * stats.cpu.system / elapsedUs const totalPercent = userPercent + systemPercent client.gauge('cpu.system', systemPercent.toFixed(2)) client.gauge('cpu.user', userPercent.toFixed(2)) client.gauge('cpu.total', totalPercent.toFixed(2)) histogram('event_loop.delay', stats.eventLoop) Object.keys(stats.gc).forEach(type => { if (type === 'all') { histogram('gc.pause', stats.gc[type]) } else { histogram('gc.pause.by.type', stats.gc[type], { gc_type: type }) } }) client.gauge('spans.finished', stats.spans.total.finished) client.gauge('spans.unfinished', stats.spans.total.unfinished) for (let i = 0, l = spaces.length; i < l; i++) { const tags = [`heap_space:${spaces[i].space_name}`] client.gauge('heap.size.by.space', spaces[i].space_size, tags) client.gauge('heap.used_size.by.space', spaces[i].space_used_size, tags) client.gauge('heap.available_size.by.space', spaces[i].space_available_size, tags) client.gauge('heap.physical_size.by.space', spaces[i].physical_space_size, tags) } if (stats.spans.operations) { const operations = stats.spans.operations Object.keys(operations.finished).forEach(name => { client.gauge('spans.finished.by.name', operations.finished[name], [`span_name:${name}`]) }) Object.keys(operations.unfinished).forEach(name => { client.gauge('spans.unfinished.by.name', operations.unfinished[name], [`span_name:${name}`]) }) } } function histogram (name, stats) { client.gauge(`${name}.min`, stats.min) client.gauge(`${name}.max`, stats.max) client.increment(`${name}.sum`, stats.sum) client.gauge(`${name}.avg`, stats.avg) client.increment(`${name}.count`, stats.count) client.gauge(`${name}.median`, stats.median) client.gauge(`${name}.95percentile`, stats.p95) }