diff --git a/packages/functions/src/async.js b/packages/functions/src/async.js index 0a41aa2..9e29e6a 100644 --- a/packages/functions/src/async.js +++ b/packages/functions/src/async.js @@ -6,30 +6,40 @@ const logger = moduleLogger("tracer"); const _asyncHandler = (fn, trace = false) => - async (req, res, next) => { - let fnName; - try { - if (trace) { - fnName = _fnName(fn); - await _traced(fn.bind(this, req, res, next), {}, fnName); - } else { - await fn(req, res, next); + async (req, res, next) => { + let fnName; + try { + if (trace) { + fnName = _fnName(fn); + await _traced(fn.bind(this, req, res, next), {}, fnName); + } else { + await fn(req, res, next); + } + if (!res.headersSent) next(); + } catch (err) { + if (!trace) { + fnName = fnName ?? _fnName(fn); + logger.error(`${fnName} execution failed - error: ${err.message} - stack: ${err.stack}`); + } + res.errorLogged = true; + if (!res.headersSent) next(err); } - if (!res.headersSent) next(); - } catch (err) { - if (!trace) { - fnName = fnName ?? _fnName(fn); - logger.error(`${fnName} execution failed - error: ${err.message} - stack: ${err.stack}`); - } - res.errorLogged = true; - if (!res.headersSent) next(err); - } - }; + }; export const asyncHandler = (fn) => _asyncHandler(fn); export const tracedAsyncHandler = (fn) => _asyncHandler(fn, true); +export const fallibleAsyncHandler = (fn) => async (req, res, next) => { + try { + await _traced(fn.bind(this, req, res, next), {}, _fnName(fn), null, true); + if (!res.headersSent) next(); + } catch (err) { + res.errorLogged = true; + if (!res.headersSent) next(err); + } +}; + export const plainAsyncHandler = (fn) => async (req, res, next) => { try { const result = fn(req, res, next) @@ -42,5 +52,6 @@ export const plainAsyncHandler = (fn) => async (req, res, next) => { export default { asyncHandler, tracedAsyncHandler, + fallibleAsyncHandler, plainAsyncHandler, }; diff --git a/packages/functions/src/traced.js b/packages/functions/src/traced.js index 7149d67..cc32a5f 100644 --- a/packages/functions/src/traced.js +++ b/packages/functions/src/traced.js @@ -5,10 +5,10 @@ import { fnName as _fnName } from "./utils"; const logger = moduleLogger("tracer"); -export const _traced = (fn, loggable = {}, fnName, layer) => { +const disableTracing = process.env.DISABLE_FUNCTION_TRACING === "true" || process.env.DISABLE_FUNCTION_TRACING === "1"; + +export const _traced = (fn, loggable = {}, fnName, layer, fallible) => { let startTime; - const disableTracing = - process.env.DISABLE_FUNCTION_TRACING === "true" || process.env.DISABLE_FUNCTION_TRACING === "1"; if (!disableTracing) { fnName = fnName ?? _fnName(fn, layer); logger.info(`${fnName} execution initiated`, loggable); @@ -25,7 +25,7 @@ export const _traced = (fn, loggable = {}, fnName, layer) => { }; const failureLog = (err) => { if (!disableTracing && !err.isLogged) { - logger.error( + logger[fallible ? "warn" : "error"]( `${fnName} execution failed - ${chalk.bold("error")}: ${err.message} - ${chalk.bold("stack")}: ${err.stack}` ); err.isLogged = true; diff --git a/packages/functions/test/__mocks__/logger.js b/packages/functions/test/__mocks__/logger.js index 00b640c..850d2fe 100644 --- a/packages/functions/test/__mocks__/logger.js +++ b/packages/functions/test/__mocks__/logger.js @@ -1,6 +1,7 @@ export const mockLogger = { info: jest.fn().mockImplementation((msg) => console.info(msg)), - error: jest.fn().mockImplementation((msg) => console.error(msg)) + error: jest.fn().mockImplementation((msg) => console.error(msg)), + warn: jest.fn().mockImplementation((msg) => console.warn(msg)) }; jest.mock("@sliit-foss/module-logger", () => ({ diff --git a/packages/functions/test/async.test.js b/packages/functions/test/async.test.js index efcdd1c..1838092 100644 --- a/packages/functions/test/async.test.js +++ b/packages/functions/test/async.test.js @@ -1,6 +1,6 @@ const { mockLogger } = require("./__mocks__"); -const { asyncHandler, tracedAsyncHandler, plainAsyncHandler } = require("../src"); +const { asyncHandler, tracedAsyncHandler, fallibleAsyncHandler, plainAsyncHandler } = require("../src"); const { coloredFnName } = require("../src/utils"); beforeEach(() => { @@ -35,6 +35,13 @@ describe("asyncHandler", () => { expect(mockLogger.info).toHaveBeenCalledWith(`${coloredFnName("testTracedFunction")} execution initiated`, {}); expect(mockNext).toHaveBeenCalled(); }); + test("test fallible async handler", async () => { + await fallibleAsyncHandler(function testTracedFunction() { + throw new Error("test error"); + })(mockReq, mockRes, mockNext); + expect(mockLogger.warn).toHaveBeenCalled(); + expect(mockNext).toHaveBeenCalled(); + }); test("test plain async handler with async function", async () => { await plainAsyncHandler(async () => { throw new Error("test")