Intended best practices to avoid cross-cutting concerns #1199
-
Okay this is probably 100% due to my lack of experience with TS. But is there an intended pattern with the powertools to provide access to an instance of the logger which contains the injected context (and will clear state) that I can use in the classes called by my handler? At this point I'm just passing logger as a parameter when instantiating the classes, and calling this.logger throughout. But it means I have to pass the logger to all of my classes to use it. I just want to make sure in my own inexperience, I'm not missing "a better way" using decorators thats already a part of the package. (beyond on the handler to inject context, event, clearstate, etc.) |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
Hi @codyfrisch, thank you for starting this discussion! I understand that you'd like to reuse the same instance of logger throughout your different classes/functions, and ideally this instance should have already been injected with the context. A pattern that we recommend would be to have your logger object instantiated in a shared file that can be imported throughout your codebase: For instance, have a file called import { Logger } from '@aws-lambda-powertools/logger';
export const logger = new Logger(); Then, in your main import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { logger } from './common/powertools';
class Lambda implements LambdaInterface {
private someMethod() {
logger.info('hello from someMethod');
}
@logger.injectLambdaContext({ logEvent: true })
public async handler(event: unknown, context: Context): Promise<unknown> {
logger.info('hello from handler');
// ... do things
this.someMethod();
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass); Both the logs (the one in Now, let's image you have another class/module/file export class OtherClass {
public constructor () {}
public doSomething() {
// ... do stuff
logger.info('foo bar');
}
} We want to emit a log inside the To do this you have two options: 1. Pass the logger as parameter to the class
export class OtherClass {
- public constructor () {
+ public constructor (logger: any) {
+ this.logger = logger;
+ }
public doSomething() {
// ... do stuff
- logger.info('foo bar');
+ this.logger.info('foo bar');
}
}
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { logger } from './common/powertools';
+ import { OtherClass } from './otherUtility.ts';
+
+ const otherClass = new OtherClass(logger);
class Lambda implements LambdaInterface {
private someMethod() {
logger.info('hello from someMethod');
}
@logger.injectLambdaContext({ logEvent: true })
public async handler(event: unknown, context: Context): Promise<unknown> {
logger.info('hello from handler');
// ... do things
this.someMethod();
+ otherClass.doSomething()
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass); In this case, since the entry point is always the 2. Import the shared module in your other class/module/file
+ import { logger } from './common/powertools';
+
export class OtherClass {
public constructor () {}
public doSomething() {
// ... do stuff
logger.info('foo bar');
}
}
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { logger } from './common/powertools';
+ import { OtherClass } from './otherUtility.ts';
+
+ const otherClass = new OtherClass();
class Lambda implements LambdaInterface {
private someMethod() {
logger.info('hello from someMethod');
}
@logger.injectLambdaContext({ logEvent: true })
public async handler(event: unknown, context: Context): Promise<unknown> {
logger.info('hello from handler');
// ... do things
this.someMethod();
+ otherClass.doSomething()
}
}
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass); In this second option we don't have to pass around the ConclusionAs a part of both methods you could also consider creating a child logger. This child logger would inherit all the characteristics and attributes of the original logger instance, but would allow you to independently mutate the child instance (i.e. appending a new attribute) without impacting the main one. The key aspect of both methods, is that the "context injection" happens around the Does this help? If not, could you please provide an example of what you're trying to achieve and a bit more details? |
Beta Was this translation helpful? Give feedback.
Hi @codyfrisch, thank you for starting this discussion!
I understand that you'd like to reuse the same instance of logger throughout your different classes/functions, and ideally this instance should have already been injected with the context.
A pattern that we recommend would be to have your logger object instantiated in a shared file that can be imported throughout your codebase:
For instance, have a file called
powertools.ts
:Then, in your main
index.ts
file, have your class: