Skip to content

Commit 68b93c5

Browse files
svetlanabrennanlegendecasvmarchaud
authored
feat(otlp-grpc-exporter): add compression configuration to exporter (#2813)
Signed-off-by: Svetlana Brennan <svetlana.svn@gmail.com> Co-authored-by: legendecas <legendecas@gmail.com> Co-authored-by: Valentin Marchaud <contact@vmarchaud.fr>
1 parent 3fd1b1e commit 68b93c5

File tree

6 files changed

+151
-3
lines changed

6 files changed

+151
-3
lines changed

packages/exporter-trace-otlp-grpc/README.md

+26
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,32 @@ provider.register();
107107

108108
Note, that this will only work if TLS is also configured on the server.
109109

110+
By default no compression will be used. To use compression, set it programmatically in `collectorOptions` or with environment variables. Supported compression options: `gzip` and `none`.
111+
112+
```js
113+
const { CompressionAlgorithm } = require('@opentelemetry/exporter-trace-otlp-grpc');
114+
115+
const collectorOptions = {
116+
// url is optional and can be omitted - default is localhost:4317
117+
url: '<collector-hostname>:<port>',
118+
metadata, // // an optional grpc.Metadata object to be sent with each request
119+
compression: CompressionAlgorithm.GZIP,
120+
};
121+
const exporter = new OTLPTraceExporter(collectorOptions);
122+
```
123+
124+
> Providing `compression` with `collectorOptions` takes precedence and overrides compression set with environment variables.
125+
126+
## Environment Variable Configuration
127+
128+
Set compression with environment variables.
129+
130+
```shell
131+
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION=gzip
132+
```
133+
134+
> Compression set programatically in `collectorOptions` takes precedence over compression set with environment variables. `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` takes precedence and overrides `OTEL_EXPORTER_OTLP_COMPRESSION`.
135+
110136
## Running opentelemetry-collector locally to see the traces
111137

112138
1. Go to examples/otlp-exporter-node

packages/exporter-trace-otlp-grpc/src/OTLPExporterNodeBase.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ import {
2525
GRPCQueueItem,
2626
ServiceClientType,
2727
} from './types';
28-
import { ServiceClient } from './types';
28+
import { ServiceClient, CompressionAlgorithm } from './types';
2929
import { getEnv, baggageUtils } from '@opentelemetry/core';
30+
import { configureCompression } from './util';
3031

3132
/**
3233
* OTLP Metric Exporter abstract base class
@@ -43,6 +44,7 @@ export abstract class OTLPExporterNodeBase<
4344
metadata?: Metadata;
4445
serviceClient?: ServiceClient = undefined;
4546
private _send!: Function;
47+
compression: CompressionAlgorithm;
4648

4749
constructor(config: OTLPExporterConfigNode = {}) {
4850
super(config);
@@ -54,6 +56,7 @@ export abstract class OTLPExporterNodeBase<
5456
for (const [k, v] of Object.entries(headers)) {
5557
this.metadata.set(k, v);
5658
}
59+
this.compression = configureCompression(config.compression);
5760
}
5861

5962
private _sendPromise(

packages/exporter-trace-otlp-grpc/src/types.ts

+9
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,18 @@ export interface OTLPExporterConfigNode
4545
extends otlpTypes.OTLPExporterConfigBase {
4646
credentials?: grpc.ChannelCredentials;
4747
metadata?: grpc.Metadata;
48+
compression?: CompressionAlgorithm;
4849
}
4950

5051
export enum ServiceClientType {
5152
SPANS,
5253
METRICS,
5354
}
55+
56+
/**
57+
* These values are defined by grpc client
58+
*/
59+
export enum CompressionAlgorithm {
60+
NONE = 0,
61+
GZIP = 2
62+
}

packages/exporter-trace-otlp-grpc/src/util.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import * as grpc from '@grpc/grpc-js';
1818
import * as protoLoader from '@grpc/proto-loader';
1919
import { diag } from '@opentelemetry/api';
20-
import { globalErrorHandler } from '@opentelemetry/core';
20+
import { globalErrorHandler, getEnv } from '@opentelemetry/core';
2121
import { otlpTypes } from '@opentelemetry/exporter-trace-otlp-http';
2222
import * as path from 'path';
2323
import { OTLPExporterNodeBase } from './OTLPExporterNodeBase';
@@ -26,6 +26,7 @@ import {
2626
OTLPExporterConfigNode,
2727
GRPCQueueItem,
2828
ServiceClientType,
29+
CompressionAlgorithm
2930
} from './types';
3031

3132
export function onInit<ExportItem, ServiceRequest>(
@@ -50,17 +51,21 @@ export function onInit<ExportItem, ServiceRequest>(
5051
.then(packageDefinition => {
5152
const packageObject: any = grpc.loadPackageDefinition(packageDefinition);
5253

54+
const options = { 'grpc.default_compression_algorithm': collector.compression };
55+
5356
if (collector.getServiceClientType() === ServiceClientType.SPANS) {
5457
collector.serviceClient =
5558
new packageObject.opentelemetry.proto.collector.trace.v1.TraceService(
5659
collector.url,
5760
credentials,
61+
options,
5862
);
5963
} else {
6064
collector.serviceClient =
6165
new packageObject.opentelemetry.proto.collector.metrics.v1.MetricsService(
6266
collector.url,
6367
credentials,
68+
options,
6469
);
6570
}
6671

@@ -125,3 +130,13 @@ export function validateAndNormalizeUrl(url: string): string {
125130
}
126131
return target.host;
127132
}
133+
134+
export function configureCompression(compression: CompressionAlgorithm | undefined): CompressionAlgorithm {
135+
if (compression) {
136+
return compression;
137+
} else {
138+
const definedCompression = getEnv().OTEL_EXPORTER_OTLP_TRACES_COMPRESSION || getEnv().OTEL_EXPORTER_OTLP_COMPRESSION;
139+
140+
return definedCompression === 'gzip' ? CompressionAlgorithm.GZIP: CompressionAlgorithm.NONE;
141+
}
142+
}

packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts

+73
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import {
3636
mockedReadableSpan,
3737
} from './traceHelper';
3838

39+
import { CompressionAlgorithm } from '../src/types';
40+
3941
const traceServiceProtoPath =
4042
'opentelemetry/proto/collector/trace/v1/trace_service.proto';
4143
const includeDirs = [path.resolve(__dirname, '../protos')];
@@ -198,6 +200,77 @@ const testCollectorExporter = (params: TestParams) =>
198200
}, 200);
199201
});
200202
});
203+
describe('export - with gzip compression', () => {
204+
beforeEach(() => {
205+
const credentials = params.useTLS
206+
? grpc.credentials.createSsl(
207+
fs.readFileSync('./test/certs/ca.crt'),
208+
fs.readFileSync('./test/certs/client.key'),
209+
fs.readFileSync('./test/certs/client.crt')
210+
)
211+
: undefined;
212+
collectorExporter = new OTLPTraceExporter({
213+
url: 'grpcs://' + address,
214+
credentials,
215+
metadata: params.metadata,
216+
compression: CompressionAlgorithm.GZIP,
217+
});
218+
219+
const provider = new BasicTracerProvider();
220+
provider.addSpanProcessor(new SimpleSpanProcessor(collectorExporter));
221+
});
222+
it('should successfully send the spans', done => {
223+
const responseSpy = sinon.spy();
224+
const spans = [Object.assign({}, mockedReadableSpan)];
225+
collectorExporter.export(spans, responseSpy);
226+
setTimeout(() => {
227+
assert.ok(
228+
typeof exportedData !== 'undefined',
229+
'resource' + " doesn't exist"
230+
);
231+
let spans;
232+
let resource;
233+
if (exportedData) {
234+
spans = exportedData.instrumentationLibrarySpans[0].spans;
235+
resource = exportedData.resource;
236+
ensureExportedSpanIsCorrect(spans[0]);
237+
238+
assert.ok(
239+
typeof resource !== 'undefined',
240+
"resource doesn't exist"
241+
);
242+
if (resource) {
243+
ensureResourceIsCorrect(resource);
244+
}
245+
}
246+
if (params.metadata && reqMetadata) {
247+
ensureMetadataIsCorrect(reqMetadata, params.metadata);
248+
}
249+
done();
250+
}, 500);
251+
});
252+
});
253+
describe('Trace Exporter with compression', () => {
254+
const envSource = process.env;
255+
it('should return gzip compression algorithm on exporter', () => {
256+
const credentials = params.useTLS
257+
? grpc.credentials.createSsl(
258+
fs.readFileSync('./test/certs/ca.crt'),
259+
fs.readFileSync('./test/certs/client.key'),
260+
fs.readFileSync('./test/certs/client.crt')
261+
)
262+
: undefined;
263+
264+
envSource.OTEL_EXPORTER_OTLP_COMPRESSION='gzip';
265+
collectorExporter = new OTLPTraceExporter({
266+
url: 'grpcs://' + address,
267+
credentials,
268+
metadata: params.metadata,
269+
});
270+
assert.strictEqual(collectorExporter.compression, CompressionAlgorithm.GZIP);
271+
delete envSource.OTEL_EXPORTER_OTLP_COMPRESSION;
272+
});
273+
});
201274
});
202275

