|
2 | 2 | // SPDX-License-Identifier: Apache-2.0 |
3 | 3 | import { OTLPLogExporter as OTLPProtoLogExporter } from '@opentelemetry/exporter-logs-otlp-proto'; |
4 | 4 | import { CompressionAlgorithm, OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base'; |
5 | | -import { gzipSync } from 'zlib'; |
6 | 5 | import { IExportLogsServiceResponse, ProtobufLogsSerializer } from '@opentelemetry/otlp-transformer'; |
7 | | -import { ReadableLogRecord } from '@opentelemetry/sdk-logs'; |
8 | | -import { AwsAuthenticator } from '../common/aws-authenticator'; |
9 | | -import { ExportResult, ExportResultCode } from '@opentelemetry/core'; |
10 | | -import { PassthroughSerializer } from '../common/passthrough-serializer'; |
| 6 | +import { LogRecordExporter, ReadableLogRecord } from '@opentelemetry/sdk-logs'; |
| 7 | +import { OTLPAwsBaseExporter } from '../common/otlp-aws-base-exporter'; |
11 | 8 |
|
12 | 9 | /** |
13 | | - * This exporter extends the functionality of the OTLPProtoLogExporter to allow spans to be exported |
| 10 | + * This exporter extends the functionality of the OTLPProtoLogExporter to allow logs to be exported |
14 | 11 | * to the CloudWatch Logs OTLP endpoint https://logs.[AWSRegion].amazonaws.com/v1/logs. Utilizes the aws-sdk |
15 | 12 | * library to sign and directly inject SigV4 Authentication to the exported request's headers. <a |
16 | 13 | * href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-OTLPEndpoint.html">...</a> |
17 | 14 | * |
18 | 15 | * This only works with version >=16 Node.js environments. |
| 16 | + * @param endpoint - The AWS CloudWatch Logs OTLP endpoint URL |
| 17 | + * @param config - Optional OTLP exporter configuration |
19 | 18 | */ |
20 | | - |
21 | | -export class OTLPAwsLogExporter extends OTLPProtoLogExporter { |
22 | | - private compression: CompressionAlgorithm | undefined; |
23 | | - private endpoint: string; |
24 | | - private region: string; |
25 | | - private serializer: PassthroughSerializer<IExportLogsServiceResponse>; |
26 | | - private authenticator: AwsAuthenticator; |
27 | | - |
| 19 | +export class OTLPAwsLogExporter |
| 20 | + extends OTLPAwsBaseExporter<ReadableLogRecord[], IExportLogsServiceResponse> |
| 21 | + implements LogRecordExporter |
| 22 | +{ |
28 | 23 | constructor(endpoint: string, config?: OTLPExporterNodeConfigBase) { |
29 | 24 | const modifiedConfig: OTLPExporterNodeConfigBase = { |
30 | 25 | ...config, |
31 | 26 | url: endpoint, |
32 | 27 | compression: CompressionAlgorithm.NONE, |
33 | 28 | }; |
34 | 29 |
|
35 | | - super(modifiedConfig); |
36 | | - this.compression = config?.compression; |
37 | | - this.region = endpoint.split('.')[1]; |
38 | | - this.endpoint = endpoint; |
39 | | - this.authenticator = new AwsAuthenticator(this.region, 'logs'); |
40 | | - |
41 | | - // This is used in order to prevent serializing and compressing the data twice. Once for signing Sigv4 and |
42 | | - // once when we pass the data to super.export() which will serialize and compress the data again. |
43 | | - this.serializer = new PassthroughSerializer(ProtobufLogsSerializer.deserializeResponse); |
44 | | - this['_delegate']._serializer = this.serializer; |
| 30 | + const parentExporter = new OTLPProtoLogExporter(modifiedConfig); |
| 31 | + super(endpoint, 'logs', parentExporter, ProtobufLogsSerializer, config?.compression); |
45 | 32 | } |
46 | | - |
47 | | - /** |
48 | | - * Overrides the upstream implementation of export. If the |
49 | | - * endpoint is the CloudWatch Logs OTLP endpoint, we sign the request with SigV4 in headers. |
50 | | - * To prevent performance degradation from serializing and compressing data twice, we handle serialization and compression |
51 | | - * locally in this exporter and pass the pre-processed data to the upstream export functionality. |
52 | | - */ |
53 | | - |
54 | | - // Upstream already implements a retry mechanism: |
55 | | - // https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/otlp-exporter-base/src/retrying-transport.ts |
56 | | - |
57 | | - public override async export( |
58 | | - items: ReadableLogRecord[], |
59 | | - resultCallback: (result: ExportResult) => void |
60 | | - ): Promise<void> { |
61 | | - let serializedLogs: Uint8Array | undefined = ProtobufLogsSerializer.serializeRequest(items); |
62 | | - |
63 | | - if (serializedLogs === undefined) { |
64 | | - resultCallback({ |
65 | | - code: ExportResultCode.FAILED, |
66 | | - error: new Error('Nothing to send'), |
67 | | - }); |
68 | | - return; |
69 | | - } |
70 | | - |
71 | | - const shouldCompress = this.compression && this.compression !== CompressionAlgorithm.NONE; |
72 | | - if (shouldCompress) { |
73 | | - serializedLogs = gzipSync(serializedLogs); |
74 | | - } |
75 | | - |
76 | | - // Pass pre-processed data to passthrough serializer. When super.export() is called, the Passthrough Serializer will |
77 | | - // use the pre-processed data instead of serializing and compressing the data again. |
78 | | - this.serializer.setSerializedData(serializedLogs); |
79 | | - |
80 | | - // See type: https://github.com/open-telemetry/opentelemetry-js/blob/experimental/v0.57.1/experimental/packages/otlp-exporter-base/src/transport/http-transport-types.ts#L31 |
81 | | - const headers = this['_delegate']._transport?._transport?._parameters?.headers(); |
82 | | - |
83 | | - if (headers) { |
84 | | - if (shouldCompress) { |
85 | | - headers['Content-Encoding'] = 'gzip'; |
86 | | - } else { |
87 | | - delete headers['Content-Encoding']; |
88 | | - } |
89 | | - |
90 | | - const signedRequest = await this.authenticator.authenticate(this.endpoint, headers, serializedLogs); |
91 | | - |
92 | | - const newHeaders: () => Record<string, string> = () => signedRequest; |
93 | | - this['_delegate']._transport._transport._parameters.headers = newHeaders; |
94 | | - } |
95 | | - |
96 | | - super.export(items, resultCallback); |
| 33 | + shutdown(): Promise<void> { |
| 34 | + return this.parentExporter.shutdown(); |
97 | 35 | } |
98 | 36 | } |
0 commit comments