Skip to content

Commit 7e243ed

Browse files
committed
fix(core): Emit flush event in Client.flush() when client has no transport
`Client.flush()` short-circuited before emitting the `flush` event when the client had no transport (e.g. no DSN was configured), leaving the weight-based flushers for logs and metrics with idle `setTimeout` handles that never resolved. Emit `flush` unconditionally so the weight-based flusher listeners drain their buffers and clear the timers on every `flush()` call.
1 parent 86dc30a commit 7e243ed

3 files changed

Lines changed: 54 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Other Changes
6+
7+
- fix(core): Emit `flush` event in `Client.flush()` when client has no transport
8+
59
## 10.48.0
610

711
### Important Changes

packages/core/src/client.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,12 +424,16 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
424424
// @ts-expect-error - PromiseLike is a subset of Promise
425425
public async flush(timeout?: number): PromiseLike<boolean> {
426426
const transport = this._transport;
427+
428+
// Emit `flush` unconditionally so weight-based log/metric flushers drain
429+
// their buffers and clear their idle timers, even when no transport is
430+
// configured (e.g. no DSN).
431+
this.emit('flush');
432+
427433
if (!transport) {
428434
return true;
429435
}
430436

431-
this.emit('flush');
432-
433437
const clientFinished = await this._isClientDoneProcessing(timeout);
434438
const transportFlushed = await transport.flush(timeout);
435439

packages/core/test/lib/client.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3115,6 +3115,29 @@ describe('Client', () => {
31153115

31163116
safeUnrefSpy.mockRestore();
31173117
});
3118+
3119+
it('flush() drains the log buffer when client has no transport', async () => {
3120+
// Client without DSN — _transport is undefined
3121+
const options = getDefaultTestClientOptions({
3122+
enableLogs: true,
3123+
});
3124+
const client = new TestClient(options);
3125+
const scope = new Scope();
3126+
scope.setClient(client);
3127+
3128+
const flushLogsHandler = vi.fn();
3129+
client.on('flushLogs', flushLogsHandler);
3130+
3131+
// Capture a log which starts the weight-based flush timer
3132+
_INTERNAL_captureLog({ message: 'test log', level: 'info' }, scope);
3133+
3134+
expect(flushLogsHandler).not.toHaveBeenCalled();
3135+
3136+
// flush() should drain the buffer (and clear the timer) even without a transport
3137+
await client.flush();
3138+
3139+
expect(flushLogsHandler).toHaveBeenCalledTimes(1);
3140+
});
31183141
});
31193142

31203143
describe('metric weight-based flushing', () => {
@@ -3201,6 +3224,27 @@ describe('Client', () => {
32013224

32023225
safeUnrefSpy.mockRestore();
32033226
});
3227+
3228+
it('flush() drains the metric buffer when client has no transport', async () => {
3229+
// Client without DSN — _transport is undefined
3230+
const options = getDefaultTestClientOptions({});
3231+
const client = new TestClient(options);
3232+
const scope = new Scope();
3233+
scope.setClient(client);
3234+
3235+
const flushMetricsHandler = vi.fn();
3236+
client.on('flushMetrics', flushMetricsHandler);
3237+
3238+
// Capture a metric which starts the weight-based flush timer
3239+
_INTERNAL_captureMetric({ name: 'test_metric', value: 42, type: 'counter', attributes: {} }, { scope });
3240+
3241+
expect(flushMetricsHandler).not.toHaveBeenCalled();
3242+
3243+
// flush() should drain the buffer (and clear the timer) even without a transport
3244+
await client.flush();
3245+
3246+
expect(flushMetricsHandler).toHaveBeenCalledTimes(1);
3247+
});
32043248
});
32053249

32063250
describe('promise buffer usage', () => {

0 commit comments

Comments
 (0)