Skip to content

enh: use lifecycle hook for before send event #3017

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 42 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
bb15b43
Add os and device attributes to Flutter logs
denrase Jun 4, 2025
6a71e25
cleanup + changelog
denrase Jun 4, 2025
306f88f
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
denrase Jun 16, 2025
62fe9ae
dont return context
denrase Jun 16, 2025
17651d6
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
denrase Jun 16, 2025
5028a17
update cl
denrase Jun 16, 2025
07089f0
extract reading of os and device
denrase Jun 16, 2025
1155ba5
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
denrase Jun 16, 2025
c767a92
fix cl
denrase Jun 16, 2025
5c21eb1
extract contexts enrichment
denrase Jun 17, 2025
cc3b371
cleanup
denrase Jun 17, 2025
3770c0b
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
denrase Jun 17, 2025
4040b91
update cl
denrase Jun 17, 2025
9547395
fix test
denrase Jun 17, 2025
e65ec0b
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
denrase Jun 18, 2025
b5414b1
change to hooks api approach
denrase Jun 18, 2025
bdbe6f6
update client tests
denrase Jun 18, 2025
85706a6
update tests
denrase Jun 18, 2025
3f974a2
update cl
denrase Jun 18, 2025
2275fc8
test for LogsEnricherIntegration
denrase Jun 18, 2025
c5a0e8d
fix error
denrase Jun 18, 2025
7b903b0
fix analyzer issues
denrase Jun 18, 2025
4259a75
update integration name
denrase Jun 18, 2025
b359c71
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
buenaflor Jun 18, 2025
416ebb1
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
denrase Jun 20, 2025
4c9d308
use generic api to register lifecycle events
denrase Jun 20, 2025
ba3e0a5
use function type
denrase Jun 20, 2025
bc977e5
move to sep file
denrase Jun 20, 2025
843e043
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
buenaflor Jun 20, 2025
0cf5295
check if logs are enabled
denrase Jun 20, 2025
d189cfd
Merge branch 'feat/add-os-and-device-attributes-to-logs' of github.co…
denrase Jun 20, 2025
56b9e42
Remove `LogsEnricherIntegration` in load contexts integration
denrase Jun 20, 2025
de10a0d
update test with real expected values
denrase Jun 20, 2025
f2d6790
add comment
denrase Jun 20, 2025
edd8da8
fix analyze issue
denrase Jun 23, 2025
b903993
fix type casts
denrase Jun 23, 2025
9900bf1
Merge branch 'main' into feat/add-os-and-device-attributes-to-logs
buenaflor Jun 24, 2025
b3e7233
fix cl
denrase Jun 24, 2025
9d51684
Update
buenaflor Jun 24, 2025
4e720b9
Merge branch 'main' into enhancements/use-lifecycle-hook-before-send-…
buenaflor Jun 27, 2025
e052838
Update
buenaflor Jun 27, 2025
9e774c1
Merge branch 'main' into enhancements/use-lifecycle-hook-before-send-…
buenaflor Jul 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions dart/lib/sentry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export 'src/integration.dart';
export 'src/noop_isolate_error_integration.dart'
if (dart.library.io) 'src/isolate_error_integration.dart';
// ignore: invalid_export_of_internal_element
export 'src/observers.dart';
export 'src/performance_collector.dart';
export 'src/protocol.dart';
export 'src/protocol/sentry_feature_flag.dart';
Expand All @@ -38,7 +37,7 @@ export 'src/sentry_baggage.dart';
// ignore: invalid_export_of_internal_element
export 'src/sentry_client.dart';
// ignore: invalid_export_of_internal_element
export 'src/lifecycle/on_before_capture_log.dart';
export 'src/sdk_lifecycle_hooks.dart';
export 'src/sentry_envelope.dart';
export 'src/sentry_envelope_item.dart';
export 'src/sentry_options.dart';
Expand Down
10 changes: 10 additions & 0 deletions dart/lib/src/hub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -630,13 +630,23 @@

