feat(core): Add Prometheus metrics for n8n events and api invocations (experimental) (#5177)

* create prometheus metrics from events

* feat(core): Add more Prometheus metrics (experimental) (#5187)

* refactor(core): Add Prometheus labels to relevant metrics

* feat(core): Add more Prometheus metrics (experimental)

* add 'v' prefix to value of version label

Co-authored-by: Cornelius Suermann <cornelius@n8n.io>
This commit is contained in:
Michael Auerswald
2023-01-19 12:11:31 +01:00
committed by GitHub
parent 4ebf40c7a2
commit 9b032d68bc
11 changed files with 257 additions and 24 deletions

View File

@@ -1,6 +1,13 @@
/* eslint-disable import/no-cycle */
import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
import type { EventDestinations } from '@/databases/entities/MessageEventBusDestinationEntity';
import { promClient } from '@/metrics';
import {
EventMessageTypeNames,
LoggerProxy,
MessageEventBusDestinationTypeNames,
} from 'n8n-workflow';
import config from '../../config';
import type { EventMessageTypes } from '../EventMessageClasses';
import type { MessageEventBusDestination } from './MessageEventBusDestination.ee';
import { MessageEventBusDestinationSentry } from './MessageEventBusDestinationSentry.ee';
import { MessageEventBusDestinationSyslog } from './MessageEventBusDestinationSyslog.ee';
@@ -24,3 +31,85 @@ export function messageEventBusDestinationFromDb(
}
return null;
}
const prometheusCounters: Record<string, promClient.Counter<string> | null> = {};
function getMetricNameForEvent(event: EventMessageTypes): string {
const prefix = config.getEnv('endpoints.metrics.prefix');
return prefix + event.eventName.replace('n8n.', '').replace(/\./g, '_') + '_total';
}
function getLabelValueForNode(nodeType: string): string {
return nodeType.replace('n8n-nodes-', '').replace(/\./g, '_');
}
function getLabelValueForCredential(credentialType: string): string {
return credentialType.replace(/\./g, '_');
}
function getLabelsForEvent(event: EventMessageTypes): Record<string, string> {
switch (event.__type) {
case EventMessageTypeNames.audit:
if (event.eventName.startsWith('n8n.audit.user.credentials')) {
return config.getEnv('endpoints.metrics.includeCredentialTypeLabel')
? {
credential_type: getLabelValueForCredential(
event.payload.credentialType ?? 'unknown',
),
}
: {};
}
if (event.eventName.startsWith('n8n.audit.workflow')) {
return config.getEnv('endpoints.metrics.includeWorkflowIdLabel')
? { workflow_id: event.payload.workflowId?.toString() ?? 'unknown' }
: {};
}
break;
case EventMessageTypeNames.node:
return config.getEnv('endpoints.metrics.includeNodeTypeLabel')
? { node_type: getLabelValueForNode(event.payload.nodeType ?? 'unknown') }
: {};
case EventMessageTypeNames.workflow:
return config.getEnv('endpoints.metrics.includeWorkflowIdLabel')
? { workflow_id: event.payload.workflowId?.toString() ?? 'unknown' }
: {};
}
return {};
}
function getCounterSingletonForEvent(event: EventMessageTypes) {
if (!prometheusCounters[event.eventName]) {
const metricName = getMetricNameForEvent(event);
if (!promClient.validateMetricName(metricName)) {
LoggerProxy.debug(`Invalid metric name: ${metricName}. Ignoring it!`);
prometheusCounters[event.eventName] = null;
return null;
}
const counter = new promClient.Counter({
name: metricName,
help: `Total number of ${event.eventName} events.`,
labelNames: Object.keys(getLabelsForEvent(event)),
});
promClient.register.registerMetric(counter);
prometheusCounters[event.eventName] = counter;
}
return prometheusCounters[event.eventName];
}
export async function incrementPrometheusMetric(event: EventMessageTypes): Promise<void> {
const counter = getCounterSingletonForEvent(event);
if (!counter) {
return;
}
counter.inc(getLabelsForEvent(event));
}