docs(tracer): Allow to reuse Tracer across your code#767
docs(tracer): Allow to reuse Tracer across your code#767dreamorosi wants to merge 4 commits intomainfrom
Conversation
|
I wonder if this singleton pattern should be done for all 3 utilities; tracer, logger and metrics. In larger projects, with many files and modules, this would be really useful I believe. |
It's something we can consider. Could you please open an issue requesting the feature? We'll then prioritize accordingly. |
@dreamorosi here it is #777 |
|
Following a discussion with the other maintainers we have decided to scrap this feature in order to avoid forcing a pattern on customers and also in light of the behaviour detailed here. In Javascript a common pattern is to define a common module that instantiates shared classes and export them. For example, customers could create a module (even in a Lambda Layer) called import { Logger } from '@aws-lambda-powertools/logger';
import { Metrics } from '@aws-lambda-powertools/metrics';
import { Tracer } from '@aws-lambda-powertools/tracer';
const awsLambdaPowertoolsVersion = '0.6.0';
const defaultValues = {
awsAccountId: process.env.AWS_ACCOUNT_ID || 'N/A',
environment: process.env.ENVIRONMENT || 'N/A'
};
const logger = new Logger({
persistentLogAttributes: {
...defaultValues,
logger: {
name: '@aws-lambda-powertools/logger',
version: awsLambdaPowertoolsVersion,
}
},
});
const metrics = new Metrics({
defaultDimensions: defaultValues
});
const tracer = new Tracer();
export {
logger,
metrics,
tracer
};This module can then be imported and used in other places and files: // payment.ts
import { tracer, logger } from './powertools.ts';
const collectPayment = async (chargeId: string) => {
const mainSegment = tracer.getSegment();
const subsegment = mainSegment.addNewSubsegment('### collectPayment');
tracer.setSegment(subsegment);
// This annotation will be done on the `### collectPayment` segment
tracer.putAnnotation('chargeId', chargeId);
// Process charge
// This log will have the same config & persistent keys as the others
logger.info('charge processed', { chargeId: chargeId });
subsegment.close();
tracer.setSegment(mainSegment);
};
export default collectPayment;and import middy from '@middy/core';
import { captureLambdaHandler } from 'from '@aws-lambda-powertools/tracer';
import { injectLambdaContext } from 'from '@aws-lambda-powertools/logger';
import { tracer, logger } from './powertools.ts';
import collectPayment from './payment.ts';
export const handler = middy(async (event: unknown) => {
await collectPayment(event.chargeId);
....
logger.info('Done');
}).use(captureLambdaHandler(tracer)).use(injectLambdaContext(logger));This PR will now focus on documenting this pattern in the documentation with the aim of providing prescriptive guidance about this use case. |
After reading #777 , two options came to my mind:
|
|
Description of your changes
When using Tracer customers might want to trace parts of code that are distributed across files/modules. At the moment, in order to maintain the correct relationship between segments, customers would have had to either pass the
Tracerinstance or a parent segment around, which is tedious and leads to tighter coupling.This PR makes Tracer reusable by returning the a previously initialised instance, if one exists. With this change customers can:
and then do:
or:
How to verify this change
See existing & new test cases passing.
See screenshot of segments generated by second example above:

See video of how the docs look after the changes:
Screen.Recording.2022-04-14.at.13.03.20.mp4
Related issues, RFCs
#275
PR status
Is this ready for review?: YES
Is it a breaking change?: NO
Checklist
Breaking change checklist
N/A
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.