Skip to content

Commit

Permalink
Feat: Use Hint for screenshots (#1165)
Browse files Browse the repository at this point in the history
  • Loading branch information
denrase authored Dec 12, 2022
1 parent 24b51c3 commit 026605e
Show file tree
Hide file tree
Showing 14 changed files with 77 additions and 135 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

### Features

- Feat: Introduce Hint data bag ([#1136](https://github.com/getsentry/sentry-dart/pull/1136))
- Feat: Introduce `Hint` data bag ([#1136](https://github.com/getsentry/sentry-dart/pull/1136))
- Feat: Use `Hint` for screenshots ([#1165](https://github.com/getsentry/sentry-dart/pull/1165))

### Fixes

Expand Down
3 changes: 0 additions & 3 deletions dart/lib/sentry_private.dart

This file was deleted.

10 changes: 10 additions & 0 deletions dart/lib/src/hint.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'sentry_attachment/sentry_attachment.dart';

/// Hints are used in [BeforeSendCallback], [BeforeBreadcrumbCallback] and
/// event processors.
///
Expand All @@ -21,6 +23,8 @@
class Hint {
final Map<String, Object> _internalStorage = {};

SentryAttachment? screenshot;

Hint();

factory Hint.withMap(Map<String, Object> map) {
Expand All @@ -29,6 +33,12 @@ class Hint {
return hint;
}

factory Hint.withScreenshot(SentryAttachment screenshot) {
final hint = Hint();
hint.screenshot = screenshot;
return hint;
}

// Objects

void addAll(Map<String, Object> keysAndValues) {
Expand Down
12 changes: 6 additions & 6 deletions dart/lib/src/sentry_client.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:math';
import 'package:meta/meta.dart';
import 'sentry_attachment/sentry_attachment.dart';

import 'event_processor.dart';
import 'hint.dart';
Expand All @@ -20,7 +21,6 @@ import 'sentry_envelope.dart';
import 'client_reports/client_report_recorder.dart';
import 'client_reports/discard_reason.dart';
import 'transport/data_category.dart';
import 'sentry_client_attachment_processor.dart';

/// Default value for [User.ipAddress]. It gets set when an event does not have
/// a user and IP address. Only applies if [SentryOptions.sendDefaultPii] is set
Expand All @@ -39,9 +39,6 @@ class SentryClient {

SentryStackTraceFactory get _stackTraceFactory => _options.stackTraceFactory;

SentryClientAttachmentProcessor get _clientAttachmentProcessor =>
_options.clientAttachmentProcessor;

/// Instantiates a client using [SentryOptions]
factory SentryClient(SentryOptions options) {
if (options.sendClientReports) {
Expand Down Expand Up @@ -137,8 +134,11 @@ class SentryClient {
preparedEvent = _eventWithRemovedBreadcrumbsIfHandled(preparedEvent);
}

final attachments = await _clientAttachmentProcessor.processAttachments(
scope?.attachments ?? [], preparedEvent);
var attachments = List<SentryAttachment>.from(scope?.attachments ?? []);
var screenshot = hint.screenshot;
if (screenshot != null) {
attachments.add(screenshot);
}

final envelope = SentryEnvelope.fromEvent(
preparedEvent,
Expand Down
14 changes: 0 additions & 14 deletions dart/lib/src/sentry_client_attachment_processor.dart

This file was deleted.

5 changes: 0 additions & 5 deletions dart/lib/src/sentry_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import 'package:meta/meta.dart';
import 'package:http/http.dart';

import '../sentry.dart';
import '../sentry_private.dart';
import 'client_reports/client_report_recorder.dart';
import 'client_reports/noop_client_report_recorder.dart';
import 'sentry_exception_factory.dart';
Expand Down Expand Up @@ -389,10 +388,6 @@ class SentryOptions {
late SentryStackTraceFactory stackTraceFactory =
SentryStackTraceFactory(this);

@internal
late SentryClientAttachmentProcessor clientAttachmentProcessor =
SentryClientAttachmentProcessor();

void _debugLogger(
SentryLevel level,
String message, {
Expand Down
21 changes: 0 additions & 21 deletions dart/test/mocks.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:async';

import 'package:sentry/sentry.dart';
import 'package:sentry/sentry_private.dart';
import 'package:sentry/src/transport/rate_limiter.dart';

final fakeDsn = 'https://[email protected]/1234567';
Expand Down Expand Up @@ -159,23 +158,3 @@ class MockRateLimiter implements RateLimiter {
this.errorCode = errorCode;
}
}

enum MockAttachmentProcessorMode { filter, add }

/// Filtering out all attachments.
class MockAttachmentProcessor implements SentryClientAttachmentProcessor {
MockAttachmentProcessorMode mode;

MockAttachmentProcessor(this.mode);

@override
Future<List<SentryAttachment>> processAttachments(
List<SentryAttachment> attachments, SentryEvent event) async {
switch (mode) {
case MockAttachmentProcessorMode.filter:
return <SentryAttachment>[];
case MockAttachmentProcessorMode.add:
return <SentryAttachment>[SentryAttachment.fromIntList([], "added")];
}
}
}
53 changes: 14 additions & 39 deletions dart/test/sentry_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1061,45 +1061,6 @@ void main() {
});
});

group('SentryClientAttachmentProcessor', () {
late Fixture fixture;

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

test('processor filtering out attachments', () async {
fixture.options.clientAttachmentProcessor =
MockAttachmentProcessor(MockAttachmentProcessorMode.filter);
final scope = Scope(fixture.options);
scope.addAttachment(SentryAttachment.fromIntList([], "scope-attachment"));
final sut = fixture.getSut();

final event = SentryEvent();
await sut.captureEvent(event, scope: scope);

final capturedEnvelope = (fixture.transport).envelopes.first;
final attachmentItem = capturedEnvelope.items.firstWhereOrNull(
(element) => element.header.type == SentryItemType.attachment);
expect(attachmentItem, null);
});

test('processor adding attachments', () async {
fixture.options.clientAttachmentProcessor =
MockAttachmentProcessor(MockAttachmentProcessorMode.add);
final scope = Scope(fixture.options);
final sut = fixture.getSut();

final event = SentryEvent();
await sut.captureEvent(event, scope: scope);

final capturedEnvelope = (fixture.transport).envelopes.first;
final attachmentItem = capturedEnvelope.items.firstWhereOrNull(
(element) => element.header.type == SentryItemType.attachment);
expect(attachmentItem != null, true);
});
});

group('ClientReportRecorder', () {
late Fixture fixture;

Expand Down Expand Up @@ -1171,6 +1132,20 @@ void main() {
expect(envelope.header.traceContext, isNotNull);
});

test('captureEvent adds screenshot from hint', () async {
final client = fixture.getSut();
final screenshot =
SentryAttachment.fromScreenshotData(Uint8List.fromList([0, 0, 0, 0]));
final hint = Hint.withScreenshot(screenshot);

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

final capturedEnvelope = (fixture.transport).envelopes.first;
final attachmentItem = capturedEnvelope.items.firstWhereOrNull(
(element) => element.header.type == SentryItemType.attachment);
expect(attachmentItem != null, true);
});

test('captureTransaction adds trace context', () async {
final client = fixture.getSut();

Expand Down
1 change: 1 addition & 0 deletions flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Future<void> setupSentry(AppRunner appRunner) async {
options.captureFailedHttpRequests = true;
options.maxRequestBodySize = MaxRequestBodySize.always;
options.maxResponseBodySize = MaxResponseBodySize.always;
options.captureFailedHttpRequests = true;
options.recordHttpBreadcrumbs = true;
},
// Init your App.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,42 @@ import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui show ImageByteFormat;

import 'package:flutter/rendering.dart';
import 'package:sentry/sentry.dart';
import 'package:sentry/sentry_private.dart';
import '../renderer/renderer.dart';
import '../screenshot/sentry_screenshot_widget.dart';
import '../sentry_flutter_options.dart';
import 'sentry_screenshot_widget.dart';
import 'package:flutter/rendering.dart';
import '../renderer/renderer.dart';

// ignore: invalid_use_of_internal_member
class ScreenshotAttachmentProcessor implements SentryClientAttachmentProcessor {
class ScreenshotEventProcessor extends EventProcessor {
final SentryFlutterOptions _options;

ScreenshotAttachmentProcessor(this._options);
ScreenshotEventProcessor(this._options);

/// This is true when the SentryWidget is in the view hierarchy
bool get _hasSentryScreenshotWidget =>
sentryScreenshotWidgetGlobalKey.currentContext != null;

@override
Future<List<SentryAttachment>> processAttachments(
List<SentryAttachment> attachments, SentryEvent event) async {
FutureOr<SentryEvent?> apply(SentryEvent event, {hint}) async {
if (event.exceptions == null &&
event.throwable == null &&
_hasSentryScreenshotWidget) {
return attachments;
return event;
}

final renderer = _options.rendererWrapper.getRenderer();
if (renderer != FlutterRenderer.skia &&
renderer != FlutterRenderer.canvasKit) {
_options.logger(SentryLevel.debug,
'Cannot take screenshot with ${_options.rendererWrapper.getRendererAsString()} renderer.');
return attachments;
return event;
}

final bytes = await _createScreenshot();
if (bytes != null) {
return attachments + [SentryAttachment.fromScreenshotData(bytes)];
} else {
return attachments;
hint?.screenshot = SentryAttachment.fromScreenshotData(bytes);
}
return event;
}

Future<Uint8List?> _createScreenshot() async {
Expand Down
20 changes: 11 additions & 9 deletions flutter/lib/src/integrations/screenshot_integration.dart
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import 'dart:async';

import 'package:sentry/sentry.dart';
import 'package:sentry/sentry_private.dart';
import '../screenshot/screenshot_attachment_processor.dart';
import '../event_processor/screenshot_event_processor.dart';
import '../sentry_flutter_options.dart';

/// Adds [ScreenshotAttachmentProcessor] to options if [attachScreenshot] is true
/// Adds [ScreenshotEventProcessor] to options event processors if [attachScreenshot] is true
class ScreenshotIntegration implements Integration<SentryFlutterOptions> {
SentryFlutterOptions? _options;
ScreenshotEventProcessor? screenshotEventProcessor;

@override
FutureOr<void> call(Hub hub, SentryFlutterOptions options) {
if (options.attachScreenshot) {
// ignore: invalid_use_of_internal_member
options.clientAttachmentProcessor =
ScreenshotAttachmentProcessor(options);
_options = options;

final screenshotEventProcessor = ScreenshotEventProcessor(options);
options.addEventProcessor(screenshotEventProcessor);
this.screenshotEventProcessor = screenshotEventProcessor;
options.sdk.addIntegration('screenshotIntegration');
}
}

@override
FutureOr<void> close() {
// ignore: invalid_use_of_internal_member
_options?.clientAttachmentProcessor = SentryClientAttachmentProcessor();
final screenshotEventProcessor = this.screenshotEventProcessor;
if (screenshotEventProcessor != null) {
_options?.removeEventProcessor(screenshotEventProcessor);
this.screenshotEventProcessor = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sentry_flutter/src/event_processor/screenshot_event_processor.dart';
import 'package:sentry_flutter/src/renderer/renderer.dart';
import 'package:sentry_flutter/src/screenshot/screenshot_attachment_processor.dart';
import '../mocks.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

Expand All @@ -26,9 +26,10 @@ void main() {

final throwable = Exception();
final event = SentryEvent(throwable: throwable);
final attachments = await sut.processAttachments([], event);
final hint = Hint();
await sut.apply(event, hint: hint);

expect(attachments.isNotEmpty, added);
expect(hint.screenshot != null, added);
});
}

Expand All @@ -55,8 +56,8 @@ void main() {
class Fixture {
SentryFlutterOptions options = SentryFlutterOptions(dsn: fakeDsn);

ScreenshotAttachmentProcessor getSut(FlutterRenderer flutterRenderer) {
ScreenshotEventProcessor getSut(FlutterRenderer flutterRenderer) {
options.rendererWrapper = MockRendererWrapper(flutterRenderer);
return ScreenshotAttachmentProcessor(options);
return ScreenshotEventProcessor(options);
}
}
Loading

0 comments on commit 026605e

Please sign in to comment.