Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: remove grpc trace exporter dependency on grpc-exporter-base #3100

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c482bcb
refactor: remove grpc trace exporter dependency on grpc-exporter-base
dyladan Jul 18, 2022
8daa6f3
test: fix imports
dyladan Jul 18, 2022
09e5859
chore: fix dependencies
dyladan Jul 18, 2022
34fa0e0
style: lint
dyladan Jul 18, 2022
3203c6f
fix: allow multiple protos directories
dyladan Jul 18, 2022
8d8f767
fix: headers cannot be used with grpc
dyladan Jul 18, 2022
38165cf
feat: implement timeout
dyladan Jul 18, 2022
96507a7
chore: update changelog
dyladan Jul 18, 2022
50cbd94
Merge branch 'main' into trace-metric-grpc
dyladan Jul 18, 2022
51c1c72
feat: implement shutdown
dyladan Jul 18, 2022
587e29e
Merge branch 'trace-metric-grpc' of github.com:dyladan/opentelemetry-…
dyladan Jul 18, 2022
62ca7e9
refactor!: reduce public surface and adapt tests
dyladan Jul 18, 2022
c8768ea
style: lint
dyladan Jul 18, 2022
2a5b265
Review comments
dyladan Jul 19, 2022
21344bd
fix: revert breaking metadata config order change
dyladan Jul 19, 2022
f6fd0eb
fix: return after shutdown and add test
dyladan Jul 19, 2022
5fc978e
Merge remote-tracking branch 'origin/main' into trace-metric-grpc
dyladan Jul 19, 2022
b60ca26
style: lint
dyladan Jul 19, 2022
80d2655
Merge branch 'main' into trace-metric-grpc
dyladan Jul 20, 2022
c20344f
test: add tests for security module
dyladan Jul 20, 2022
7eb1b22
Merge branch 'trace-metric-grpc' of github.com:dyladan/opentelemetry-…
dyladan Jul 20, 2022
25696c0
style: lint
dyladan Jul 20, 2022
3f9dad2
Merge branch 'main' into trace-metric-grpc
dyladan Jul 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "experimental/packages/otlp-proto-exporter-base/protos"]
path = experimental/packages/otlp-proto-exporter-base/protos
url = https://github.com/open-telemetry/opentelemetry-proto.git
[submodule "experimental/packages/exporter-trace-otlp-grpc/protos"]
path = experimental/packages/exporter-trace-otlp-grpc/protos
url = https://github.com/open-telemetry/opentelemetry-proto
2 changes: 2 additions & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ All notable changes to experimental packages in this project will be documented
### :bug: (Bug Fix)

* fix(histogram): fix maximum when only values < -1 are provided [#3086](https://github.com/open-telemetry/opentelemetry-js/pull/3086) @pichlermarc
* fix(exporter-grpc): prevent trace and metrics grpc service clients from interfering with each other [#3100](https://github.com/open-telemetry/opentelemetry-js/pull/3100) @dyladan
* refactor trace grpc exporter to not use the grpc exporter base class
* fix(sdk-metrics-base): fix PeriodicExportingMetricReader keeping Node.js process from exiting
[#3106](https://github.com/open-telemetry/opentelemetry-js/pull/3106) @seemk

Expand Down
8 changes: 3 additions & 5 deletions experimental/packages/exporter-trace-otlp-grpc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"devDependencies": {
"@babel/core": "7.16.0",
"@opentelemetry/api": "^1.0.0",
"@opentelemetry/otlp-exporter-base": "0.30.0",
"@opentelemetry/resources": "1.4.0",
"@types/mocha": "8.2.3",
"@types/node": "14.17.33",
"@types/sinon": "10.0.6",
Expand All @@ -67,12 +67,10 @@
"@opentelemetry/api": "^1.0.0"
},
"dependencies": {
"@grpc/grpc-js": "^1.5.9",
"@grpc/proto-loader": "^0.6.9",
"@grpc/grpc-js": "1.5.9",
"@grpc/proto-loader": "0.6.9",
"@opentelemetry/core": "1.4.0",
"@opentelemetry/otlp-grpc-exporter-base": "0.30.0",
"@opentelemetry/otlp-transformer": "0.30.0",
"@opentelemetry/resources": "1.4.0",
"@opentelemetry/sdk-trace-base": "1.4.0"
}
}
1 change: 1 addition & 0 deletions experimental/packages/exporter-trace-otlp-grpc/protos
Submodule protos added at c5c8b2
Original file line number Diff line number Diff line change
Expand Up @@ -14,58 +14,100 @@
* limitations under the License.
*/

import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
import { baggageUtils, getEnv } from '@opentelemetry/core';
import { Metadata } from '@grpc/grpc-js';
import {
OTLPGRPCExporterConfigNode,
OTLPGRPCExporterNodeBase,
ServiceClientType,
validateAndNormalizeUrl,
DEFAULT_COLLECTOR_URL
} from '@opentelemetry/otlp-grpc-exporter-base';
import { createExportTraceServiceRequest, IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer';
import * as grpc from '@grpc/grpc-js';
import { loadSync } from '@grpc/proto-loader';
import { ExportResult, ExportResultCode, getEnv } from '@opentelemetry/core';
import { createExportTraceServiceRequest } from '@opentelemetry/otlp-transformer';
import type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
import * as path from 'path';
import { RequestCounter } from './internal/request-counter';
import { TraceServiceClient } from './internal/types';
import type { OTLPGRPCTraceExporterConfig } from './types';
import { getConnectionOptions } from './util';

/**
* OTLP Trace Exporter for Node
*/
export class OTLPTraceExporter
extends OTLPGRPCExporterNodeBase<ReadableSpan,
IExportTraceServiceRequest>
implements SpanExporter {
export class OTLPTraceExporter implements SpanExporter {
private _metadata: grpc.Metadata;
private _serviceClient: TraceServiceClient;
private _timeoutMillis: number;
private _requestCounter = new RequestCounter();
private _isShutdown = false;

constructor(config: OTLPGRPCExporterConfigNode = {}) {
super(config);
const headers = baggageUtils.parseKeyPairsIntoRecord(getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS);
this.metadata ||= new Metadata();
for (const [k, v] of Object.entries(headers)) {
this.metadata.set(k, v);
}
}
constructor(config: OTLPGRPCTraceExporterConfig = {}) {
const { host, credentials, metadata, compression } = getConnectionOptions(config, getEnv());
this._metadata = metadata;
this._timeoutMillis = config.timeoutMillis ?? 10_000;

convert(spans: ReadableSpan[]): IExportTraceServiceRequest {
return createExportTraceServiceRequest(spans);
}
const packageDefinition = loadSync('opentelemetry/proto/collector/trace/v1/trace_service.proto', {
keepCase: false,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: [
// In webpack environments, the bundle may be at the same level as the protos/ directory
path.resolve(__dirname, 'protos'),
// When running typescript directly in tests or with ts-node, the protos/ directory is one level above the src/ directory
path.resolve(__dirname, '..', 'protos'),
// When running the compiled `js, the protos directory is two levels above the build/src/ directory
path.resolve(__dirname, '..', '..', 'protos'),
],
});

getDefaultUrl(config: OTLPGRPCExporterConfigNode) {
return validateAndNormalizeUrl(this.getUrlFromConfig(config));
// any required here because the types returned by loadSync don't know our package structure
const packageObject: any = grpc.loadPackageDefinition(packageDefinition);
const channelOptions: grpc.ChannelOptions = {
'grpc.default_compression_algorithm': compression,
};
this._serviceClient = new packageObject.opentelemetry.proto.collector.trace.v1.TraceService(host, credentials, channelOptions);
}

getServiceClientType() {
return ServiceClientType.SPANS;
}
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {
if (this._isShutdown) {
dyladan marked this conversation as resolved.
Show resolved Hide resolved
setImmediate(resultCallback, {
code: ExportResultCode.FAILED,
error: new Error('Cannot export after shutdown'),
});
return;
}

getServiceProtoPath(): string {
return 'opentelemetry/proto/collector/trace/v1/trace_service.proto';
const deadline = Date.now() + this._timeoutMillis;
this._requestCounter.startRequest();

const req = createExportTraceServiceRequest(spans);
this._serviceClient.export(
req,
this._metadata,
{ deadline },
(error: Error) => {
this._requestCounter.endRequest();

if (error) {
resultCallback({
code: ExportResultCode.FAILED,
error,
});
} else {
resultCallback({
code: ExportResultCode.SUCCESS,
});
}
},
);
}

getUrlFromConfig(config: OTLPGRPCExporterConfigNode): string {
if (typeof config.url === 'string') {
return config.url;
}
shutdown(): Promise<void> {
this._isShutdown = true;
return new Promise((resolve, _reject) => {
if (this._requestCounter.requests === 0) {
resolve();
return;
}

return getEnv().OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
getEnv().OTEL_EXPORTER_OTLP_ENDPOINT ||
DEFAULT_COLLECTOR_URL;
this._requestCounter.onEndLastRequest(resolve);
});
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
* limitations under the License.
*/

export * from './OTLPTraceExporter';
export { OTLPTraceExporter } from './OTLPTraceExporter';
export { CompressionAlgorithm, OTLPGRPCTraceExporterConfig } from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import EventEmitter = require('events');

export class RequestCounter {
public requests = 0;
private _e = new EventEmitter();

startRequest() {
this.requests += 1;
}

endRequest() {
if (this.requests === 0) {
throw new Error('Tried to end more requests than were started');
}
this.requests -= 1;
this._e.emit('endRequest');
}

onEndLastRequest(cb: () => void) {
if (this.requests === 0) {
setImmediate(cb);
}

const self = this;
this._e.on('endRequest', function onEndRequest() {
if (self.requests === 0) {
self._e.removeListener('endRequest', onEndRequest);
cb();
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import type * as grpc from '@grpc/grpc-js';

export interface TraceServiceClient extends grpc.Client {
export: (
request: any,
metadata: grpc.Metadata,
options: grpc.CallOptions,
callback: Function
) => {};
}


export type ConnectionOptions = {
host: string;
metadata: grpc.Metadata;
credentials: grpc.ChannelCredentials;
compression: grpc.compressionAlgorithms;
};
33 changes: 33 additions & 0 deletions experimental/packages/exporter-trace-otlp-grpc/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import type * as grpc from '@grpc/grpc-js';

export type OTLPGRPCTraceExporterConfig = {
credentials?: grpc.ChannelCredentials;
metadata?: grpc.Metadata;
compression?: CompressionAlgorithm;

url?: string;
/** Maximum time the OTLP exporter will wait for each batch export.
* The default value is 10000ms. */
timeoutMillis?: number;
};

export enum CompressionAlgorithm {
NONE = 'none',
GZIP = 'gzip'
}
Loading