@avanzu/decorators v1.2.2
@avanzu/decorators
Decorators allowing to extend and alter behavior of methods
Attach logging to class methods
import { Log, LogAsync } from '@avanzu/decorators'
class Example {
@Log()
public demo(myArg: number) : string {
return `demo`
}
@LogAsync()
public async demoAsync(myArg: number) : Promise<string> {
return 'demo-async'
}
}During execution of the decorated methods, you will now have log entries
Example.demo() {
time: 1714361138410,
className: 'LogTest',
methodName: 'demo',
ms: 0
}
LogTest.demoAsync() {
time: 1714361138410,
className: 'LogTest',
methodName: 'demo',
ms: 0
}Decorator options
In order to customize the logging behavior, both decorators accept configuration options.
enter(boolean) - wether to generate a log entry before the actual method call. Default:falseexit(boolean) - wether to generate a log entry after the method call. Default:trueerror(boolean) - wether to generate a log entry when an error occured during method execution. Default:truemessage(string) - the log message that should be used for the log entry. Default:${ClassName}.${MethodName}()args(boolean) - wether to include the method arguments in the log context. Default:falseresult(boolean) - wether to include the return value of the method call in the log context. Default:falseenterPrefix(string) - string to prepend to theenterlog message. Default:''errorPrefix(string) - string to prepend to theexitlog message. Default:''exitPrefix(string) - string to prepent to theerrorlog message. Default:''level('debug'|'info'|'warn'|'error') - the log level ofenterandexitlogs. Default:'info'
Global configuration
In order to to configure the logging behavior globally, you can use the same decorator options on the LogBox
import { LogBox } from '@avanzu/decorators'
LogBox.configure({
// ...
})Change the logger
You can change the logger from console to anything else that implements the Logger interface.
import { LogBox } from '@avanzu/decorators'
import { myLogger } from './logger'
LogBox.use(myLogger)Attach plugin capabilities to classes
Make your class PluginAware, decorate the methods as Pluggable that should allow plugins.
// ./src/pluginDemo.class.ts
import { PluginAware, Pluggable } from '@avanzu/decorators'
@PlguinAware()
class PluginDemo {
@Pluggable()
async doFoo(myArg: string) : Promise<string>{
return `did foo with ${myArg}`
}
@Pluggable()
async doBar(myArg: number): Promise<string>{
return `didBar`
}
}Create your plugin(s)
// ./src/plugins/demo.plugin.ts
import { Plugin, ExecutionContext } from '@avanzu/decorators'
import { PluginDemo } from './pluginDemo.class'
type FooContext = ExecutionContext<[string], string>
@Plugin({ target: PluginDemo })
class DemoPlugin {
async beforeDoFoo({ params }: FooContext) : Promise<void> {
params.at(0) = params.at(0).concat('[extended before]')
}
async afterDoFoo(context: FooContext) : Promise<FooContext> {
return {
...context,
result: '[extended after]'.concat(context.result)
}
}
}Register your plugin instance
// ./src/main.ts
import { Plugins } from '@avanzu/decorators'
import { PluginDemo } from './pluginDemo.class'
import { DemoPlugin } from './plugins/demo.plugin'
Plugins.register(PluginDemo, new DemoPlugin())Execute your plugin aware method
// ./src/main.ts
const pluginDemo = new PluginDemo()
const result = await pluginDemo.doFoo('foo')
// -> [extended after] did foo with foo[extended before]Using symbols or strings
Instead of using class constructors to correlate a plugin with its corresponding class, you can also use symbols or strings.
Declare your key(s)
// ./src/types.ts
export const Types = {
Demo: Symbol('pluginAware.demo'),
// ...
}Assing the key to your plugin aware class
// ./src/pluginDemo.class.ts
import { PluginAware, Pluggable } from '@avanzu/decorators'
import { Types } from './types'
@PlguinAware({ key: Types.Demo })
class PluginDemo {
// ...
}Assing the target to your plugin
// ./src/plugins/demo.plugin.ts
import { Plugin, ExecutionContext } from '@avanzu/decorators'
import { Types } from '../types'
// ...
@Plugin({ target: Types.Demo })
class DemoPlugin {
//...
}Register the plugin instance with the corresponding key
// ./src/main.ts
import { Plugins } from '@avanzu/decorators'
import { DemoPlugin } from './plugins/demo.plugin'
import { Types } from './types'
Plugins.register(Types.Demo, new DemoPlugin())Limitations
- Due to the implementation,
Pluggablemethods should beasync(or return aPromise). Decorating a synchronous method will make itasync. - The order of execution is determined by the order of registration. If your plugins have dependencies on each other, you have to register them in the correct order yourself.