SentryProfilerFactory? _profilerFactory;

@internal

Check warning on line 633 in dart/lib/src/hub.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/hub.dart#L633

Added line #L633 was not covered by tests
Map<Type, List<Function>> get lifecycleCallbacks =>
_peek().client.lifecycleCallbacks;

Check warning on line 635 in dart/lib/src/hub.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/hub.dart#L635

Added line #L635 was not covered by tests

@internal
void registerCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {
final item = _peek();
item.client.registerCallback<T>(callback);
}

@internal

Check warning on line 644 in dart/lib/src/hub.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/hub.dart#L644

Added line #L644 was not covered by tests
void removeCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {
_peek().client.removeCallback<T>(callback);

Check warning on line 647 in dart/lib/src/hub.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/hub.dart#L647

Added line #L647 was not covered by tests
}

SentryEvent _assignTraceContext(SentryEvent event) {
// assign trace context
if (event.throwable != null && event.contexts.trace == null) {
Expand Down
10 changes: 10 additions & 0 deletions dart/lib/src/hub_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,19 @@
@override
FutureOr<void> captureLog(SentryLog log) => Sentry.currentHub.captureLog(log);

@override

Check warning on line 203 in dart/lib/src/hub_adapter.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/hub_adapter.dart#L203

Added line #L203 was not covered by tests
Map<Type, List<Function>> get lifecycleCallbacks =>
Sentry.currentHub.lifecycleCallbacks;

Check warning on line 205 in dart/lib/src/hub_adapter.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/hub_adapter.dart#L205

Added line #L205 was not covered by tests

@override
void registerCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {
Sentry.currentHub.registerCallback(callback);
}

@override

Check warning on line 213 in dart/lib/src/hub_adapter.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/hub_adapter.dart#L213

Added line #L213 was not covered by tests
void removeCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {
Sentry.currentHub.removeCallback(callback);

Check warning on line 216 in dart/lib/src/hub_adapter.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/hub_adapter.dart#L216

Added line #L216 was not covered by tests
}
}
2 changes: 1 addition & 1 deletion dart/lib/src/logs_enricher_integration.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import 'dart:async';
import 'package:meta/meta.dart';

import 'sdk_lifecycle_hooks.dart';
import 'utils/os_utils.dart';
import 'integration.dart';
import 'hub.dart';
import 'protocol/sentry_log_attribute.dart';
import 'sentry_options.dart';
import 'lifecycle/on_before_capture_log.dart';

@internal
class LogsEnricherIntegration extends Integration<SentryOptions> {
Expand Down
8 changes: 7 additions & 1 deletion dart/lib/src/noop_hub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,14 @@
@override
Scope get scope => Scope(_options);

@internal
@override
Map<Type, List<Function>> get lifecycleCallbacks => {};

Check warning on line 149 in dart/lib/src/noop_hub.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/noop_hub.dart#L148-L149

Added lines #L148 - L149 were not covered by tests

@override
void registerCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {}

@override

Check warning on line 155 in dart/lib/src/noop_hub.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/noop_hub.dart#L155

Added line #L155 was not covered by tests
void removeCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {}
}
7 changes: 7 additions & 0 deletions dart/lib/src/noop_sentry_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ class NoOpSentryClient implements SentryClient {
@override
FutureOr<void> captureLog(SentryLog log, {Scope? scope}) async {}

@override
Map<Type, List<Function>> get lifecycleCallbacks => {};

@override
void registerCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {}

@override
void removeCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {}
}
18 changes: 0 additions & 18 deletions dart/lib/src/observers.dart

This file was deleted.

17 changes: 17 additions & 0 deletions dart/lib/src/sdk_lifecycle_hooks.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:meta/meta.dart';

import '../sentry.dart';

@internal
class OnBeforeCaptureLog extends SdkLifecycleEvent {
OnBeforeCaptureLog(this.log);

final SentryLog log;
}

@internal
class OnBeforeSendEvent extends SdkLifecycleEvent {
OnBeforeSendEvent(this.event);

final SentryEvent event;
}
43 changes: 15 additions & 28 deletions dart/lib/src/sentry_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import 'client_reports/discard_reason.dart';
import 'event_processor/run_event_processors.dart';
import 'hint.dart';
import 'sdk_lifecycle_hooks.dart';
import 'protocol.dart';
import 'protocol/sentry_feedback.dart';
import 'scope.dart';
Expand All @@ -29,7 +30,7 @@
import 'utils/stacktrace_utils.dart';
import 'sentry_log_batcher.dart';
import 'version.dart';
import 'lifecycle/on_before_capture_log.dart';
import 'sdk_lifecycle_hooks.dart';

Check warning on line 33 in dart/lib/src/sentry_client.dart

View workflow job for this annotation

GitHub Actions / analyze / analyze

Duplicate import.

Try removing all but one import of the library. See https://dart.dev/diagnostics/duplicate_import to learn more about this problem.

/// Default value for [SentryUser.ipAddress]. It gets set when an event does not have
/// a user and IP address. Only applies if [SentryOptions.sendDefaultPii] is set
Expand Down Expand Up @@ -58,6 +59,8 @@

SentryStackTraceFactory get _stackTraceFactory => _options.stackTraceFactory;

@internal
Map<Type, List<Function>> get lifecycleCallbacks => _lifecycleCallbacks;

Check warning on line 63 in dart/lib/src/sentry_client.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/sentry_client.dart#L62-L63

Added lines #L62 - L63 were not covered by tests
final Map<Type, List<Function>> _lifecycleCallbacks = {};

/// Instantiates a client using [SentryOptions]
Expand Down Expand Up @@ -174,8 +177,8 @@
return _emptySentryId;
}

