Skip to content

Commit

Permalink
chore: converting fetch plugin to instrumentation (#1796)
Browse files Browse the repository at this point in the history
  • Loading branch information
obecny authored Jan 12, 2021
1 parent 4ab9465 commit 5862bc0
Show file tree
Hide file tree
Showing 21 changed files with 113 additions and 82 deletions.
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
File renamed without changes.
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

0 comments on commit 5862bc0

Please sign in to comment.