Skip to content

Commit b7f74b3

Browse files
authored
[Structured Logs]: Buffering and Flushing of Logs (#2930)
1 parent 58535ae commit b7f74b3

10 files changed

+380
-49
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- Logs: Models & Envelopes ([#2916](https://github.com/getsentry/sentry-dart/pull/2916))
88
- Logs: Integrate in Sentry Client ([#2920](https://github.com/getsentry/sentry-dart/pull/2920))
9+
- [Structured Logs]: Buffering and Flushing of Logs ([#2930](https://github.com/getsentry/sentry-dart/pull/2930))
910

1011
## 9.0.0-beta.2
1112

dart/lib/src/noop_log_batcher.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'dart:async';
2+
3+
import 'sentry_log_batcher.dart';
4+
import 'protocol/sentry_log.dart';
5+
6+
class NoopLogBatcher implements SentryLogBatcher {
7+
@override
8+
FutureOr<void> addLog(SentryLog log) {}
9+
10+
@override
11+
Future<void> flush() async {}
12+
}

dart/lib/src/sentry_client.dart

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import 'type_check_hint.dart';
2727
import 'utils/isolate_utils.dart';
2828
import 'utils/regex_utils.dart';
2929
import 'utils/stacktrace_utils.dart';
30+
import 'sentry_log_batcher.dart';
3031
import 'version.dart';
3132

3233
/// Default value for [SentryUser.ipAddress]. It gets set when an event does not have
@@ -75,6 +76,9 @@ class SentryClient {
7576
if (enableFlutterSpotlight) {
7677
options.transport = SpotlightHttpTransport(options, options.transport);
7778
}
79+
if (options.enableLogs) {
80+
options.logBatcher = SentryLogBatcher(options);
81+
}
7882
return SentryClient._(options);
7983
}
8084

@@ -528,9 +532,12 @@ class SentryClient {
528532
SentryLog? processedLog = log;
529533
if (beforeSendLog != null) {
530534
try {
531-
processedLog = beforeSendLog(log);
532-
if (processedLog == null) {
533-
return;
535+
final callbackResult = beforeSendLog(log);
536+
537+
if (callbackResult is Future<SentryLog?>) {
538+
processedLog = await callbackResult;
539+
} else {
540+
processedLog = callbackResult;
534541
}
535542
} catch (exception, stackTrace) {
536543
_options.logger(
@@ -544,15 +551,9 @@ class SentryClient {
544551
}
545552
}
546553
}
547-
548-
// TODO: Batch in separate PR, so we can send multiple logs at once.
549-
final envelope = SentryEnvelope.fromLogs(
550-
[processedLog ?? log],
551-
_options.sdk,
552-
);
553-
554-
// TODO: Make sure the Android SDK understands the log envelope type.
555-
await captureEnvelope(envelope);
554+
if (processedLog != null) {
555+
_options.logBatcher.addLog(processedLog);
556+
}
556557
}
557558

558559
void close() {

dart/lib/src/sentry_log_batcher.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import 'dart:async';
2+
import 'sentry_envelope.dart';
3+
import 'sentry_options.dart';
4+
import 'protocol/sentry_log.dart';
5+
import 'package:meta/meta.dart';
6+
7+
@internal
8+
class SentryLogBatcher {
9+
SentryLogBatcher(this._options, {Duration? flushTimeout, int? maxBufferSize})
10+
: _flushTimeout = flushTimeout ?? Duration(seconds: 5),
11+
_maxBufferSize = maxBufferSize ?? 100;
12+
13+
final SentryOptions _options;
14+
final Duration _flushTimeout;
15+
final int _maxBufferSize;
16+
17+
final _logBuffer = <SentryLog>[];
18+
19+
Timer? _flushTimer;
20+
21+
void addLog(SentryLog log) {
22+
_logBuffer.add(log);
23+
24+
_flushTimer?.cancel();
25+
26+
if (_logBuffer.length >= _maxBufferSize) {
27+
return flush();
28+
} else {
29+
_flushTimer = Timer(_flushTimeout, flush);
30+
}
31+
}
32+
33+
void flush() {
34+
_flushTimer?.cancel();
35+
_flushTimer = null;
36+
37+
final logs = List<SentryLog>.from(_logBuffer);
38+
_logBuffer.clear();
39+
40+
if (logs.isEmpty) {
41+
return;
42+
}
43+
44+
final envelope = SentryEnvelope.fromLogs(
45+
logs,
46+
_options.sdk,
47+
);
48+
49+
// TODO: Make sure the Android SDK understands the log envelope type.
50+
_options.transport.send(envelope);
51+
}
52+
}

dart/lib/src/sentry_options.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import 'sentry_exception_factory.dart';
1515
import 'sentry_stack_trace_factory.dart';
1616
import 'transport/noop_transport.dart';
1717
import 'version.dart';
18+
import 'sentry_log_batcher.dart';
19+
import 'noop_log_batcher.dart';
1820

1921
// TODO: shutdownTimeout, flushTimeoutMillis
2022
// https://api.dart.dev/stable/2.10.2/dart-io/HttpClient/close.html doesn't have a timeout param, we'd need to implement manually
@@ -540,6 +542,9 @@ class SentryOptions {
540542
/// Disabled by default.
541543
bool enableLogs = false;
542544

545+
@internal
546+
SentryLogBatcher logBatcher = NoopLogBatcher();
547+
543548
SentryOptions({String? dsn, Platform? platform, RuntimeChecker? checker}) {
544549
this.dsn = dsn;
545550
if (platform != null) {
@@ -671,7 +676,7 @@ typedef BeforeMetricCallback = bool Function(
671676

672677
/// This function is called right before a log is about to be sent.
673678
/// Can return a modified log or null to drop the log.
674-
typedef BeforeSendLogCallback = SentryLog? Function(SentryLog log);
679+
typedef BeforeSendLogCallback = FutureOr<SentryLog?> Function(SentryLog log);
675680

676681
/// Used to provide timestamp for logging.
677682
typedef ClockProvider = DateTime Function();

dart/test/mocks/mock_log_batcher.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import 'dart:async';
2+
3+
import 'package:sentry/src/protocol/sentry_log.dart';
4+
import 'package:sentry/src/sentry_log_batcher.dart';
5+
6+
class MockLogBatcher implements SentryLogBatcher {
7+
final addLogCalls = <SentryLog>[];
8+
final flushCalls = <void>[];
9+
10+
@override
11+
FutureOr<void> addLog(SentryLog log) {
12+
addLogCalls.add(log);
13+
}
14+
15+
@override
16+
Future<void> flush() async {
17+
flushCalls.add(null);
18+
}
19+
}

0 commit comments

Comments
 (0)