https://github.com/samber/node-promfiler
Expose a http endpoint for exporting node.js v8 profiling
https://github.com/samber/node-promfiler
Last synced: 7 months ago
JSON representation
Expose a http endpoint for exporting node.js v8 profiling
- Host: GitHub
- URL: https://github.com/samber/node-promfiler
- Owner: samber
- License: mit
- Created: 2017-08-31T10:54:32.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2018-04-10T10:25:50.000Z (almost 8 years ago)
- Last Synced: 2025-04-02T19:49:19.114Z (9 months ago)
- Language: JavaScript
- Homepage:
- Size: 27.3 KB
- Stars: 7
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Live profiling exporter for Node.js, Prometheus and Grafana
## About flame graphs
[](http://spiermar.github.io/d3-flame-graph/)
Please read [Brendan Gregg's post](http://www.brendangregg.com/flamegraphs.html)
Flame graph are oriented graphs (like a tree).
Flame graph are useful for analysing the time spent on each node (or function execution, in case of program profiling).
Nodes have a "signature" (even if these are not exit nodes). This is the path from the `` node to itself.
The signature describes only the depth dimension. Multiple signatures shape a graph.
Node value:
- Nodes in a flame graph have a **virtual weight**. This is usually the time spent on each local node.
- A node **real weight** is equal to the virtual weight, plus the sum of the children real weight (recursion).
- The virtual weight is equal to the real weight, minus all children real weight.
- We only export virtual weight. The real weight is implicit and can be computed easily.
## Install
```
$ npm install -g promfiler
```
## Running as a separate cli
This execution mode exposes a HTTP endpoint for Prometheus metric sraping (`0.0.0.0:9142/metrics` by default).
```
Usage: node-promfiler [options] [argv...]
Options:
-V, --version output the version number
-h, --hostname Address to listen for metric interface.
-p, --port Port to listen for metric interface.
-P, --path Path under which to expose metrics.
-s, --sampling-interval Changes default CPU profiler sampling interval to the specified number of microseconds.
-h, --help output usage information
Examples:
$ promfiler ./app.js
$ promfiler ./app.js foo bar
$ promfiler --port 9090 ./app.js foo bar
```
## Running as a Node.JS library
Promfiler can start an external http server or let you reuse an existing Express/Hapi/Koa/... instance.
### Using the provided HTTP server
```js
const promfiler = require('promfiler');
promfiler.startServer({
hostname: '0.0.0.0', // optional, default 0.0.0.0
port: 9142, // optional, default 9142
path: '/metrics', // optional, default /metrics
}).then( (uri) => {
promfiler.startProfiling({
samplingInterval: 1000, // optional, default 1000
});
console.log("Profiler listening on %s", uri);
// your code here
});
```
### Using your Own HTTP server
```js
const promfiler = require('promfiler');
const express = require('express');
const app = express();
app.get('/metrics', function (req, res) {
res.send(promfiler.getMetrics());
});
app.listen(8080, function () {
promfiler.startProfiling({
samplingInterval: 1000, // optional, default 1000
});
});
```
## Configuring Promfiler
- Sampling interval: Changes default CPU profiler sampling interval to the specified number of microseconds. Default interval is 1000us. Decreasing this value may improve accuracy, but will also reduce speed of execution.
## Configuring Prometheus
```
scrape_configs:
- job_name: 'test'
scrape_interval: 30s
scrape_timeout: 3s
static_configs:
- targets: ['localhost:9142']
```
## Visualizing flame graphs
Install [grafana-flamegraph-panel](https://github.com/samber/grafana-flamegraph-panel) into your Grafana instance.
## Output format
```
$ promfiler demo/app.js
$ curl localhost:9142/metrics
```
```
# HELP promfiler_cpu_profile CPU stack trace samples
# TYPE promfiler_cpu_profile gauge
promfiler_cpu_profile{signature="(root)"} 0
promfiler_cpu_profile{signature="(root)#parserOnHeadersComplete"} 0
promfiler_cpu_profile{signature="(root)#parserOnHeadersComplete#parserOnIncoming"} 0
promfiler_cpu_profile{signature="(root)#parserOnHeadersComplete#parserOnIncoming#emit"} 0
promfiler_cpu_profile{signature="(root)#parserOnHeadersComplete#parserOnIncoming#emit#emitTwo"} 0
promfiler_cpu_profile{signature="(root)#parserOnHeadersComplete#parserOnIncoming#emit#emitTwo#http.createServer"} 0
promfiler_cpu_profile{signature="(root)#parserOnMessageComplete"} 1
promfiler_cpu_profile{signature="(root)#_tickCallback"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#onFinish"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#onFinish#emit"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#onFinish#emit#emitNone"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#onFinish#emit#emitNone#resOnFinish"} 1
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#afterWrite"} 1
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone#socketOnEnd"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone#socketOnEnd#Socket.end"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone#socketOnEnd#Socket.end#Writable.end"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone#socketOnEnd#Socket.end#Writable.end#endWritable"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone#socketOnEnd#Socket.end#Writable.end#endWritable#finishMaybe"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone#socketOnEnd#Socket.end#Writable.end#endWritable#finishMaybe#emit"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone#socketOnEnd#Socket.end#Writable.end#endWritable#finishMaybe#emit#emitNone"} 0
promfiler_cpu_profile{signature="(root)#_tickCallback#_combinedTickCallback#endReadableNT#emit#emitNone#socketOnEnd#Socket.end#Writable.end#endWritable#finishMaybe#emit#emitNone#onSocketFinish"} 1
promfiler_cpu_profile{signature="(root)#_handle.close"} 0
promfiler_cpu_profile{signature="(root)#_handle.close#emit"} 0
promfiler_cpu_profile{signature="(root)#_handle.close#emit#emitOne"} 0
promfiler_cpu_profile{signature="(root)#_handle.close#emit#emitOne#socketOnClose"} 0
promfiler_cpu_profile{signature="(root)#_handle.close#emit#emitOne#socketOnClose#freeParser"} 1
...
```
## Troubleshooting
I observed huge memory leaks, increasing over long running profling. This is due to v8-profiler library (and probably v8 :trollface:). You should not use it in production until it's fixed (or contribute !).