Skip to content

Commit

Permalink
chore: adding interceptor for getting headers before each request
Browse files Browse the repository at this point in the history
  • Loading branch information
obecny committed Mar 26, 2021
1 parent 435ab73 commit 3949289
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 5 deletions.
9 changes: 8 additions & 1 deletion examples/tracer-web/examples/zipkin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import { ZipkinExporter } from '@opentelemetry/exporter-zipkin';

const provider = new WebTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
provider.addSpanProcessor(new SimpleSpanProcessor(new ZipkinExporter()));
provider.addSpanProcessor(new SimpleSpanProcessor(new ZipkinExporter({
// testing interceptor
// getHeadersBeforeSend: ()=> {
// return {
// foo: 'bar',
// }
// }
})));

provider.register();

Expand Down
12 changes: 11 additions & 1 deletion packages/opentelemetry-exporter-zipkin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ const options = {
'my-header': 'header-value',
},
url: 'your-zipkin-url',
serviceName: 'your-application-name'
serviceName: 'your-application-name',
// optional interceptor
getHeadersBeforeSend: () => {
return {
'my-header': 'header-value',
}
}
}
const exporter = new ZipkinExporter(options);
```
Expand All @@ -46,6 +52,10 @@ You can use built-in `SimpleSpanProcessor` or `BatchSpanProcessor` or write your
- [SimpleSpanProcessor](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk.md#simple-processor): The implementation of `SpanProcessor` that passes ended span directly to the configured `SpanExporter`.
- [BatchSpanProcessor](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk.md#batching-processor): The implementation of the `SpanProcessor` that batches ended spans and pushes them to the configured `SpanExporter`. It is recommended to use this `SpanProcessor` for better performance and optimization.

### Options
- **getHeadersBeforeSend** - optional interceptor that allows adding new headers everytime time the exporter is going to send spans.
This is optional and can be used if headers are changing over time. This is a sync callback.

## Viewing your traces

Please visit the Zipkin UI endpoint <http://localhost:9411>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import * as zipkinTypes from '../../types';

/**
* Prepares send function that will send spans to the remote Zipkin service.
* @param urlStr - url to send spans
* @param headers - headers
* send
*/
export function prepareSend(urlStr: string, headers?: Record<string, string>) {
let xhrHeaders: Record<string, string>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import * as zipkinTypes from '../../types';

/**
* Prepares send function that will send spans to the remote Zipkin service.
* @param urlStr - url to send spans
* @param headers - headers
* send
*/
export function prepareSend(urlStr: string, headers?: Record<string, string>) {
const urlOpts = url.parse(urlStr);
Expand Down
5 changes: 4 additions & 1 deletion packages/opentelemetry-exporter-zipkin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import { ExportResult } from '@opentelemetry/core';
* Exporter config
*/
export interface ExporterConfig {
headers?: { [key: string]: string };
headers?: Record<string, string>;
serviceName?: string;
url?: string;
// Optional mapping overrides for OpenTelemetry status code and description.
statusCodeTagName?: string;
statusDescriptionTagName?: string;
getHeadersBeforeSend?: () => Record<string, string> | undefined;
}

/**
Expand Down Expand Up @@ -184,3 +185,5 @@ export type SendFunction = (
zipkinSpans: Span[],
done: (result: ExportResult) => void
) => void;

export type GetHeaders = () => Record<string, string> | undefined;
24 changes: 24 additions & 0 deletions packages/opentelemetry-exporter-zipkin/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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 { GetHeaders } from './types';

export function prepareGetHeaders(
getHeadersBeforeSend: GetHeaders
): () => Record<string, string> | undefined {
return function () {
return getHeadersBeforeSend();
};
}
26 changes: 24 additions & 2 deletions packages/opentelemetry-exporter-zipkin/src/zipkin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
statusDescriptionTagName,
} from './transform';
import { SERVICE_RESOURCE } from '@opentelemetry/resources';
import { prepareGetHeaders } from './utils';

/**
* Zipkin Exporter
Expand All @@ -34,19 +35,27 @@ export class ZipkinExporter implements SpanExporter {
private readonly DEFAULT_SERVICE_NAME = 'OpenTelemetry Service';
private readonly _statusCodeTagName: string;
private readonly _statusDescriptionTagName: string;
private _urlStr: string;
private _send: zipkinTypes.SendFunction;
private _getHeaders: zipkinTypes.GetHeaders | undefined;
private _serviceName?: string;
private _isShutdown: boolean;
private _sendingPromises: Promise<unknown>[] = [];

constructor(config: zipkinTypes.ExporterConfig = {}) {
const urlStr = config.url || ZipkinExporter.DEFAULT_URL;
this._send = prepareSend(urlStr, config.headers);
this._urlStr = config.url || ZipkinExporter.DEFAULT_URL;
this._send = prepareSend(this._urlStr, config.headers);
this._serviceName = config.serviceName;
this._statusCodeTagName = config.statusCodeTagName || statusCodeTagName;
this._statusDescriptionTagName =
config.statusDescriptionTagName || statusDescriptionTagName;
this._isShutdown = false;
if (typeof config.getHeadersBeforeSend === 'function') {
this._getHeaders = prepareGetHeaders(config.getHeadersBeforeSend);
} else {
// noop
this._beforeSend = function () {};
}
}

/**
Expand Down Expand Up @@ -96,6 +105,18 @@ export class ZipkinExporter implements SpanExporter {
});
}

/**
* if user defines getHeadersBeforeSend in config then this will be called
* everytime before send, otherwise it will be replaced with noop in
* constructor
* @default noop
*/
private _beforeSend() {
if (this._getHeaders) {
this._send = prepareSend(this._urlStr, this._getHeaders());
}
}

/**
* Transform spans and sends to Zipkin service.
*/
Expand All @@ -116,6 +137,7 @@ export class ZipkinExporter implements SpanExporter {
this._statusDescriptionTagName
)
);
this._beforeSend();
return this._send(zipkinSpans, (result: ExportResult) => {
if (done) {
return done(result);
Expand Down
34 changes: 34 additions & 0 deletions packages/opentelemetry-exporter-zipkin/test/browser/zipkin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,40 @@ describe('Zipkin Exporter - web', () => {
});
});
});
describe('when getHeadersBeforeSend is defined', () => {
let server: any;
beforeEach(() => {
server = sinon.fakeServer.create();
spySend.restore();
});

afterEach(() => {
server.restore();
});

it('should add headers from callback', done => {
zipkinExporter = new ZipkinExporter({
getHeadersBeforeSend: () => {
return {
foo1: 'bar1',
foo2: 'bar2',
};
},
});
zipkinExporter.export(spans, () => {});

setTimeout(() => {
const [{ requestHeaders }] = server.requests;

ensureHeadersContain(requestHeaders, {
foo1: 'bar1',
foo2: 'bar2',
});

done();
});
});
});

describe('export with custom headers', () => {
let server: any;
Expand Down
50 changes: 50 additions & 0 deletions packages/opentelemetry-exporter-zipkin/test/common/zipkin.test.ts
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 { ZipkinExporter } from '../../src';
import * as sinon from 'sinon';
import { mockedReadableSpan } from '../helper';

describe('zipkin - header interceptor', () => {
describe('getHeadersBeforeSend', () => {
describe('when callback is defined', () => {
it('should call callback before sending', () => {
const getHeadersBeforeSend = sinon.spy();
const span = Object.assign({}, mockedReadableSpan);
const exporter = new ZipkinExporter({
getHeadersBeforeSend,
});
const oldFunction = exporter['_send'];
exporter.export([span], () => {});

assert.strictEqual(getHeadersBeforeSend.callCount, 1);
assert.notStrictEqual(exporter['_getHeaders'], undefined);
assert.notStrictEqual(oldFunction, exporter['_send']);
});
});
describe('when callback is NOT defined', () => {
it('should call callback before sending', () => {
const span = Object.assign({}, mockedReadableSpan);
const exporter = new ZipkinExporter();
const oldFunction = exporter['_send'];
assert.strictEqual(exporter['_getHeaders'], undefined);
exporter.export([span], () => {});
assert.strictEqual(oldFunction, exporter['_send']);
});
});
});
});

0 comments on commit 3949289

Please sign in to comment.