Skip to content

Commit

Permalink
feat: browser support for exporter-trace-otlp-proto (#3208)
Browse files Browse the repository at this point in the history
* add node & browser platform for exporter-trace-otlp-proto

* add browser support for proto base exporter

* add base browser class with xhr support

* add browser class for proto trace exporter

* slight tweaks to make it work

* send data as blob to avoid making sync xhr requests

* fix lint

* remove console.logs and add browser proto example

* cleanup and start adding tests

* Undo formatting changes

* exporter-trace-otlp-proto: fix compile errors

* Misc updates from review comments

* Adding changelog entry

* Reverting format changes not needed

* Moving the send function into the class for browser case.

* Adjust indentation to fix lint errors

* Remove template parameter that's not needed

* Apply review changes

* fix the import path

* Addressing lint errors

* Explicit imports for browser case

* More explicit exports

* Add missing exports

* Address lint issues with export statements

* Adding missing exports

* Adding missing export

* Using import from top level folder

* Trigger Build

* Update experimental/packages/exporter-trace-otlp-proto/test/browser/CollectorTraceExporter.test.ts

Co-authored-by: Marc Pichler <marcpi@edu.aau.at>

* Remove trailing comma

* Remove blank line to fix lint error

* Fixes based on testing opentelemetry-web/fetch-proto

* Add additional missing export

* Skip hex conversion of traceId for the protobuf

* Add esm/esnext builds for the proto packages

that will now be used for the browser case as well.

* Trigger Build

Co-authored-by: Santosh Cheler <scheler@cisco.com>
Co-authored-by: Santosh Cheler <santosh.cheler@gmail.com>
Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com>
Co-authored-by: Marc Pichler <marcpi@edu.aau.at>
  • Loading branch information
5 people authored Jan 20, 2023
1 parent 652d167 commit 9589d54
Show file tree
Hide file tree
Showing 35 changed files with 623 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/
### :rocket: (Enhancement)

* feat(instrumentation-grpc): set net.peer.name and net.peer.port on client spans [#3430](https://github.com/open-telemetry/opentelemetry-js/pull/3430)
* feat(exporter-trace-otlp-proto): Add protobuf otlp trace exporter support for browser [#3208](https://github.com/open-telemetry/opentelemetry-js/pull/3208) @pkanal

### :bug: (Bug Fix)

Expand Down
20 changes: 20 additions & 0 deletions examples/opentelemetry-web/examples/fetch-proto/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<title>Fetch Plugin Example</title>
<base href="/">

<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
Example of using Web Tracer with Fetch plugin with console exporter and proto exporter
<script type="text/javascript" src="fetch-proto.js"></script>
<br/>
<button id="button1">Test</button>

</body>

</html>
80 changes: 80 additions & 0 deletions examples/opentelemetry-web/examples/fetch-proto/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const { context, trace } = require("@opentelemetry/api");
const { ConsoleSpanExporter, SimpleSpanProcessor} = require("@opentelemetry/sdk-trace-base");
const { WebTracerProvider } = require("@opentelemetry/sdk-trace-web");
const { FetchInstrumentation } = require("@opentelemetry/instrumentation-fetch");
const { ZoneContextManager } = require("@opentelemetry/context-zone");
const { B3Propagator } = require("@opentelemetry/propagator-b3");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");
const { OTLPTraceExporter: OTLPTraceExporterProto } = require("@opentelemetry/exporter-trace-otlp-proto");

const provider = new WebTracerProvider();

// Note: For production consider using the "BatchSpanProcessor" to reduce the number of requests
// to your exporter. Using the SimpleSpanProcessor here as it sends the spans immediately to the
// exporter without delay
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
provider.addSpanProcessor(
new SimpleSpanProcessor(new OTLPTraceExporterProto())
);

provider.register({
contextManager: new ZoneContextManager(),
propagator: new B3Propagator(),
});

registerInstrumentations({
instrumentations: [
new FetchInstrumentation({
ignoreUrls: [/localhost:8090\/sockjs-node/],
propagateTraceHeaderCorsUrls: [
"https://cors-test.appspot.com/test",
"https://httpbin.org/get",
],
clearTimingResources: true,
}),
],
});

const webTracerWithZone = provider.getTracer("example-tracer-web");

const getData = (url) =>
fetch(url, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});

// example of keeping track of context between async operations
const prepareClickEvent = () => {
const url = "https://httpbin.org/get";

const element = document.getElementById("button1");

const onClick = () => {
const singleSpan = webTracerWithZone.startSpan("files-series-info");
context.with(trace.setSpan(context.active(), singleSpan), () => {
getData(url).then((_data) => {
trace
.getSpan(context.active())
.addEvent("fetching-single-span-completed");
singleSpan.end();
});
});
for (let i = 0, j = 5; i < j; i += 1) {
const span = webTracerWithZone.startSpan(`files-series-info-${i}`);
context.with(trace.setSpan(context.active(), span), () => {
getData(url).then((_data) => {
trace
.getSpan(context.active())
.addEvent(`fetching-span-${i}-completed`);
span.end();
});
});
}
};
element.addEventListener("click", onClick);
};

window.addEventListener("load", prepareClickEvent);
1 change: 1 addition & 0 deletions examples/opentelemetry-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@opentelemetry/core": "1.9.0",
"@opentelemetry/exporter-metrics-otlp-http": "0.35.0",
"@opentelemetry/exporter-trace-otlp-http": "0.35.0",
"@opentelemetry/exporter-trace-otlp-proto": "0.35.0",
"@opentelemetry/exporter-zipkin": "1.9.0",
"@opentelemetry/instrumentation": "0.35.0",
"@opentelemetry/instrumentation-fetch": "0.35.0",
Expand Down
3 changes: 2 additions & 1 deletion examples/opentelemetry-web/webpack.dev.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const common = {
'xml-http-request': 'examples/xml-http-request/index.js',
fetchXhr: 'examples/fetchXhr/index.js',
fetchXhrB3: 'examples/fetchXhrB3/index.js',
'fetch-proto': 'examples/fetch-proto/index.js',
zipkin: 'examples/zipkin/index.js',
},
output: {
Expand Down Expand Up @@ -41,7 +42,7 @@ const common = {
resolve: {
modules: [
path.resolve(directory),
'node_modules',
'node_modules'
],
extensions: ['.ts', '.js', '.jsx', '.json'],
},
Expand Down
1 change: 1 addition & 0 deletions examples/opentelemetry-web/webpack.prod.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const common = {
'xml-http-request': 'examples/xml-http-request/index.js',
fetchXhr: 'examples/fetchXhr/index.js',
fetchXhrB3: 'examples/fetchXhrB3/index.js',
"fetch-proto": "examples/fetch-proto/index.js",
zipkin: 'examples/zipkin/index.js',
},
output: {
Expand Down
26 changes: 26 additions & 0 deletions experimental/packages/exporter-trace-otlp-proto/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*!
* 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
*
* http://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.
*/

const karmaWebpackConfig = require('../../../karma.webpack');
const karmaBaseConfig = require('../../../karma.base');

module.exports = (config) => {
config.set(Object.assign({}, karmaBaseConfig, {
webpack: karmaWebpackConfig,
files: ['test/browser/index-webpack.ts'],
preprocessors: { 'test/browser/index-webpack.ts': ['webpack'] }
}))
};
23 changes: 19 additions & 4 deletions experimental/packages/exporter-trace-otlp-proto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,27 @@
"version": "0.35.0",
"description": "OpenTelemetry Collector Exporter allows user to send collected traces to the OpenTelemetry Collector using protobuf over HTTP",
"main": "build/src/index.js",
"module": "build/esm/index.js",
"esnext": "build/esnext/index.js",
"types": "build/src/index.d.ts",
"repository": "open-telemetry/opentelemetry-js",
"browser": {
"./src/platform/index.ts": "./src/platform/browser/index.ts",
"./build/esm/platform/index.js": "./build/esm/platform/browser/index.js",
"./build/esnext/platform/index.js": "./build/esnext/platform/browser/index.js",
"./build/src/platform/index.js": "./build/src/platform/browser/index.js"
},
"scripts": {
"prepublishOnly": "npm run compile",
"compile": "tsc --build",
"clean": "tsc --build --clean",
"compile": "tsc --build tsconfig.json tsconfig.esm.json tsconfig.esnext.json",
"clean": "tsc --build --clean tsconfig.json tsconfig.esm.json tsconfig.esnext.json",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"tdd": "npm run test -- --watch-extensions ts --watch",
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts'",
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'",
"test:browser": "nyc karma start --single-run",
"version": "node ../../../scripts/version-update.js",
"watch": "tsc --build --watch",
"watch": "tsc --build --watch tsconfig.json tsconfig.esm.json tsconfig.esnext.json",
"precompile": "lerna run version --scope $(npm pkg get name) --include-dependencies",
"prewatch": "npm run precompile",
"peer-api-check": "node ../../../scripts/peer-api-check.js",
Expand All @@ -35,6 +44,12 @@
"node": ">=14"
},
"files": [
"build/esm/**/*.js",
"build/esm/**/*.js.map",
"build/esm/**/*.d.ts",
"build/esnext/**/*.js",
"build/esnext/**/*.js.map",
"build/esnext/**/*.d.ts",
"build/src/**/*.js",
"build/src/**/*.js.map",
"build/src/**/*.d.ts",
Expand Down
3 changes: 1 addition & 2 deletions experimental/packages/exporter-trace-otlp-proto/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from './OTLPTraceExporter';
export { OTLPTraceExporter } from './platform';
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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 { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
import { getEnv, baggageUtils } from '@opentelemetry/core';
import {
OTLPExporterConfigBase,
appendResourcePathToUrl,
appendRootPathToUrlIfNeeded,
} from '@opentelemetry/otlp-exporter-base';
import {
OTLPProtoExporterBrowserBase,
ServiceClientType,
} from '@opentelemetry/otlp-proto-exporter-base';
import {
createExportTraceServiceRequest,
IExportTraceServiceRequest,
} from '@opentelemetry/otlp-transformer';

const DEFAULT_COLLECTOR_RESOURCE_PATH = 'v1/traces';
const DEFAULT_COLLECTOR_URL = `http://localhost:4318/${DEFAULT_COLLECTOR_RESOURCE_PATH}`;

/**
* Collector Trace Exporter for Web
*/
export class OTLPTraceExporter
extends OTLPProtoExporterBrowserBase<ReadableSpan, IExportTraceServiceRequest>
implements SpanExporter
{
constructor(config: OTLPExporterConfigBase = {}) {
super(config);
this._headers = Object.assign(
this._headers,
baggageUtils.parseKeyPairsIntoRecord(
getEnv().OTEL_EXPORTER_OTLP_TRACES_HEADERS
)
);
}
convert(spans: ReadableSpan[]): IExportTraceServiceRequest {
return createExportTraceServiceRequest(spans);
}

getDefaultUrl(config: OTLPExporterConfigBase): string {
return typeof config.url === 'string'
? config.url
: getEnv().OTEL_EXPORTER_OTLP_TRACES_ENDPOINT.length > 0
? appendRootPathToUrlIfNeeded(getEnv().OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)
: getEnv().OTEL_EXPORTER_OTLP_ENDPOINT.length > 0
? appendResourcePathToUrl(
getEnv().OTEL_EXPORTER_OTLP_ENDPOINT,
DEFAULT_COLLECTOR_RESOURCE_PATH
)
: DEFAULT_COLLECTOR_URL;
}

getServiceClientType() {
return ServiceClientType.SPANS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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.
*/
export { OTLPTraceExporter } from './OTLPTraceExporter';
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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.
*/
export { OTLPTraceExporter } from './node';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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.
*/

export { OTLPTraceExporter } from './OTLPTraceExporter';
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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 * as assert from 'assert';
import * as sinon from 'sinon';
import { OTLPTraceExporter } from '../../src/platform/browser/index';

describe('OTLPTraceExporter - web', () => {
let collectorTraceExporter: OTLPTraceExporter;
describe('constructor', () => {
let onInitSpy: any;
beforeEach(() => {
onInitSpy = sinon.stub(OTLPTraceExporter.prototype, 'onInit');
const collectorExporterConfig = {
hostname: 'foo',
url: 'http://foo.bar.com',
};
collectorTraceExporter = new OTLPTraceExporter(collectorExporterConfig);
});
afterEach(() => {
sinon.restore();
});
it('should create an instance', () => {
assert.ok(typeof collectorTraceExporter !== 'undefined');
});
it('should call onInit', () => {
assert.strictEqual(onInitSpy.callCount, 1);
});
it('should set hostname', () => {
assert.strictEqual(collectorTraceExporter.hostname, 'foo');
});

it('should set url', () => {
assert.strictEqual(collectorTraceExporter.url, 'http://foo.bar.com');
});
});
});
Loading

0 comments on commit 9589d54

Please sign in to comment.