Files
n8n-enterprise-unlocked/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestinationSentry.ee.ts
2023-11-27 09:11:52 +01:00

129 lines
4.0 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { MessageEventBusDestination } from './MessageEventBusDestination.ee';
import * as Sentry from '@sentry/node';
import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
import type {
MessageEventBusDestinationOptions,
MessageEventBusDestinationSentryOptions,
} from 'n8n-workflow';
import { isLogStreamingEnabled } from '../MessageEventBus/MessageEventBusHelper';
import { eventMessageGenericDestinationTestEvent } from '../EventMessageClasses/EventMessageGeneric';
import { N8N_VERSION } from '@/constants';
import type { MessageEventBus, MessageWithCallback } from '../MessageEventBus/MessageEventBus';
export const isMessageEventBusDestinationSentryOptions = (
candidate: unknown,
): candidate is MessageEventBusDestinationSentryOptions => {
const o = candidate as MessageEventBusDestinationSentryOptions;
if (!o) return false;
return o.dsn !== undefined;
};
export class MessageEventBusDestinationSentry
extends MessageEventBusDestination
implements MessageEventBusDestinationSentryOptions
{
dsn: string;
tracesSampleRate = 1.0;
sendPayload: boolean;
sentryClient?: Sentry.NodeClient;
constructor(eventBusInstance: MessageEventBus, options: MessageEventBusDestinationSentryOptions) {
super(eventBusInstance, options);
this.label = options.label ?? 'Sentry DSN';
this.__type = options.__type ?? MessageEventBusDestinationTypeNames.sentry;
this.dsn = options.dsn;
if (options.sendPayload) this.sendPayload = options.sendPayload;
if (options.tracesSampleRate) this.tracesSampleRate = options.tracesSampleRate;
const { ENVIRONMENT: environment } = process.env;
this.sentryClient = new Sentry.NodeClient({
dsn: this.dsn,
tracesSampleRate: this.tracesSampleRate,
environment,
release: N8N_VERSION,
transport: Sentry.makeNodeTransport,
integrations: Sentry.defaultIntegrations,
stackParser: Sentry.defaultStackParser,
});
}
async receiveFromEventBus(emitterPayload: MessageWithCallback): Promise<boolean> {
const { msg, confirmCallback } = emitterPayload;
let sendResult = false;
if (!this.sentryClient) return sendResult;
if (msg.eventName !== eventMessageGenericDestinationTestEvent) {
if (!isLogStreamingEnabled()) return sendResult;
if (!this.hasSubscribedToEvent(msg)) return sendResult;
}
try {
const payload = this.anonymizeAuditMessages ? msg.anonymize() : msg.payload;
const scope: Sentry.Scope = new Sentry.Scope();
const level = (
msg.eventName.toLowerCase().endsWith('error') ? 'error' : 'log'
) as Sentry.SeverityLevel;
scope.setLevel(level);
scope.setTags({
event: msg.getEventName(),
logger: this.label ?? this.getId(),
app: 'n8n',
});
if (this.sendPayload) {
scope.setExtras(payload);
}
const sentryResult = this.sentryClient.captureMessage(
msg.message ?? msg.eventName,
level,
{ event_id: msg.id, data: payload },
scope,
);
if (sentryResult) {
// eventBus.confirmSent(msg, { id: this.id, name: this.label });
confirmCallback(msg, { id: this.id, name: this.label });
sendResult = true;
}
} catch (error) {
if (error.message) this.logger.debug(error.message as string);
}
return sendResult;
}
serialize(): MessageEventBusDestinationSentryOptions {
const abstractSerialized = super.serialize();
return {
...abstractSerialized,
dsn: this.dsn,
tracesSampleRate: this.tracesSampleRate,
sendPayload: this.sendPayload,
};
}
static deserialize(
eventBusInstance: MessageEventBus,
data: MessageEventBusDestinationOptions,
): MessageEventBusDestinationSentry | null {
if (
'__type' in data &&
data.__type === MessageEventBusDestinationTypeNames.sentry &&
isMessageEventBusDestinationSentryOptions(data)
) {
return new MessageEventBusDestinationSentry(eventBusInstance, data);
}
return null;
}
toString() {
return JSON.stringify(this.serialize());
}
async close() {
await super.close();
await this.sentryClient?.close();
}
}