Skip to content

Commit 23db7f0

Browse files
authored
feat(tracing): auto flush BatchSpanProcessor on browser (#2243)
1 parent a3b7738 commit 23db7f0

24 files changed

+285
-27
lines changed

packages/opentelemetry-tracing/karma.conf.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const karmaBaseConfig = require('../../karma.base');
1919

2020
module.exports = (config) => {
2121
config.set(Object.assign({}, karmaBaseConfig, {
22-
webpack: karmaWebpackConfig
22+
webpack: karmaWebpackConfig,
23+
files: ['test/browser/index-webpack.ts'],
24+
preprocessors: { 'test/browser/index-webpack.ts': ['webpack'] }
2325
}))
2426
};

packages/opentelemetry-tracing/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"scripts": {
1515
"compile": "tsc --build tsconfig.json tsconfig.esm.json",
1616
"clean": "tsc --build --clean tsconfig.json tsconfig.esm.json",
17-
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/index-webpack.ts'",
17+
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'",
1818
"test:browser": "nyc karma start --single-run",
1919
"tdd": "npm run tdd:node",
2020
"tdd:node": "npm run test -- --watch-extensions ts --watch",

packages/opentelemetry-tracing/src/BasicTracerProvider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { SDKRegistrationConfig, TracerConfig } from './types';
3737
// eslint-disable-next-line @typescript-eslint/no-var-requires
3838
const merge = require('lodash.merge');
3939
import { SpanExporter } from './export/SpanExporter';
40-
import { BatchSpanProcessor } from './export/BatchSpanProcessor';
40+
import { BatchSpanProcessor } from './platform';
4141

4242
export type PROPAGATOR_FACTORY = () => TextMapPropagator;
4343
export type EXPORTER_FACTORY = () => SpanExporter;

packages/opentelemetry-tracing/src/export/BatchSpanProcessor.ts renamed to packages/opentelemetry-tracing/src/export/BatchSpanProcessorBase.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { SpanExporter } from './SpanExporter';
3232
* Implementation of the {@link SpanProcessor} that batches spans exported by
3333
* the SDK then pushes them to the exporter pipeline.
3434
*/
35-
export class BatchSpanProcessor implements SpanProcessor {
35+
export abstract class BatchSpanProcessorBase<T extends BufferConfig> implements SpanProcessor {
3636
private readonly _maxExportBatchSize: number;
3737
private readonly _maxQueueSize: number;
3838
private readonly _scheduledDelayMillis: number;
@@ -43,23 +43,23 @@ export class BatchSpanProcessor implements SpanProcessor {
4343
private _isShutdown = false;
4444
private _shuttingDownPromise: Promise<void> = Promise.resolve();
4545

46-
constructor(private readonly _exporter: SpanExporter, config?: BufferConfig) {
46+
constructor(private readonly _exporter: SpanExporter, config?: T) {
4747
const env = getEnv();
4848
this._maxExportBatchSize =
4949
typeof config?.maxExportBatchSize === 'number'
5050
? config.maxExportBatchSize
5151
: env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE;
5252
this._maxQueueSize =
5353
typeof config?.maxQueueSize === 'number'
54-
? config?.maxQueueSize
54+
? config.maxQueueSize
5555
: env.OTEL_BSP_MAX_QUEUE_SIZE;
5656
this._scheduledDelayMillis =
5757
typeof config?.scheduledDelayMillis === 'number'
58-
? config?.scheduledDelayMillis
58+
? config.scheduledDelayMillis
5959
: env.OTEL_BSP_SCHEDULE_DELAY;
6060
this._exportTimeoutMillis =
6161
typeof config?.exportTimeoutMillis === 'number'
62-
? config?.exportTimeoutMillis
62+
? config.exportTimeoutMillis
6363
: env.OTEL_BSP_EXPORT_TIMEOUT;
6464
}
6565

@@ -87,6 +87,9 @@ export class BatchSpanProcessor implements SpanProcessor {
8787
this._isShutdown = true;
8888
this._shuttingDownPromise = new Promise((resolve, reject) => {
8989
Promise.resolve()
90+
.then(() => {
91+
return this.onShutdown();
92+
})
9093
.then(() => {
9194
return this._flushAll();
9295
})
@@ -190,4 +193,6 @@ export class BatchSpanProcessor implements SpanProcessor {
190193
this._timer = undefined;
191194
}
192195
}
196+
197+
protected abstract onShutdown(): void;
193198
}

packages/opentelemetry-tracing/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
export * from './Tracer';
1818
export * from './BasicTracerProvider';
19+
export * from './platform';
1920
export * from './export/ConsoleSpanExporter';
20-
export * from './export/BatchSpanProcessor';
2121
export * from './export/InMemorySpanExporter';
2222
export * from './export/ReadableSpan';
2323
export * from './export/SimpleSpanProcessor';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { BatchSpanProcessorBase } from '../../../export/BatchSpanProcessorBase';
18+
import { SpanExporter } from '../../../export/SpanExporter';
19+
import { BatchSpanProcessorBrowserConfig } from '../../../types';
20+
21+
export class BatchSpanProcessor extends BatchSpanProcessorBase<BatchSpanProcessorBrowserConfig> {
22+
private _visibilityChangeListener?: () => void
23+
private _pageHideListener?: () => void
24+
25+
constructor(_exporter: SpanExporter, config?: BatchSpanProcessorBrowserConfig) {
26+
super(_exporter, config)
27+
this.onInit(config)
28+
}
29+
30+
private onInit(config?: BatchSpanProcessorBrowserConfig): void {
31+
if (config?.disableAutoFlushOnDocumentHide !== true && document != null) {
32+
this._visibilityChangeListener = () => {
33+
if (document.visibilityState === 'hidden') {
34+
void this.forceFlush();
35+
}
36+
}
37+
this._pageHideListener = () => {
38+
void this.forceFlush()
39+
}
40+
document.addEventListener('visibilitychange', this._visibilityChangeListener);
41+
42+
// use 'pagehide' event as a fallback for Safari; see https://bugs.webkit.org/show_bug.cgi?id=116769
43+
document.addEventListener('pagehide', this._pageHideListener);
44+
}
45+
}
46+
47+
protected onShutdown(): void {
48+
if (this._visibilityChangeListener) {
49+
document.removeEventListener('visibilitychange', this._visibilityChangeListener);
50+
}
51+
if (this._pageHideListener) {
52+
document.removeEventListener('pagehide', this._pageHideListener);
53+
}
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export * from './export/BatchSpanProcessor';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export * from './node';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { BatchSpanProcessorBase } from '../../../export/BatchSpanProcessorBase';
18+
import { BufferConfig } from '../../../types';
19+
20+
export class BatchSpanProcessor extends BatchSpanProcessorBase<BufferConfig> {
21+
protected onShutdown(): void {}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export * from './export/BatchSpanProcessor';

packages/opentelemetry-tracing/src/types.ts

+7
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,10 @@ export interface BufferConfig {
8989
* The default value is 2048. */
9090
maxQueueSize?: number;
9191
}
92+
93+
/** Interface configuration for BatchSpanProcessor on browser */
94+
export interface BatchSpanProcessorBrowserConfig extends BufferConfig {
95+
/** Disable flush when a user navigates to a new page, closes the tab or the browser, or,
96+
* on mobile, switches to a different app. Auto flush is enabled by default. */
97+
disableAutoFlushOnDocumentHide?: boolean;
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as assert from 'assert';
18+
import * as sinon from 'sinon';
19+
import { SpanExporter } from '../../../src';
20+
import { BatchSpanProcessor } from '../../../src/platform/browser/export/BatchSpanProcessor';
21+
import { TestTracingSpanExporter } from '../../common/export/TestTracingSpanExporter';
22+
23+
describe('BatchSpanProcessor - web', () => {
24+
let visibilityState: VisibilityState = 'visible';
25+
let exporter: SpanExporter
26+
let processor: BatchSpanProcessor;
27+
let forceFlushSpy: sinon.SinonStub;
28+
let visibilityChangeEvent: Event;
29+
let pageHideEvent: Event
30+
31+
beforeEach(() => {
32+
sinon.replaceGetter(document, 'visibilityState', () => visibilityState);
33+
visibilityState = 'visible';
34+
exporter = new TestTracingSpanExporter();
35+
processor = new BatchSpanProcessor(exporter, {});
36+
forceFlushSpy = sinon.stub(processor, 'forceFlush');
37+
visibilityChangeEvent = new Event('visibilitychange');
38+
pageHideEvent = new Event('pagehide');
39+
});
40+
41+
afterEach(async () => {
42+
sinon.restore();
43+
});
44+
45+
describe('when document becomes hidden', () => {
46+
const testDocumentHide = (hideDocument: () => void) => {
47+
it('should force flush spans', () => {
48+
assert.strictEqual(forceFlushSpy.callCount, 0);
49+
hideDocument()
50+
assert.strictEqual(forceFlushSpy.callCount, 1);
51+
});
52+
53+
describe('AND shutdown has been called', () => {
54+
it('should NOT force flush spans', async () => {
55+
assert.strictEqual(forceFlushSpy.callCount, 0);
56+
await processor.shutdown();
57+
hideDocument()
58+
assert.strictEqual(forceFlushSpy.callCount, 0);
59+
});
60+
})
61+
62+
describe('AND disableAutoFlushOnDocumentHide configuration option', () => {
63+
it('set to false should force flush spans', () => {
64+
processor = new BatchSpanProcessor(exporter, { disableAutoFlushOnDocumentHide: false });
65+
forceFlushSpy = sinon.stub(processor, 'forceFlush');
66+
assert.strictEqual(forceFlushSpy.callCount, 0);
67+
hideDocument()
68+
assert.strictEqual(forceFlushSpy.callCount, 1);
69+
})
70+
71+
it('set to true should NOT force flush spans', () => {
72+
processor = new BatchSpanProcessor(exporter, { disableAutoFlushOnDocumentHide: true });
73+
forceFlushSpy = sinon.stub(processor, 'forceFlush');
74+
assert.strictEqual(forceFlushSpy.callCount, 0);
75+
hideDocument()
76+
assert.strictEqual(forceFlushSpy.callCount, 0);
77+
})
78+
})
79+
}
80+
81+
describe('by the visibilitychange event', () => {
82+
testDocumentHide(() => {
83+
visibilityState = 'hidden';
84+
document.dispatchEvent(visibilityChangeEvent);
85+
})
86+
})
87+
88+
describe('by the pagehide event', () => {
89+
testDocumentHide(() => {
90+
document.dispatchEvent(pageHideEvent);
91+
})
92+
})
93+
})
94+
95+
describe('when document becomes visible', () => {
96+
it('should NOT force flush spans', () => {
97+
assert.strictEqual(forceFlushSpy.callCount, 0);
98+
document.dispatchEvent(visibilityChangeEvent);
99+
assert.strictEqual(forceFlushSpy.callCount, 0);
100+
});
101+
})
102+
});

packages/opentelemetry-tracing/test/index-webpack.ts renamed to packages/opentelemetry-tracing/test/browser/index-webpack.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
const testsContext = require.context('.', true, /test$/);
16+
const testsContext = require.context('../browser', true, /test$/);
1717
testsContext.keys().forEach(testsContext);
1818

19+
const testsContextCommon = require.context('../common', true, /test$/);
20+
testsContextCommon.keys().forEach(testsContextCommon);
21+
1922
const srcContext = require.context('.', true, /src$/);
2023
srcContext.keys().forEach(srcContext);

packages/opentelemetry-tracing/test/BasicTracerProvider.test.ts renamed to packages/opentelemetry-tracing/test/common/BasicTracerProvider.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import {
4343
InMemorySpanExporter,
4444
SpanExporter,
4545
BatchSpanProcessor,
46-
} from '../src';
46+
} from '../../src';
4747

4848
describe('BasicTracerProvider', () => {
4949
let removeEvent: Function | undefined;

packages/opentelemetry-tracing/test/MultiSpanProcessor.test.ts renamed to packages/opentelemetry-tracing/test/common/MultiSpanProcessor.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ import {
2222
SimpleSpanProcessor,
2323
Span,
2424
SpanProcessor,
25-
} from '../src';
25+
} from '../../src';
2626
import {
2727
setGlobalErrorHandler,
2828
loggingErrorHandler,
2929
} from '@opentelemetry/core';
30-
import { MultiSpanProcessor } from '../src/MultiSpanProcessor';
30+
import { MultiSpanProcessor } from '../../src/MultiSpanProcessor';
3131

3232
class TestProcessor implements SpanProcessor {
3333
spans: Span[] = [];

packages/opentelemetry-tracing/test/Span.test.ts renamed to packages/opentelemetry-tracing/test/common/Span.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
} from '@opentelemetry/core';
3131
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
3232
import * as assert from 'assert';
33-
import { BasicTracerProvider, Span, SpanProcessor } from '../src';
33+
import { BasicTracerProvider, Span, SpanProcessor } from '../../src';
3434

3535
const performanceTimeOrigin = hrTime();
3636

packages/opentelemetry-tracing/test/Tracer.test.ts renamed to packages/opentelemetry-tracing/test/common/Tracer.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
suppressTracing
3333
} from '@opentelemetry/core';
3434
import * as assert from 'assert';
35-
import { BasicTracerProvider, Span, Tracer } from '../src';
35+
import { BasicTracerProvider, Span, Tracer } from '../../src';
3636
import { TestStackContextManager } from './export/TestStackContextManager';
3737
import * as sinon from 'sinon';
3838

0 commit comments

Comments
 (0)