All files sentry-transport.ts

81.25% Statements 26/32
70.83% Branches 17/24
83.33% Functions 5/6
81.25% Lines 26/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 841x 1x   1x             2x 2x 2x 1x                 2x               2x   2x   1x 1x   1x 1x   1x 1x 1x         1x           2x 1x   1x 1x             1x 1x   1x                             1x  
import { LogLevel } from '@node-dlc/logger';
import * as Sentry from '@sentry/node';
 
export class SentryTransport implements ITransport {
  constructor(
    nodeOptions?: Sentry.NodeOptions & { transport?: any },
    tags: ITag[] = [],
  ) {
    // Only initialize Sentry if it hasn't been initialized yet
    // In test scenarios, Sentry may already be initialized by sentry-testkit
    try {
      const currentClient = Sentry.getCurrentHub().getClient();
      if (!currentClient && nodeOptions) {
        Sentry.init(nodeOptions);
      }
    } catch (e) {
      // If Sentry hasn't been initialized at all, initialize it
      if (nodeOptions) {
        Sentry.init(nodeOptions);
      }
    }
 
    tags.forEach((tag) => {
      Sentry.setTag(tag.key, tag.value);
    });
  }
 
  public write(line: string, level?: LogLevel, error?: Error): void {
    // Parse log level from the formatted message since @node-dlc/logger@0.24.0
    // doesn't pass the level parameter correctly
    const parsedLevel = this.parseLogLevel(line);
 
    switch (parsedLevel) {
      case LogLevel.Warn:
        Sentry.captureMessage(line);
        break;
      case LogLevel.Error:
        Sentry.withScope((scope) => {
          scope.setExtra('log', line);
          // Extract error message from formatted log and create Error object
          const errorMessage = this.extractErrorMessage(line);
          Eif (errorMessage) {
            Sentry.captureException(new Error(errorMessage));
          } else {
            Sentry.captureException(line);
          }
        });
        break;
      default:
    }
  }
 
  private parseLogLevel(line: string): LogLevel | undefined {
    if (line.includes('[WRN]:') || line.includes('[WARN]:')) {
      return LogLevel.Warn;
    }
    Eif (line.includes('[ERR]:') || line.includes('[ERROR]:')) {
      return LogLevel.Error;
    }
    return undefined;
  }
 
  private extractErrorMessage(line: string): string | null {
    // Extract error message from formatted log like: "2025-06-03T15:52:24.761Z [ERR]: Error: testing"
    const errorMatch = line.match(/\[ERR\]: Error: (.+)/);
    Eif (errorMatch && errorMatch[1]) {
      // Get just the first line of the error message (before stack trace)
      return errorMatch[1].split('\n')[0];
    }
    return null;
  }
}
 
export interface ITransport {
  write(line: string, level?: LogLevel, error?: Error): void;
}
 
export interface ITag {
  key: string;
  value: string;
}
 
export const SentryHandlers = Sentry.Handlers;