Skip to content

Instantly share code, notes, and snippets.

@mikaelvesavuori
Last active March 11, 2023 17:26
Show Gist options
  • Save mikaelvesavuori/759f639a05ce61452d1bbbc1d5b037fb to your computer and use it in GitHub Desktop.
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.
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