|
| 1 | +const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node'); |
| 2 | +const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base'); |
| 3 | +const { OTLPTraceExporter: OTLPTraceExporterHttp } = require('@opentelemetry/exporter-trace-otlp-http'); |
| 4 | +const { OTLPTraceExporter: OTLPTraceExporterProto } = require('@opentelemetry/exporter-trace-otlp-proto'); |
1 | 5 | const { Resource } = require('@opentelemetry/resources'); |
2 | | -const { BasicTracerProvider, InMemorySpanExporter } = require('@opentelemetry/sdk-trace-base'); |
3 | | -const { createExportTraceServiceRequest } = require('@opentelemetry/otlp-transformer'); |
4 | | -const { trace, SpanKind, SpanStatusCode } = require('@opentelemetry/api'); |
5 | | - |
6 | | -const root = require('@opentelemetry/otlp-transformer/build/esm/generated/root'); |
7 | | -const ExportTraceServiceRequest = root.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; |
8 | | -const ExportTraceServiceResponse = root.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; |
| 6 | +const { ATTR_SERVICE_NAME } = require('@opentelemetry/semantic-conventions'); |
9 | 7 |
|
10 | 8 | exports.handler = async (event, context) => { |
11 | | - console.log('Starting OTLP response validation...'); |
12 | | - const result = await validateOtlpResponse(); |
13 | | - return { |
14 | | - statusCode: result.success ? 200 : 500, |
15 | | - body: JSON.stringify(result, null, 2), |
16 | | - }; |
17 | | -}; |
18 | | - |
19 | | -async function validateOtlpResponse() { |
20 | | - try { |
21 | | - const requestBuffer = createOtlpTraceRequest(); |
22 | | - const response = await fetch('http://localhost:4318/v1/traces', { |
23 | | - method: 'POST', |
24 | | - headers: { |
25 | | - 'Content-Type': 'application/x-protobuf', |
26 | | - }, |
27 | | - body: requestBuffer, |
28 | | - }); |
29 | | - |
30 | | - if (response.status !== 200) { |
31 | | - return { |
32 | | - success: false, |
33 | | - error: `Expected status 200, got ${response.status}`, |
34 | | - statusCode: response.status, |
35 | | - }; |
36 | | - } |
37 | | - |
38 | | - const responseBuffer = Buffer.from(await response.arrayBuffer()); |
39 | | - |
40 | | - const contentType = response.headers.get('content-type'); |
41 | | - if (contentType !== 'application/x-protobuf') { |
42 | | - return { |
43 | | - success: false, |
44 | | - error: `Expected Content-Type 'application/x-protobuf', got '${contentType}'`, |
45 | | - statusCode: response.status, |
46 | | - contentType, |
47 | | - }; |
48 | | - } |
49 | | - |
50 | | - let decodedResponse; |
51 | | - try { |
52 | | - decodedResponse = ExportTraceServiceResponse.decode(responseBuffer); |
53 | | - } catch (decodeError) { |
54 | | - return { |
55 | | - success: false, |
56 | | - error: `Failed to decode response as ExportTraceServiceResponse: ${decodeError.message}`, |
57 | | - statusCode: response.status, |
58 | | - contentType, |
59 | | - decodeError: decodeError.message, |
60 | | - }; |
61 | | - } |
| 9 | + const serviceName = context.functionName; |
| 10 | + const requestId = context.awsRequestId; |
| 11 | + const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318'; |
62 | 12 |
|
63 | | - // Verify that partial_success is not set (should be None for success) |
64 | | - if (decodedResponse.partialSuccess) { |
65 | | - return { |
66 | | - success: false, |
67 | | - error: 'Unexpected partial_success in response - should only be success or failure', |
68 | | - statusCode: response.status, |
69 | | - contentType, |
70 | | - partialSuccess: decodedResponse.partialSuccess, |
71 | | - }; |
72 | | - } |
| 13 | + const jsonProvider = new NodeTracerProvider({ |
| 14 | + resource: new Resource({ [ATTR_SERVICE_NAME]: serviceName }), |
| 15 | + }); |
| 16 | + const jsonExporter = new OTLPTraceExporterHttp({ |
| 17 | + url: endpoint + '/v1/traces', |
| 18 | + headers: { 'Content-Type': 'application/json' }, |
| 19 | + }); |
| 20 | + jsonProvider.addSpanProcessor(new SimpleSpanProcessor(jsonExporter)); |
| 21 | + jsonProvider.register(); |
73 | 22 |
|
74 | | - return { |
75 | | - success: true, |
76 | | - statusCode: response.status, |
77 | | - contentType, |
78 | | - response: decodedResponse, |
79 | | - message: 'OTLP response is properly formatted and decodable', |
80 | | - }; |
| 23 | + const jsonSpan = jsonProvider.getTracer('json-test').startSpan('test-span-json'); |
| 24 | + jsonSpan.setAttribute('request_id', requestId); |
| 25 | + jsonSpan.setAttribute('encoding', 'json'); |
| 26 | + jsonSpan.end(); |
81 | 27 |
|
82 | | - } catch (error) { |
83 | | - console.error('Validation error:', error); |
84 | | - return { |
85 | | - success: false, |
86 | | - error: `Unexpected error: ${error.message}`, |
87 | | - stack: error.stack, |
88 | | - }; |
89 | | - } |
90 | | -} |
| 28 | + await jsonProvider.forceFlush(); |
| 29 | + await jsonProvider.shutdown(); |
91 | 30 |
|
92 | | -function createOtlpTraceRequest() { |
93 | | - const resource = new Resource({ |
94 | | - 'service.name': process.env.DD_SERVICE, |
| 31 | + const protoProvider = new NodeTracerProvider({ |
| 32 | + resource: new Resource({ [ATTR_SERVICE_NAME]: serviceName }), |
95 | 33 | }); |
96 | | - |
97 | | - const provider = new BasicTracerProvider({ resource }); |
98 | | - const exporter = new InMemorySpanExporter(); |
99 | | - provider.register(); |
100 | | - |
101 | | - const tracer = provider.getTracer('validation-tracer', '1.0.0'); |
102 | | - const span = tracer.startSpan('test-span', { |
103 | | - kind: SpanKind.INTERNAL, |
104 | | - attributes: { |
105 | | - 'test': 'validation', |
106 | | - }, |
| 34 | + const protoExporter = new OTLPTraceExporterProto({ |
| 35 | + url: endpoint + '/v1/traces', |
107 | 36 | }); |
108 | | - span.setStatus({ code: SpanStatusCode.OK }); |
109 | | - span.end(); |
| 37 | + protoProvider.addSpanProcessor(new SimpleSpanProcessor(protoExporter)); |
110 | 38 |
|
111 | | - provider.forceFlush(); |
| 39 | + const protoSpan = protoProvider.getTracer('proto-test').startSpan('test-span-protobuf'); |
| 40 | + protoSpan.setAttribute('request_id', requestId); |
| 41 | + protoSpan.setAttribute('encoding', 'protobuf'); |
| 42 | + protoSpan.end(); |
112 | 43 |
|
113 | | - const finishedSpans = exporter.getFinishedSpans(); |
114 | | - const otlpRequest = createExportTraceServiceRequest(finishedSpans, { |
115 | | - useHex: true, |
116 | | - useLongBits: false, |
117 | | - }); |
| 44 | + await protoProvider.forceFlush(); |
| 45 | + await protoProvider.shutdown(); |
118 | 46 |
|
119 | | - const message = ExportTraceServiceRequest.create(otlpRequest); |
120 | | - const buffer = ExportTraceServiceRequest.encode(message).finish(); |
121 | | - return Buffer.from(buffer); |
122 | | -} |
| 47 | + return { statusCode: 200, body: JSON.stringify({ message: 'Success' }) }; |
| 48 | +}; |
0 commit comments