-
Notifications
You must be signed in to change notification settings - Fork 148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Documentation: reuse across code files/modules #777
Comments
Thanks for the issue. DX is high priority for us and we appreciate this kind of feedback.
We had a discussion about this. We're considering scraping the singleton implementation for tracing (#767 ). Instead of enforcing this (potentially dangerous) pattern, it would be better to let user control this (like introducing Our examples mislead people to think that we should instantiate one utility per handler. I'm thinking about these two changes:
What do you think?
I do not understand this part. Could you elaborate more and point me to the example with these issues? |
Thanks @ijemmy and @dreamorosi for looking into this.
I do like the I'm coming from a Java background mostly, and have been using Powertools there. With a recent TypeScript project where I knew Powertools would be a good option to drive consistency/good practices in my team's Lambda functions, I wanted to check Powertools TypeScript but from the documentation, the answers to how to do logging/metrics/tracing in non-handler file/function was not clear.
I'll mention a use case and draw some parallels to Java/Powertools. Assume that the Lambda function is servicing requests by human users (e.g. lambda is a backend for a frontend). I'd like to add a
|
Are the If default instances are expensive to instantiate, then they can be published under a separate file that gets imported only by those who want them. Unless that import comes up in the handler path, bundlers will just exclude the code that instantiates default instances. Best of both worlds. Here's an example of what such default imports could look like: // Easy imports for most use-cases
import { logger } from '@aws-lambda-powertools/logger/defaults'
import { metrics } from '@aws-lambda-powertools/metrics/defaults'
import { tracer } from '@aws-lambda-powertools/tracer/defaults'
// If a hypothetical single wrapper package for all of these existed:
import { logger, metrics, tracer } from '@aws-lambda-powertools/powertools/defaults' Separate from the topic of reuse is the importance of request scoped log attributes. The Would love to hear your thoughts on this matter, @dreamorosi, @ijemmy, @humanzz. These two pain points are the most annoying parts of using Powertools in Node today. |
Hi @bilalq, thanks for engaging with this issue. The classes for the utilities that we support at the moment are not expensive to instantiate, the only logic that is run at init time is configuring the instances according to the options/params that can be passed by env variables or constructor. At the moment we don't provide instances of the utilities instantiated with default because we wanted to give developers the ability to initialise the classes and customise their behaviour directly. Even using the method you propose you'd be essentially be saving one line but you'd be losing the chance to configure the class via constructor parameters. We simply don't have enough feedback from the community yet to determine whether the default settings would be fine for most use cases like you suggest, but if that's the case we can consider providing these already-instantiated utils. I'd like to defer the decision to a later date after more members from the community and users have also given their opinion. Would you be so kind to open a dedicated discussion essentially porting the content of your comment, so that we can have a period of time to gather more opinions? If after that we see enough interest we'll definitely consider this. Regarding the second topic, I'm not sure I understand the issue you are describing. When using So for instance this code: import middy from "@middy/core";
import { Logger, injectLambdaContext } from "@aws-lambda-powertools/logger";
import axios from "axios";
const logger = new Logger({
persistentLogAttributes: {
foo: "bar",
},
});
export const handler = middy(async (event: any = {}): Promise<any> => {
logger.info("Hello World");
try {
await axios.get("https://httpbin.org/status/200");
} catch (error) {
logger.error("error:catch", { error });
throw error;
} finally {
}
}).use(injectLambdaContext(logger, { clearState: true })); Will have the Let's now take a second example: import middy from "@middy/core";
import { Logger, injectLambdaContext } from "@aws-lambda-powertools/logger";
import axios from "axios";
const logger = new Logger({
persistentLogAttributes: {
foo: "bar",
},
});
let isColdStart = true;
export const handler = middy(async (event: any = {}): Promise<any> => {
if (isColdStart) {
isColdStart = false;
logger.appendKeys({
another: "key",
});
logger.addPersistentLogAttributes({
other: "key",
});
}
logger.info("Hello World");
try {
await axios.get("https://httpbin.org/status/200");
} catch (error) {
logger.error("error:catch", { error });
throw error;
} finally {
}
}).use(injectLambdaContext(logger, { clearState: true })); In the above I'm using a trivial logic to run the portion of code that calls In this case, the So to sum up, the behaviour you're describing is already happening: only request specific state is cleared while persistent attributes set outside of the handler are maintained. This, by the way, is an advantage of having you - the developer - instantiate the If you want to continue the conversation on this second topic, please open a dedicated issue. |
Discussion created over here as requested: #1012 |
This comment was marked as off-topic.
This comment was marked as off-topic.
Revisiting this issue, and after some consideration I am inclined to close it The current implementation, together with Node.js module caching properties already cover the requirement of enabling reuse of instances of our utilities across customer modules. Relevant excerpt from the Node.js docs:
This means that, today, even without explicitly converting the utilities to singletons customers are able to share instances of the same utility following this patten:
import { Logger } from '@aws-lambda-powertools/logger';
export const logger = new Logger({
serviceName: 'product-service',
logLevel: 'debug'
});
import { logger } from './logger.js';
export const foo = () => {
logger.appendKeys({ someKey: 'someVal' });
};
import { logger } from './logger.js';
import { foo } from './foo.js';
const handler = async () => {
foo();
logger.info('I am a singleton-ish');
}; which once run yields the following result: Notice how in the logs (bottom of the screen) the log includes the Examples of this can be found both in our example app in the examples directory and the Powertools for AWS Lambda workshop. Credits for the idea of using this pattern go to @saragerion, from whom I lifted it from here. |
This issue is now closed. Please be mindful that future comments are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so. |
Description of the feature request
Problem statement
Powertools should make it easy to implement logging, metrics and tracing for code bases that are split across multiple files and modules. This is in contrast to simplistic examples that have all the example logic needed in a single handler file.
Currently, logger, tracer, and metrics examples all show instantiation in the handler file. It's not clear, from documentation, how developers should handle logging, metrics and tracing in other files
Some example use cases where the current implementation makes them unnecessarily difficult - or not clear - to implement
Additional context
LoggingUtils
class which can be accessed anywhere in code to manipulate some feature of logging e.g. https://awslabs.github.io/aws-lambda-powertools-java/core/logging/#appending-additional-keysMetricsUtils
class which can be accessed anywhere in code.Solution(s)
I'm not proposing a specific solution, but I'd like for the team to think about it. It seems like Java Powertools and the Tracer PR mentioned above do leverage some variation of singleton so this might be worth exploring more.
Off the top of my head, and based on the logger feature parity intended in #482, I don't feel that a simple singleton is the solution, but maybe a singleton root logger, and then child loggers for other files/modules https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/logger/#using-multiple-logger-instances-across-your-code.
In an example application I've started testing Powertools in, I introduced a
powertools.ts
file which instantiates/exportslogger
,metrics
,tracer
. My handler file imports those instances and configures the handler with the relevant middy middleware.Other files import those instances as needed as well (this is essentially me introducing the singletons).
The text was updated successfully, but these errors were encountered: