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

Convert fetch plugin to instrumentation #1796

Merged
merged 3 commits into from
Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions examples/tracer-web/examples/fetch/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
'use strict';

// for debugging
// import { context, getSpan, setSpan } from '../../../../packages/opentelemetry-api/src';
import { context, getSpan, setSpan } from '@opentelemetry/api';
import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing';
import { CollectorTraceExporter } from '@opentelemetry/exporter-collector';
import { WebTracerProvider } from '@opentelemetry/web';
import { FetchPlugin } from '@opentelemetry/plugin-fetch';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { B3Propagator } from '@opentelemetry/propagator-b3';

const provider = new WebTracerProvider({
plugins: [
new FetchPlugin({
new FetchInstrumentation({
ignoreUrls: [/localhost:8090\/sockjs-node/],
propagateTraceHeaderCorsUrls: [
'https://cors-test.appspot.com/test',
Expand Down
9 changes: 4 additions & 5 deletions examples/tracer-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,12 @@
"@opentelemetry/core": "^0.14.0",
"@opentelemetry/exporter-collector": "^0.14.0",
"@opentelemetry/exporter-zipkin": "^0.14.0",
"@opentelemetry/instrumentation-fetch": "^0.14.0",
"@opentelemetry/instrumentation-xml-http-request": "^0.14.0",
"@opentelemetry/metrics": "^0.14.0",
"@opentelemetry/propagator-b3": "^0.14.0",
"@opentelemetry/plugin-document-load": "^0.11.0",
"@opentelemetry/plugin-fetch": "^0.14.0",
"@opentelemetry/plugin-user-interaction": "^0.11.0",
"@opentelemetry/instrumentation": "^0.14.0",
"@opentelemetry/instrumentation-xml-http-request": "^0.14.0",
"@opentelemetry/plugin-document-load": "^0.12.0",
"@opentelemetry/plugin-user-interaction": "^0.12.0",
"@opentelemetry/tracing": "^0.14.0",
"@opentelemetry/web": "^0.14.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This module provides auto instrumentation for web using fetch.
## Installation

```bash
npm install --save @opentelemetry/plugin-fetch
npm install --save @opentelemetry/instrumentation-fetch
```

## Usage
Expand All @@ -20,12 +20,12 @@ npm install --save @opentelemetry/plugin-fetch
'use strict';
import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing';
import { WebTracerProvider } from '@opentelemetry/web';
import { FetchPlugin } from '@opentelemetry/plugin-fetch';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { ZoneContextManager } from '@opentelemetry/context-zone';

const provider = new WebTracerProvider({
plugins: [
new FetchPlugin(),
new FetchInstrumentation(),
],
});

Expand All @@ -35,6 +35,17 @@ provider.register({
contextManager: new ZoneContextManager(),
});

// or plugin can be also initialised separately and then set the tracer provider or meter provider
const fetchInstrumentation = new FetchInstrumentation();
const provider = new WebTracerProvider();
provider.register({
contextManager: new ZoneContextManager(),
});
fetchInstrumentation.setTracerProvider(provider);

provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));


// and some test

fetch('http://localhost:8090/fetch.js');
Expand Down Expand Up @@ -63,9 +74,9 @@ Apache 2.0 - See [LICENSE][license-url] for more information.
[gitter-url]: https://gitter.im/open-telemetry/opentelemetry-node?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/master/LICENSE
[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat
[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-plugin-fetch
[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-fetch
[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-plugin-fetch
[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-fetch&type=dev
[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugin-fetch
[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugin-fetch.svg
[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-instrumentation-fetch
[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-instrumentation-fetch
[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-instrumentation-fetch
[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-instrumentation-fetch&type=dev
[npm-url]: https://www.npmjs.com/package/@opentelemetry/instrumentation-fetch
[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Finstrumentation-fetch.svg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@opentelemetry/plugin-fetch",
"name": "@opentelemetry/instrumentation-fetch",
"version": "0.14.0",
"description": "OpenTelemetry fetch automatic instrumentation package.",
"main": "build/src/index.js",
Expand Down Expand Up @@ -76,7 +76,8 @@
"dependencies": {
"@opentelemetry/api": "^0.14.0",
"@opentelemetry/core": "^0.14.0",
"@opentelemetry/web": "^0.14.0",
"shimmer": "^1.2.1"
"@opentelemetry/instrumentation": "^0.14.0",
"@opentelemetry/semantic-conventions": "^0.14.0",
"@opentelemetry/web": "^0.14.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,4 @@
*/
export enum AttributeNames {
COMPONENT = 'component',
HTTP_HOST = 'http.host',
HTTP_FLAVOR = 'http.flavor',
HTTP_METHOD = 'http.method',
HTTP_SCHEME = 'http.scheme',
HTTP_STATUS_CODE = 'http.status_code',
HTTP_STATUS_TEXT = 'http.status_text',
HTTP_URL = 'http.url',
HTTP_TARGET = 'http.target',
HTTP_USER_AGENT = 'http.user_agent',
HTTP_RESPONSE_CONTENT_LENGTH = 'http.response_content_length',
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
* limitations under the License.
*/

import * as shimmer from 'shimmer';
import * as api from '@opentelemetry/api';
import {
isWrapped,
InstrumentationBase,
InstrumentationConfig,
} from '@opentelemetry/instrumentation';
import * as core from '@opentelemetry/core';
import * as web from '@opentelemetry/web';
import { AttributeNames } from './enums/AttributeNames';
import { HttpAttribute } from '@opentelemetry/semantic-conventions';
import { FetchError, FetchResponse, SpanData } from './types';
import { VERSION } from './version';

Expand All @@ -31,7 +36,7 @@ const OBSERVER_WAIT_TIME_MS = 300;
/**
* FetchPlugin Config
*/
export interface FetchPluginConfig extends core.PluginConfig {
export interface FetchInstrumentationConfig extends InstrumentationConfig {
// the number of timing resources is limited, after the limit
// (chrome 250, safari 150) the information is not collected anymore
// the only way to prevent that is to regularly clean the resources
Expand All @@ -40,18 +45,38 @@ export interface FetchPluginConfig extends core.PluginConfig {
clearTimingResources?: boolean;
// urls which should include trace headers when origin doesn't match
propagateTraceHeaderCorsUrls?: web.PropagateTraceHeaderCorsUrls;
/**
* URLs that partially match any regex in ignoreUrls will not be traced.
* In addition, URLs that are _exact matches_ of strings in ignoreUrls will
* also not be traced.
*/
ignoreUrls?: Array<string | RegExp>;
}

/**
* This class represents a fetch plugin for auto instrumentation
*/
export class FetchPlugin extends core.BasePlugin<Promise<Response>> {
moduleName = 'fetch';
export class FetchInstrumentation extends InstrumentationBase<
Promise<Response>
> {
readonly component: string = 'fetch';
readonly version: string = VERSION;
moduleName = this.component;
private _usedResources = new WeakSet<PerformanceResourceTiming>();
private _tasksCount = 0;

constructor(protected _config: FetchPluginConfig = {}) {
super('@opentelemetry/plugin-fetch', VERSION);
constructor(config: FetchInstrumentationConfig = {}) {
super(
'@opentelemetry/instrumentation-fetch',
VERSION,
Object.assign({}, config)
);
}

init() {}

private _getConfig(): FetchInstrumentationConfig {
return this._config;
}

/**
Expand All @@ -63,7 +88,7 @@ export class FetchPlugin extends core.BasePlugin<Promise<Response>> {
span: api.Span,
corsPreFlightRequest: PerformanceResourceTiming
): void {
const childSpan = this._tracer.startSpan(
const childSpan = this.tracer.startSpan(
'CORS Preflight',
{
startTime: corsPreFlightRequest[web.PerformanceTimingNames.FETCH_START],
Expand All @@ -86,14 +111,14 @@ export class FetchPlugin extends core.BasePlugin<Promise<Response>> {
response: FetchResponse
): void {
const parsedUrl = web.parseUrl(response.url);
span.setAttribute(AttributeNames.HTTP_STATUS_CODE, response.status);
span.setAttribute(AttributeNames.HTTP_STATUS_TEXT, response.statusText);
span.setAttribute(AttributeNames.HTTP_HOST, parsedUrl.host);
span.setAttribute(HttpAttribute.HTTP_STATUS_CODE, response.status);
span.setAttribute(HttpAttribute.HTTP_STATUS_TEXT, response.statusText);
span.setAttribute(HttpAttribute.HTTP_HOST, parsedUrl.host);
span.setAttribute(
AttributeNames.HTTP_SCHEME,
HttpAttribute.HTTP_SCHEME,
parsedUrl.protocol.replace(':', '')
);
span.setAttribute(AttributeNames.HTTP_USER_AGENT, navigator.userAgent);
span.setAttribute(HttpAttribute.HTTP_USER_AGENT, navigator.userAgent);
}

/**
Expand All @@ -105,7 +130,7 @@ export class FetchPlugin extends core.BasePlugin<Promise<Response>> {
if (
!web.shouldPropagateTraceHeaders(
spanUrl,
this._config.propagateTraceHeaderCorsUrls
this._getConfig().propagateTraceHeaderCorsUrls
)
) {
return;
Expand All @@ -129,7 +154,7 @@ export class FetchPlugin extends core.BasePlugin<Promise<Response>> {
* @private
*/
private _clearResources() {
if (this._tasksCount === 0 && this._config.clearTimingResources) {
if (this._tasksCount === 0 && this._getConfig().clearTimingResources) {
performance.clearResourceTimings();
this._usedResources = new WeakSet<PerformanceResourceTiming>();
}
Expand All @@ -144,18 +169,18 @@ export class FetchPlugin extends core.BasePlugin<Promise<Response>> {
url: string,
options: Partial<Request | RequestInit> = {}
): api.Span | undefined {
if (core.isUrlIgnored(url, this._config.ignoreUrls)) {
if (core.isUrlIgnored(url, this._getConfig().ignoreUrls)) {
this._logger.debug('ignoring span as url matches ignored url');
return;
}
const method = (options.method || 'GET').toUpperCase();
const spanName = `HTTP ${method}`;
return this._tracer.startSpan(spanName, {
return this.tracer.startSpan(spanName, {
kind: api.SpanKind.CLIENT,
attributes: {
[AttributeNames.COMPONENT]: this.moduleName,
[AttributeNames.HTTP_METHOD]: method,
[AttributeNames.HTTP_URL]: url,
[HttpAttribute.HTTP_METHOD]: method,
[HttpAttribute.HTTP_URL]: url,
},
});
}
Expand Down Expand Up @@ -245,15 +270,13 @@ export class FetchPlugin extends core.BasePlugin<Promise<Response>> {
original: (input: RequestInfo, init?: RequestInit) => Promise<Response>
): ((input: RequestInfo, init?: RequestInit) => Promise<Response>) => {
const plugin = this;

return function patchConstructor(
this: (input: RequestInfo, init?: RequestInit) => Promise<Response>,
input: RequestInfo,
init?: RequestInit
): Promise<Response> {
const url = input instanceof Request ? input.url : input;
const options = input instanceof Request ? input : init || {};

const span = plugin._createSpan(url, options);
if (!span) {
return original.apply(this, [url, options]);
Expand Down Expand Up @@ -340,24 +363,21 @@ export class FetchPlugin extends core.BasePlugin<Promise<Response>> {
}

/**
* implements patch function
* implements enable function
*/
patch() {
if (core.isWrapped(window.fetch)) {
shimmer.unwrap(window, 'fetch');
enable() {
if (isWrapped(window.fetch)) {
this._unwrap(window, 'fetch');
this._logger.debug('removing previous patch for constructor');
}

shimmer.wrap(window, 'fetch', this._patchConstructor());

return this._moduleExports;
this._wrap(window, 'fetch', this._patchConstructor());
}

/**
* implements unpatch function
*/
unpatch() {
shimmer.unwrap(window, 'fetch');
disable() {
this._unwrap(window, 'fetch');
this._usedResources = new WeakSet<PerformanceResourceTiming>();
}
}
Loading