Add metrics endpoint (#1515)

This commit adds the new package prom-client in order to expose metrics
to prometheus as well as the endpoint and the configuration to do so.
This commit is contained in:
Bruno Bacarini 2021-03-10 23:10:10 +00:00 committed by GitHub
parent 8d2371917f
commit b0b172a362
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 5 deletions

View file

@ -446,6 +446,20 @@ const config = convict({
},
endpoints: {
metrics: {
enable: {
format: 'Boolean',
default: false,
env: 'N8N_ENABLE_METRICS',
doc: 'Enable metrics endpoint',
},
prefix: {
format: String,
default: 'n8n_',
env: 'N8N_METRICS_PREFIX',
doc: 'An optional prefix for metric names. Default: n8n_',
},
},
rest: {
format: String,
default: 'rest',
@ -471,7 +485,7 @@ const config = convict({
doc: 'Disable production webhooks from main process. This helps ensures no http traffic load to main process when using webhook-specific processes.',
},
skipWebhoooksDeregistrationOnShutdown: {
/**
/**
* Longer explanation: n8n deregisters webhooks on shutdown / deactivation
* and registers on startup / activation. If we skip
* deactivation on shutdown, webhooks will remain active on 3rd party services.

View file

@ -111,6 +111,7 @@
"oauth-1.0a": "^2.2.6",
"open": "^7.0.0",
"pg": "^8.3.0",
"prom-client": "^13.1.0",
"request-promise-native": "^1.0.7",
"sqlite3": "^5.0.1",
"sse-channel": "^3.1.1",

View file

@ -23,6 +23,7 @@ import * as csrf from 'csrf';
import * as requestPromise from 'request-promise-native';
import { createHmac } from 'crypto';
import { compare } from 'bcryptjs';
import * as promClient from 'prom-client';
import {
ActiveExecutions,
@ -108,6 +109,7 @@ import * as parseUrl from 'parseurl';
import * as querystring from 'querystring';
import * as Queue from '../src/Queue';
import { OptionsWithUrl } from 'request-promise-native';
import { Registry } from 'prom-client';
class App {
@ -197,6 +199,16 @@ class App {
async config(): Promise<void> {
const enableMetrics = config.get('endpoints.metrics.enable') as boolean;
let register: Registry;
if (enableMetrics === true) {
const prefix = config.get('endpoints.metrics.prefix') as string;
register = new promClient.Registry();
register.setDefaultLabels({ prefix });
promClient.collectDefaultMetrics({ register });
}
this.versions = await GenericHelpers.getVersions();
this.frontendSettings.versionCli = this.versions.cli;
@ -204,7 +216,7 @@ class App {
const excludeEndpoints = config.get('security.excludeEndpoints') as string;
const ignoredEndpoints = ['healthz', this.endpointWebhook, this.endpointWebhookTest, this.endpointPresetCredentials];
const ignoredEndpoints = ['healthz', 'metrics', this.endpointWebhook, this.endpointWebhookTest, this.endpointPresetCredentials];
ignoredEndpoints.push.apply(ignoredEndpoints, excludeEndpoints.split(':'));
const authIgnoreRegex = new RegExp(`^\/(${_(ignoredEndpoints).compact().join('|')})\/?.*$`);
@ -386,7 +398,7 @@ class App {
this.app.use(history({
rewrites: [
{
from: new RegExp(`^\/(${this.restEndpoint}|healthz|css|js|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`),
from: new RegExp(`^\/(${this.restEndpoint}|healthz|metrics|css|js|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`),
to: (context) => {
return context.parsedUrl!.pathname!.toString();
},
@ -454,7 +466,16 @@ class App {
ResponseHelper.sendSuccessResponse(res, responseData, true, 200);
});
// ----------------------------------------
// Metrics
// ----------------------------------------
if (enableMetrics === true) {
this.app.get('/metrics', async (req: express.Request, res: express.Response) => {
const response = await register.metrics();
res.setHeader('Content-Type', register.contentType);
ResponseHelper.sendSuccessResponse(res, response, true, 200);
});
}
// ----------------------------------------
// Workflow
@ -1728,7 +1749,7 @@ class App {
stoppedAt: result.stoppedAt ? new Date(result.stoppedAt) : undefined,
finished: result.finished,
};
return returnData;
}