// Event is fully processed and ready to be sent, emit beforeSendEvent observer
await _emitBeforeSendEventObserver(preparedEvent, hint);
// Event is fully processed and ready to be sent
await _dispatchCallback(OnBeforeSendEvent(event));

var attachments = List<SentryAttachment>.from(scope?.attachments ?? []);
attachments.addAll(hint.attachments);
Expand Down Expand Up @@ -581,7 +584,7 @@
}

if (processedLog != null) {
await _dispatchCallbacks(OnBeforeCaptureLog(processedLog));
await _dispatchCallback(OnBeforeCaptureLog(processedLog));
_options.logBatcher.addLog(processedLog);
} else {
_options.recorder.recordLostEvent(
Expand Down Expand Up @@ -687,37 +690,21 @@
}
}

FutureOr<void> _emitBeforeSendEventObserver(
SentryEvent event, Hint hint) async {
for (final observer in _options.beforeSendEventObservers) {
try {
final result = observer.onBeforeSendEvent(event, hint);
if (result is Future) {
await result;
}
} catch (exception, stackTrace) {
_options.log(
SentryLevel.error,
'Error while running beforeSendEvent observer',
exception: exception,
stackTrace: stackTrace,
);
if (_options.automatedTestMode) {
rethrow;
}
}
}
}

@internal
void registerCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {
_lifecycleCallbacks[T] ??= [];
_lifecycleCallbacks[T]?.add(callback);
}

