Skip to content

Commit 4f9e618

Browse files
Update AWS and Azure exporters' file naming convention for exported traces (#112)
* refactor: update timestamp formatting and improve span grouping in S3 and Azure Blob exporters Signed-off-by: Ajeet Patel <patelajeet1606@gmail.com> * remove unwanted comments Signed-off-by: Ajeet Patel <patelajeet1606@gmail.com> * refactor: remove unused time formatting and random string generation functions Signed-off-by: Ajeet Patel <patelajeet1606@gmail.com> * chore: update comments Signed-off-by: Ajeet Patel <patelajeet1606@gmail.com> --------- Signed-off-by: Ajeet Patel <patelajeet1606@gmail.com> Co-authored-by: prasad-okahu <147112411+prasad-okahu@users.noreply.github.com>
1 parent 397e932 commit 4f9e618

3 files changed

Lines changed: 62 additions & 44 deletions

File tree

src/exporters/aws/AWSS3SpanExporter.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { S3 } from '@aws-sdk/client-s3';
22
import { ExportResultCode } from '@opentelemetry/core';
3-
import { getUrlFriendlyTime, makeid, exportInfo } from '../utils';
3+
import { getS3FormattedTime, exportInfo } from '../utils';
44
import { consoleLog } from '../../common/logging';
55
import { Span } from '@opentelemetry/api';
66
import { ExportTaskProcessor } from '../taskProcessor/LambdaExportTaskProcessor';
@@ -72,35 +72,47 @@ class AWSS3SpanExporter {
7272
}
7373

7474
private async _sendSpans(spans: Span[], done: (result: { code: ExportResultCode, error?: Error }) => void): Promise<void> {
75-
const timestamp = getUrlFriendlyTime(new Date());
7675
const prefix = this.keyPrefix + (process.env.MONOCLE_S3_KEY_PREFIX_CURRENT || '');
77-
const key = `${prefix}${timestamp}_${makeid(5)}.ndjson`;
7876

79-
consoleLog(`AWSS3SpanExporter| Preparing to send spans to S3 - Key: ${key}`);
80-
consoleLog(`AWSS3SpanExporter| Current prefix: ${prefix}, Timestamp: ${timestamp}`);
77+
// Group spans by trace id so each trace is uploaded as its own file.
78+
const spansByTrace = new Map<string, Span[]>();
79+
for (const span of spans) {
80+
const traceId = span.spanContext().traceId;
81+
if (!spansByTrace.has(traceId)) {
82+
spansByTrace.set(traceId, []);
83+
}
84+
spansByTrace.get(traceId).push(span);
85+
}
8186

82-
const body = spans.map(span => JSON.stringify(this._exportInfo(span))).join('\n');
83-
consoleLog(`AWSS3SpanExporter| Generated body size: ${body.length} bytes`);
87+
try {
88+
for (const [traceId, traceSpans] of spansByTrace) {
89+
const timestamp = getS3FormattedTime(new Date());
90+
const key = `${prefix}${timestamp}_${traceId}.ndjson`;
8491

85-
const params = {
86-
Bucket: this.bucketName,
87-
Key: key,
88-
Body: body,
89-
ContentType: 'application/x-ndjson'
90-
};
92+
consoleLog(`AWSS3SpanExporter| Preparing to send trace ${traceId} to S3 - Key: ${key}`);
93+
94+
const body = traceSpans.map(span => JSON.stringify(this._exportInfo(span))).join('\n') + '\n';
95+
consoleLog(`AWSS3SpanExporter| Generated body size: ${body.length} bytes`);
96+
97+
const params = {
98+
Bucket: this.bucketName,
99+
Key: key,
100+
Body: body,
101+
ContentType: 'application/x-ndjson'
102+
};
103+
104+
consoleLog(`AWSS3SpanExporter| Uploading to S3 - Bucket: ${this.bucketName}, Key: ${key}`);
105+
await this.s3Client.putObject(params);
106+
consoleLog(`AWSS3SpanExporter| Successfully uploaded trace ${traceId} to S3`);
107+
}
91108

92-
try {
93-
consoleLog(`AWSS3SpanExporter| Uploading to S3 - Bucket: ${this.bucketName}, Key: ${key}`);
94-
await this.s3Client.putObject(params);
95-
consoleLog('AWSS3SpanExporter| Successfully uploaded spans to S3');
96109
if (done) {
97110
done({ code: ExportResultCode.SUCCESS });
98111
}
99-
100112
} catch (error) {
101113
console.error('Error uploading spans to S3:', error);
102114
consoleLog(`AWSS3SpanExporter| Failed to upload spans. Error: ${error.message}`);
103-
if(done){
115+
if (done) {
104116
done({ code: ExportResultCode.FAILED, error });
105117
}
106118
}

src/exporters/azure/AzureBlobSpanExporter.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BlobServiceClient } from '@azure/storage-blob';
22
import { ExportResultCode } from '@opentelemetry/core';
3-
import { exportInfo, getUrlFriendlyTime, makeid } from '../utils';
3+
import { exportInfo, getBlobFormattedTime } from '../utils';
44
import { consoleLog } from '../../common/logging';
55
import { Span } from '@opentelemetry/api';
66
import { ExportTaskProcessor } from '../taskProcessor/LambdaExportTaskProcessor';
@@ -20,7 +20,7 @@ class AzureBlobSpanExporter {
2020

2121
constructor({ containerName, blobPrefix, connectionString, taskProcessor }: AzureBlobSpanExporterConfig) {
2222
this.containerName = containerName || process.env.MONOCLE_BLOB_CONTAINER_NAME || "default-container";
23-
this.blobPrefix = blobPrefix || process.env.MONOCLE_AZURE_BLOB_PREFIX || "monocle_trace__";
23+
this.blobPrefix = blobPrefix || process.env.MONOCLE_AZURE_BLOB_PREFIX || "monocle_trace_";
2424
const blobConnectionString = connectionString || process.env.MONOCLE_BLOB_CONNECTION_STRING || process.env.AZURE_STORAGE_CONNECTION_STRING;
2525
if (!blobConnectionString)
2626
throw new Error('Azure Blob Storage connection string is required in MONOCLE_BLOB_CONNECTION_STRING or AZURE_STORAGE_CONNECTION_STRING');
@@ -57,18 +57,31 @@ class AzureBlobSpanExporter {
5757
}
5858

5959
async _sendSpans(spans: Span[], done) {
60-
const timestamp = getUrlFriendlyTime(new Date());
61-
const blobName = `${this.blobPrefix}${timestamp}_${makeid(5)}.ndjson`;
62-
const body = spans.map(span => JSON.stringify(this._exportInfo(span))).join('\n');
63-
consoleLog('sending spans to Azure Blob Storage:', blobName, body);
60+
// Group spans by trace id so each trace is uploaded as its own blob,
61+
const spansByTrace = new Map<string, Span[]>();
62+
for (const span of spans) {
63+
const traceId = span.spanContext().traceId;
64+
if (!spansByTrace.has(traceId)) {
65+
spansByTrace.set(traceId, []);
66+
}
67+
spansByTrace.get(traceId).push(span);
68+
}
6469

6570
const containerClient = this.blobServiceClient.getContainerClient(this.containerName);
66-
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
6771

6872
try {
69-
consoleLog('try to upload spans to Azure Blob Storage:', blobName);
70-
await blockBlobClient.upload(body, body.length, { blobHTTPHeaders: { blobContentType: 'application/x-ndjson' } });
71-
consoleLog('successfully uploaded spans to Azure Blob Storage:', blobName);
73+
for (const [traceId, traceSpans] of spansByTrace) {
74+
const timestamp = getBlobFormattedTime(new Date());
75+
const blobName = `${this.blobPrefix}${timestamp}_${traceId}.ndjson`;
76+
const body = traceSpans.map(span => JSON.stringify(this._exportInfo(span))).join('\n') + '\n';
77+
consoleLog('sending spans to Azure Blob Storage:', blobName, body);
78+
79+
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
80+
consoleLog('try to upload spans to Azure Blob Storage:', blobName);
81+
await blockBlobClient.upload(body, body.length, { blobHTTPHeaders: { blobContentType: 'application/x-ndjson' } });
82+
consoleLog('successfully uploaded spans to Azure Blob Storage:', blobName);
83+
}
84+
7285
if (done) {
7386
return done({ code: ExportResultCode.SUCCESS });
7487
}

src/exporters/utils.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ import { format } from "date-fns";
22
import { hrTimeToTimeStamp } from "@opentelemetry/core";
33
import { Span } from "@opentelemetry/sdk-trace-node";
44

5-
export function getUrlFriendlyTime(date = new Date()) {
6-
return format(date, "yyyyMMdd'T'HHmmss");
5+
// Generates S3 exporter time format: "%Y-%m-%d__%H.%M.%S"
6+
export function getS3FormattedTime(date = new Date()) {
7+
return format(date, "yyyy-MM-dd'__'HH.mm.ss");
8+
}
9+
10+
// Generates Azure Blob exporter time format: "%Y-%m-%d_%H.%M.%S"
11+
export function getBlobFormattedTime(date = new Date()) {
12+
return format(date, "yyyy-MM-dd'_'HH.mm.ss");
713
}
814

915
export interface SpanExport {
@@ -37,19 +43,6 @@ export interface SpanExport {
3743
};
3844
}
3945

40-
// generate random alphanumeric string of given length
41-
export function makeid(length) {
42-
let result = '';
43-
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
44-
const charactersLength = characters.length;
45-
let counter = 0;
46-
while (counter < length) {
47-
result += characters.charAt(Math.floor(Math.random() * charactersLength));
48-
counter += 1;
49-
}
50-
return result;
51-
}
52-
5346
export function exportInfo(span: Span) {
5447
const span_object: SpanExport = {
5548
name: span.name,

0 commit comments

Comments
 (0)