@avanzu/decorators
v1.2.2
Published
> TODO: description
Downloads
613
Readme
@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:false
exit
(boolean) - wether to generate a log entry after the method call. Default:true
error
(boolean) - wether to generate a log entry when an error occured during method execution. Default:true
message
(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:false
result
(boolean) - wether to include the return value of the method call in the log context. Default:false
enterPrefix
(string) - string to prepend to theenter
log message. Default:''
errorPrefix
(string) - string to prepend to theexit
log message. Default:''
exitPrefix
(string) - string to prepent to theerror
log message. Default:''
level
('debug'|'info'|'warn'|'error'
) - the log level ofenter
andexit
logs. 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,
Pluggable
methods 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.