This is a simple AOP-ish toolset that lets to wrap sync and async functions and class methods with hooks which let you:
- override onTry:
- function's input parameters
 - function's this arg
 - function itself (hook can conditionally do some extra wrapping)
 
 - override onCatch, onFinally:
- function's result
 - function's error to (re-)throw
 
 - log, measure all of those mentioned above
 
It supports using as decorator, function wrapper or scope of anonymous function:
It supports typescript
It supports sync and async functions/methods
/// track.ts
export const hooks = new Hooks()
  .add(callStack)
  .add(measureDuration)
  .add(ctx => {
    console.log(`ℹ️ Action ${ctx.name} started...`)
    return {
      onFinally() {
        if(ctx.funcOutcome.error)
          console.log(`❌ Action ${ctx.name} failed with ${ctx.funcOutcome.error}. Took ${ctx.duration}ms to complete`)
        else
          console.log(`✅ Action ${ctx.name} succeed with result:${ctx.funcOutcome.result}. Took ${ctx.duration}ms to complete`)
        // you can override either result or error here
        // if you set ctx.funcOutcome.error = undefined - function will not throw error
        datadog.histogram(
          `action-${ctx.name}-duration`,
          ctx.duration,
          { tags: ['action:' + ctx.name, 'callstack:' + ctx.getCallStack().map(c => c.name).join('/')] }
        );
      },
    };
  })
export const track = hooks.create()
// myClass.ts
class MyClass{
  @track({name:'Hello world'})
  //@hooks.decor({name:'Hello world'}) //alternative
  async helloWorld(url:string)
  {
    await doing()
    await somethingLongRunning(url)
    return await andErroneous()
  }
}
// myFunc.ts
export const myFunc = track({name: 'My function'}, (param1:number)=>{   //alternative: myFunc = hooks.wrap({name:'Hello world'}, (param1...)=>{...})
    // doing something long running and risky
  step1()
  step2()
  const res = track.scope({name:'important step 3'},()=>{
    return step3(param1)
  })
  return res
})See specs for examples