FutureOr<void> _dispatchCallbacks<T extends SdkLifecycleEvent>(
T event) async {
@internal

Check warning on line 700 in dart/lib/src/sentry_client.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/sentry_client.dart#L700

Added line #L700 was not covered by tests
void removeCallback<T extends SdkLifecycleEvent>(
SdkLifecycleCallback<T> callback) {
final callbacks = _lifecycleCallbacks[T];
callbacks?.remove(callback);

Check warning on line 704 in dart/lib/src/sentry_client.dart

View check run for this annotation

Codecov / codecov/patch

dart/lib/src/sentry_client.dart#L703-L704

Added lines #L703 - L704 were not covered by tests
}

FutureOr<void> _dispatchCallback<T extends SdkLifecycleEvent>(T event) async {
final callbacks = _lifecycleCallbacks[event.runtimeType] ?? [];
for (final cb in callbacks) {
try {
Expand Down
20 changes: 0 additions & 20 deletions dart/lib/src/sentry_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -511,26 +511,6 @@ class SentryOptions {
/// iOS only supports http proxies, while macOS also supports socks.
SentryProxy? proxy;

final List<BeforeSendEventObserver> _beforeSendEventObserver = [];

@internal
List<BeforeSendEventObserver> get beforeSendEventObservers =>
List.unmodifiable(_beforeSendEventObserver);

/// Adds an observer which is called right before an event is sent.
/// This should not be used to mutate the event.
///
/// Note: this is not triggered for transactions/spans with startTransaction or startChild.
@internal
void addBeforeSendEventObserver(BeforeSendEventObserver observer) {
_beforeSendEventObserver.add(observer);
}

@internal
void removeBeforeSendEventObserver(BeforeSendEventObserver observer) {
_beforeSendEventObserver.remove(observer);
}

/// Whether to group exceptions hierarchically.
///
/// If true, exceptions will be grouped hierarchically if possible.
Expand Down
77 changes: 0 additions & 77 deletions dart/test/sentry_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2640,49 +2640,6 @@ void main() {
await client.captureEvent(fakeEvent, stackTrace: StackTrace.current);
});
});

group('beforeSendEvent observer', () {
late Fixture fixture;

setUp(() {
fixture = Fixture();
});

test('is called with correct event and hint', () async {
final observer = _TestEventObserver();
fixture.options.addBeforeSendEventObserver(observer);

final client = fixture.getSut();
final hint = Hint();
final event = SentryEvent(type: 'some random type');

await client.captureEvent(event, hint: hint);

expect(observer.inspectedEvent?.type, 'some random type');
expect(observer.inspectedHint, same(hint));
});

test('is called after all other processors', () async {
final processingOrder = <String>[];
final beforeSend = (SentryEvent event, Hint hint) {
processingOrder.add('beforeSend');
return event;
};

fixture.options
.addBeforeSendEventObserver(_TestOrderObserver(processingOrder));

final client = fixture.getSut(
beforeSend: beforeSend,
eventProcessor: _TestEventProcessor(processingOrder));
final event = SentryEvent();

await client.captureEvent(event);

expect(
processingOrder, ['eventProcessor', 'beforeSend', 'beforeSendEvent']);
});
});
}

Future<SentryEvent> eventFromEnvelope(SentryEnvelope envelope) async {
Expand Down Expand Up @@ -2905,37 +2862,3 @@ class ExceptionWithStackTraceExtractor
return error.stackTrace;
}
}

class _TestEventObserver implements BeforeSendEventObserver {
SentryEvent? inspectedEvent;
Hint? inspectedHint;

@override
FutureOr<void> onBeforeSendEvent(SentryEvent event, Hint hint) {
inspectedEvent = event;
inspectedHint = hint;
}
}

class _TestEventProcessor extends EventProcessor {
_TestEventProcessor(this._processingOrder);

final List<String> _processingOrder;

@override
FutureOr<SentryEvent?> apply(SentryEvent event, Hint hint) {
_processingOrder.add('eventProcessor');
return event;
}
}

class _TestOrderObserver extends BeforeSendEventObserver {
final List<String> _processingOrder;

_TestOrderObserver(this._processingOrder);

@override
FutureOr<void> onBeforeSendEvent(SentryEvent event, Hint hint) {
_processingOrder.add('beforeSendEvent');
}
}
2 changes: 1 addition & 1 deletion flutter/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dependencies:

dev_dependencies:
flutter_lints: ^2.0.0
sentry_dart_plugin: ^3.0.0
sentry_dart_plugin: ^2.2.0
integration_test:
sdk: flutter
flutter_test:
Expand Down
Loading
Loading