Last active
March 11, 2023 17:26
-
-
Save mikaelvesavuori/759f639a05ce61452d1bbbc1d5b037fb to your computer and use it in GitHub Desktop.
Demo of some of the basics of manually implementing a tracing library in TypeScript. Has an AWS Lambda handler, but can work anywhere.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { randomBytes } from 'crypto'; | |
| /** | |
| * @description Simple demo of tracing in a manual way. | |
| * | |
| * Note that this demo does not actually set the duration of traces | |
| * given that you provide them manually here. | |
| * | |
| * Another thing obviously missing is therefore, by logical extension, | |
| * any way of "ending" a span. | |
| */ | |
| export async function handler(event: any, context: any): Promise<any> { | |
| process.env.__CORRELATIONID__ = produceCorrelationId(event, context); | |
| const trace = getExistingTrace(event); | |
| const traceId = trace.traceId || randomBytes(16).toString('hex'); | |
| console.log('traceId', traceId); | |
| const trace1 = createTrace('Span 1', 350, traceId); | |
| writeTrace(trace1); | |
| const trace2 = createTrace('Span 2', 200, traceId, trace1); | |
| writeTrace(trace2); | |
| const trace3 = createTrace('Span 3', 50, traceId, trace2); | |
| writeTrace(trace3); | |
| const trace4 = createTrace('Span 4', 25, traceId, trace1); | |
| writeTrace(trace4); | |
| return { | |
| statusCode: 200, | |
| body: JSON.stringify('OK') | |
| }; | |
| } | |
| /** | |
| * @description Create a valid, well-formed trace object. | |
| */ | |
| function createTrace( | |
| spanName: string, | |
| durationMs: number, | |
| traceId: string, | |
| parentSpan?: Record<string, any> | |
| ) { | |
| const timeNow = Date.now(); | |
| const trace: Record<string, any> = { | |
| correlationId: getCorrelationId(), | |
| spanId: randomBytes(16).toString('hex'), | |
| spanName, | |
| traceId, | |
| durationMs, | |
| timestamp: new Date(timeNow).toISOString(), | |
| timestampEpoch: `${timeNow}` | |
| }; | |
| if (parentSpan?.spanName && parentSpan?.spanId) { | |
| trace['spanParent'] = parentSpan.spanName; | |
| trace['spanParentId'] = parentSpan.spanId; | |
| } | |
| return trace; | |
| } | |
| /** | |
| * @description Output the trace using raw STDOUT. | |
| */ | |
| const writeTrace = (trace: Record<string, any>) => { | |
| process.stdout.write(JSON.stringify(trace) + '\n'); | |
| }; | |
| /** | |
| * @description Extract any existing trace from incoming headers. | |
| */ | |
| const getExistingTrace = (event: Record<string, any>): Record<string, any> => { | |
| const header = event?.headers?.['traceheader']; | |
| if (!header) return {}; | |
| const [version, traceId, parentId, traceFlags] = header.split('-'); | |
| return { | |
| version, | |
| traceId, | |
| parentId, | |
| traceFlags | |
| }; | |
| }; | |
| /** | |
| * Utility to get correlation ID from environment. | |
| */ | |
| const getCorrelationId = () => process.env.__CORRELATIONID__ || ''; | |
| /** | |
| * Utility to set correlation ID in environment. | |
| */ | |
| const produceCorrelationId = (event: any, context: any): string => { | |
| // Check first if this is 1) via event, 2) via header (API), or 3) set new one from AWS request ID, else set as empty | |
| if ( | |
| event && | |
| event['detail'] && | |
| event['detail']['metadata'] && | |
| event['detail']['metadata']['correlationId'] | |
| ) | |
| return event['detail']['metadata']['correlationId']; | |
| else if (event && event['headers'] && event['headers']['x-correlation-id']) | |
| return event['headers']['x-correlation-id']; | |
| else if (context && context['awsRequestId']) return context['awsRequestId']; | |
| return ''; | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment