diff --git a/README.md b/README.md index 2ca0c0a..0405aa7 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,12 @@ You can also use `npx`: ``` +To specify a custom logger function, use `setTraceUnhandledLogger`: + +```ts +window.setTraceUnhandledLogger( msg => { ... } ); // msg is a string +``` + ## Programatically - API @@ -127,6 +133,13 @@ const { register } = require( 'trace-unhandled' ); register( ); ``` +To specify a custom logger function, use `setLogger`: + +```ts +const { setLogger } = require( 'trace-unhandled' ); +setLogger( msg => { ... } ); // msg is a string +``` + ## In unit tests diff --git a/index.ts b/index.ts index 72135ac..ded7f4c 100644 --- a/index.ts +++ b/index.ts @@ -1,2 +1,2 @@ - +export { LoggerFunction, setLogger } from './lib/core'; export const register = ( ) => require( './lib/register' ); diff --git a/jest.config.js b/jest.config.js index d47197d..9b630f9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,4 +2,5 @@ module.exports = { testEnvironment: "node", testMatch: ['/test/**/*.spec.js'], coverageReporters: ["lcov", "text", "html"], + coveragePathIgnorePatterns: [ '/node_modules/', '/test/' ], }; diff --git a/lib/core.ts b/lib/core.ts index aff9090..4f70dd5 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -54,6 +54,20 @@ function mergeErrors( traceError: Error, mainError?: Error ) errorLines.slice( 0, i ).reverse( ).join( "\n" ); } +function defaultLogger( msg: string ) +{ + console.error( msg ); +} + +export type LoggerFunction = ( msg: string ) => void; + +let loggerFunction: LoggerFunction = defaultLogger; + +export function setLogger( loggerFn: LoggerFunction ) +{ + loggerFunction = loggerFn ?? defaultLogger; +} + export function logger( reason: Error | undefined, promise: TraceablePromise< any >, @@ -69,7 +83,7 @@ export function logger( const prefix = pid == null ? '' : `(node:${pid}) `; - console.error( + loggerFunction( `${prefix}UnhandledPromiseRejectionWarning\n` + ( !promise.__tracedError diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..9d79bbd --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,41 @@ +const { Finally, Try, delay } = require( 'already' ); + +module.exports.withConsoleSpy = fn => async ( ) => +{ + const oldError = console.error; + console.error = jest.fn( ); + return Try( fn ) + .then( ...Finally( ( ) => + { + console.error = oldError; + } ) ); +} + +module.exports.triggerUnhandledWarnings = + async function triggerUnhandledWarnings( ) +{ + await delay( 0 ); + global.gc && global.gc( ); + await delay( 0 ); +} + +module.exports.splitLines = function splitLines( lines ) +{ + return [ ].concat( ...lines.map( line => line.split( "\n" ) ) ); +} + +module.exports.cutColumn = function cutColumn( line ) +{ + const m = line.match( /(.*:\d+):\d+\)?$/ ); + if ( !m ) + return line; + return m[ 1 ]; +} + +module.exports.cutLocation = function cutLocation( line ) +{ + const m = line.match( /(.*):\d+:\d+\)?$/ ); + if ( !m ) + return line; + return m[ 1 ]; +} diff --git a/test/logger.spec.js b/test/logger.spec.js new file mode 100644 index 0000000..c90ec32 --- /dev/null +++ b/test/logger.spec.js @@ -0,0 +1,44 @@ + +const index = require( '../dist/index' ); +const { + triggerUnhandledWarnings, + splitLines, + cutColumn, +} = require( './helpers' ); + + +describe( "logger", ( ) => +{ + it( "setLogger", async ( ) => + { + const logger = jest.fn( ); + index.setLogger( logger ); + index.register( ); + + const err = new Error( "foobar" ); + + new Promise( ( resolve, reject ) => + { + throw err; + } ); + + await triggerUnhandledWarnings( ); + + index.setLogger( ); + + const linesWoColumns = splitLines( logger.mock.calls[ 0 ] ) + .filter( line => line.includes( "logger.spec.js" ) ) + .map( line => cutColumn( line ) ); + + const errorAndShared = splitLines( logger.mock.calls[ 0 ] ) + .filter( line => line ); + + expect( linesWoColumns.length ).toBeGreaterThanOrEqual( 2 ); + expect( linesWoColumns[ 0 ] ).not.toBe( linesWoColumns[ 1 ] ); + + expect( errorAndShared ) + .toContain( " ==== Error at: ====================" ); + expect( errorAndShared ) + .toContain( " ==== Shared trace: ================" ); + } ); +} ); diff --git a/test/register.spec.js b/test/register.spec.js index 817df49..da98921 100644 --- a/test/register.spec.js +++ b/test/register.spec.js @@ -1,45 +1,14 @@ -const { Finally, Try, delay } = require( 'already' ); -require( '../register' ); - -const withConsoleSpy = fn => async ( ) => -{ - const oldError = console.error; - console.error = jest.fn( ); - return Try( fn ) - .then( ...Finally( ( ) => - { - console.error = oldError; - } ) ); -} +const { + withConsoleSpy, + triggerUnhandledWarnings, + splitLines, + cutColumn, + cutLocation, +} = require( './helpers' ); -async function triggerUnhandledWarnings( ) -{ - await delay( 0 ); - global.gc && global.gc( ); - await delay( 0 ); -} - -function splitLines( lines ) -{ - return [ ].concat( ...lines.map( line => line.split( "\n" ) ) ); -} - -function cutColumn( line ) -{ - const m = line.match( /(.*:\d+):\d+\)?$/ ); - if ( !m ) - return line; - return m[ 1 ]; -} +require( '../register' ); -function cutLocation( line ) -{ - const m = line.match( /(.*):\d+:\d+\)?$/ ); - if ( !m ) - return line; - return m[ 1 ]; -} describe( "register", ( ) => { @@ -151,7 +120,7 @@ describe( "register", ( ) => const errorAndShared = splitLines( console.error.mock.calls[ 0 ] ) .filter( line => line ); - expect( linesWoColumns.length ).toBeGreaterThanOrEqual( 2 ); + expect( linesWoColumns.length ).toBeGreaterThanOrEqual( 1 ); expect( linesWoColumns[ 0 ] ).not.toBe( linesWoColumns[ 1 ] ); expect( errorAndShared[ errorAndShared.length - 2 ] ).toBe( @@ -180,7 +149,7 @@ describe( "register", ( ) => const errorAndShared = splitLines( console.error.mock.calls[ 0 ] ) .filter( line => line ); - expect( linesWoColumns.length ).toBeGreaterThanOrEqual( 2 ); + expect( linesWoColumns.length ).toBeGreaterThanOrEqual( 1 ); expect( linesWoColumns[ 0 ] ).not.toBe( linesWoColumns[ 1 ] ); expect( errorAndShared[ errorAndShared.length - 2 ] ).toBe( diff --git a/web/register-web.ts b/web/register-web.ts index 61bd713..7ec658b 100644 --- a/web/register-web.ts +++ b/web/register-web.ts @@ -1,5 +1,5 @@ -import { logger, TraceablePromise } from "../lib/core"; +import { logger, TraceablePromise, setLogger} from "../lib/core"; window.onunhandledrejection = function( event: PromiseRejectionEvent ) { @@ -7,3 +7,4 @@ window.onunhandledrejection = function( event: PromiseRejectionEvent ) }; ( < any >window ).Promise = TraceablePromise; +( < any >window ).setTraceUnhandledLogger = setLogger;