203276
describe('OTLPTraceExporter - node (getDefaultUrl)', () => {

packages/exporter-trace-otlp-grpc/test/util.test.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import * as sinon from 'sinon';
1818
import * as assert from 'assert';
1919

2020
import { diag } from '@opentelemetry/api';
21-
import { validateAndNormalizeUrl } from '../src/util';
21+
import { validateAndNormalizeUrl, configureCompression } from '../src/util';
22+
import { CompressionAlgorithm} from '../src/types';
2223

2324
// Tests added to detect breakage released in #2130
2425
describe('validateAndNormalizeUrl()', () => {
@@ -79,3 +80,24 @@ describe('validateAndNormalizeUrl()', () => {
7980
});
8081
});
8182
});
83+
84+
describe('configureCompression', () => {
85+
const envSource = process.env;
86+
it('should return none for compression', () => {
87+
const compression = CompressionAlgorithm.NONE;
88+
assert.strictEqual(configureCompression(compression), CompressionAlgorithm.NONE);
89+
});
90+
it('should return gzip compression defined via env', () => {
91+
envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = 'gzip';
92+
assert.strictEqual(configureCompression(undefined),CompressionAlgorithm.GZIP);
93+
delete envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION;
94+
});
95+
it('should return none for compression defined via env', () => {
96+
envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION = 'none';
97+
assert.strictEqual(configureCompression(undefined),CompressionAlgorithm.NONE);
98+
delete envSource.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION;
99+
});
100+
it('should return none for compression when no compression is set', () => {
101+
assert.strictEqual(configureCompression(undefined),CompressionAlgorithm.NONE);
102+
});
103+
});

0 commit comments

Comments
 (0)