repo_description: Actionhero is a realtime multi-transport nodejs API Server with integrated cluster capabilities and delayed tasks
repo_topics: actionhero api api-server async-await cluster delayed-jobs delayed-tasks framework frameworks http https javascript nodejs server socket-client tcp ts typescript websocket
code: import { api, config, log, Initializer, Action } from "../index";
  import { ExceptionReporter } from "../classes/exceptionReporter";
  import { ParsedJob } from "node-resque";
  import { ActionheroLogLevel } from "../modules/log";
  
  export interface ExceptionHandlerAPI {
    reporters: Array<ExceptionReporter>;
    report: ExceptionsInitializer["report"];
    initializer: ExceptionsInitializer["initializer"];
    action: ExceptionsInitializer["action"];
    task: ExceptionsInitializer["task"];
  }
  
  /**
   * Handlers for when things go wrong.
   */
  export class ExceptionsInitializer extends Initializer {
    constructor() {
      super();
      this.name = "exceptions";
      this.loadPriority = 1;
    }
  
    report = (
      error: NodeJS.ErrnoException,
      type: string,
      name: string,
      objects?: any,
      severity?: ActionheroLogLevel,
    ) => {
      if (!severity) severity = "error";
  
      for (const reporter of api.exceptionHandlers.reporters) {
        reporter(error, type, name, objects, severity);
      }
    };
  
    initializer = (error: NodeJS.ErrnoException, fullFilePath: string) => {
      const name = "initializer:" + fullFilePath;
      api.exceptionHandlers.report(
        error,
        "initializer",
        name,
        { fullFilePath: fullFilePath },
        "alert",
      );
    };
  
    action = (
      error: NodeJS.ErrnoException,
      {
        to,
        action,
        params,
        duration,
        response,
      }: {
        to: string;
        action: Action["name"];
        params: string;
        duration: number;
        response: string;
      },
    ) => {
      api.exceptionHandlers.report(
        error,
        "action",
        `action: ${action}`,
        { to, action, params, duration, error, response },
        "alert",
      );
    };
  
    task = (
      error: NodeJS.ErrnoException,
      queue: string,
      task: ParsedJob,
      workerId: string | number,
    ) => {
      let simpleName;
      try {
        simpleName = task["class"];
      } catch (e) {
        simpleName = error.message;
      }
      const name = "task:" + simpleName;
      api.exceptionHandlers.report(
        error,
        "task",
        name,
        { task: task, queue: queue, workerId: workerId },
        config.tasks.workerLogging.failure,
      );
    };
  
    async initialize() {
      api.exceptionHandlers = {
        reporters: [],
        report: this.report,
        initializer: this.initializer,
        action: this.action,
        task: this.task,
      };
  
      api.exceptionHandlers.reporters.push(consoleReporter);
    }
  }
  
  const consoleReporter: ExceptionReporter = (
    error: NodeJS.ErrnoException & { [key: string]: any },
    type,
    name,
    objects,
    severity,
  ) => {
    let message = "";
    const data = error["data"] ?? {};
  
    if (type === "uncaught") {
      message = `Uncaught ${name}`;
    } else if (type === "action") {
      // no need to log anything, it was handled already by the actionProcessor
    } else if (type === "initializer") {
      message = `Error from Initializer`;
    } else if (type === "task") {
      message = `Error from Task`;
      data["name"] = name;
      data["queue"] = objects.queue;
      data["worker"] = objects.workerId;
      data["arguments"] = objects?.task?.args
        ? JSON.stringify(objects.task.args[0])
        : undefined;
    } else {
      message = `Error: ${error?.message || error.toString()}`;
      Object.getOwnPropertyNames(error)
        .filter((prop) => prop !== "message")
        .sort((a, b) => (a === "stack" || b === "stack" ? -1 : 1))
        .forEach((prop) => (data[prop] = error[prop]));
      data["type"] = type;
      data["name"] = name;
      data["data"] = objects;
    }
  
    if (error["stack"]) {
      data["stack"] = error.stack;
    } else {
      data["stack"] = error.message ?? error.toString();
    }
  
    try {
      if (message) log(message, severity, data);
    } catch (e) {
      console.log(message, data);
    }
  };
  