Skip to content

Commit e7cb678

Browse files
committed
docs: add Bugsee implementation documentation
1 parent 9906020 commit e7cb678

7 files changed

Lines changed: 187 additions & 54 deletions

File tree

doc/Bugsee.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Bugsee
2+
3+
This project include [Bugsee](https://www.bugsee.com/) reporting and Logging, for both Android and iOS apps.
4+
Bugsee lets you monitor and get instant log of unhandled exceptions with traces, events, stacktrace and videos/screenshots of the reported exception. More features are provided by Bugsee like data obscuration and log filter.
5+
6+
For this implementation we've used [bugsee_flutter](https://pub.dev/packages/bugsee_flutter) package.
7+
8+
- By default only release apps will have Bugsee reporting enabled, to enable Bugsee in debug mode add your token in `launch.json` add remove the check on debug mode in `BugseeManager` class.
9+
- **Token**
10+
- Generate your token in order to test Bugsee logging and reporting
11+
- Head to [Bugsee dashboard](https://www.bugsee.com/)
12+
- Create a new account
13+
- Create a new Android/iOS (choose Flutter framework)
14+
- Copy-paste the generated token into `launch.json`
15+
16+
17+
## 1) Features
18+
19+
In this project we've implemented the following features of Bugsee:
20+
- [Manual invocation](https://docs.bugsee.com/sdk/flutter/manual/) helpfull for developers to test their Bugsee integration and setup with new tokens.
21+
- [Custom data](https://docs.bugsee.com/sdk/flutter/custom/) custom data could be attached to the logged exceptions (emails or other type of data)
22+
- [Email](https://docs.bugsee.com/sdk/flutter/custom/#:~:text=Adding%20custom%20data-,User%20email,-When%20you%20already) this will identify the user whom experienced the exception/bug this will update the exception dashboard item from anonymous to the user's mail this data will be reported with every logged exception unless the app is deleted or removed manually.
23+
- [Attributes](https://docs.bugsee.com/sdk/flutter/custom/#:~:text=User/Session%20attributes) attributes related to the user info, will be reported with every logged exception unless the app is deleted or removed manually.
24+
- [Traces](https://docs.bugsee.com/sdk/flutter/custom/#:~:text=them%0ABugsee.clearAllAttributes()%3B-,Custom%20traces,-Traces%20may%20be) helpfull when developer want to track the change of specific value before the logging of the exception.
25+
- [Events](https://docs.bugsee.com/sdk/flutter/custom/#:~:text=Custom%20events-,Events,-are%20identified%20by) highlight on which event the exception is logged, accept also json data attached to it.
26+
- [Exception Logging](https://docs.bugsee.com/sdk/flutter/logs/) the app automatically log every unhandled exception: Dart SDK exception are related to data or logic errors and Flutter SDK errors that are related to layout and rendering issues. The implementation also offer an API to manually log an exception with traces and events.
27+
- [Video Capturing](https://docs.bugsee.com/sdk/flutter/privacy/video/) video capturing is by default enabled in this project, but it can be turned off using the `videoEnabled` flag in the launchOptions object for Android and iOS.
28+
- [Log reporting](https://docs.bugsee.com/sdk/flutter/privacy/logs/) all logs are filtered by default using the `_filterBugseeLogs` method, this can be turned off from the app or by removing the call to `setLogFilter` Bugsee method.
29+
- [Obscure Data](https://docs.bugsee.com/sdk/flutter/privacy/video/#:~:text=Protecting%20flutter%20views): data obscuration is by default enabled in the project in order to protect user-related data from being leaked through captured videos.
30+
31+
**Default configurations:**
32+
Data obscuration, log collection, log filter and attaching log file features are initialized from the `.env.staging` file.
33+
```
34+
BUGSEE_IS_DATA_OBSCURE=true
35+
BUGSEE_DISABLE_LOG_COLLECTION=true
36+
BUGSEE_FILTER_LOG_COLLECTION=false
37+
BUGSEE_ATTACH_LOG_FILE=true
38+
```
39+
40+
## 2) Implementation
41+
- [Bugsee Manager](../src/app/lib/business/bugsee/bugsee_manager.dart): a service class that handle the Bugsee intialization, capturing logs, customize Bugsee fields and features (Video capture, data obscure, logs filter...) .
42+
- [Bugsee Config State](../src/app/lib/business/bugsee/bugsee_config_state.dart): a state class holds all the actual Bugsee features status (whether enabled or not).
43+
- [Bugsee Repository](../src/app/lib/access/bugsee/bugsee_repository.dart): save and retrieve Bugsee configuration from the shared preference storage.
44+
- [Bugsee saved configuration](../src/app/lib/access/bugsee/bugsee_configuration_data.dart): holds the Bugsee saved configuration in shared preference, used in [Bugsee Manager](../src/app/lib/business/bugsee/bugsee_manager.dart) to initialize [Bugsee Config State](../src/app/lib/business/bugsee/bugsee_config_state.dart).
45+
46+
### Intecepting exceptions
47+
Bugsee implementation in this project, by default intecepts all the unhandled Dart and Flutter exception.
48+
49+
`main.dart`
50+
```dart
51+
runZonedGuarded(
52+
() async {
53+
FlutterError.onError =
54+
GetIt.I.get<BugseeManager>().inteceptRenderExceptions;
55+
await initializeComponents();
56+
await registerBugseeManager();
57+
runApp(const App());
58+
},
59+
GetIt.I.get<BugseeManager>().inteceptExceptions,
60+
);
61+
```
62+
the `inteceptExceptions` and `inteceptRenderExceptions` recerespectively report Dart and Flutter exceptions to the Bugsee Dashboard.
63+
64+
`inteceptExceptions`
65+
```dart
66+
@override
67+
Future<void> inteceptExceptions(
68+
Object error,
69+
StackTrace stackTrace,
70+
) async {
71+
String? message = switch (error.runtimeType) {
72+
const (PersistenceException) => (error as PersistenceException).message,
73+
_ => null,
74+
};
75+
await logException(
76+
exception: Exception(error),
77+
stackTrace: stackTrace,
78+
traces: {
79+
'message': message,
80+
},
81+
);
82+
}
83+
```
84+
85+
`inteceptRenderExceptions`
86+
```dart
87+
@override
88+
Future<void> inteceptRenderExceptions(FlutterErrorDetails error) async {
89+
await logException(
90+
exception: Exception(error.exception),
91+
stackTrace: error.stack,
92+
);
93+
}
94+
```
95+
96+
### Manually invoke report dialog
97+
98+
To manually display the report dialog, you call the `showCaptureLogReport` method from the `BugseeManager` class.
99+
100+
```dart
101+
final bugseeManager = Get.I.get<BugseeManager>();
102+
bugseeManger.showCaptureLogReport();
103+
```
104+
105+
106+
### Manually log an exception
107+
108+
To manually log an exception, you call the `logException` method from the `BugseeManager` class. You can add traces and events to the reported exception.
109+
110+
```dart
111+
final bugseeManager = Get.I.get<BugseeManager>();
112+
bugseeManger.logException(exception: Exception());
113+
```
114+
115+
116+
### Add attributes
117+
118+
To attach the user's email to the logged exception use `addEmailAttribute` method and to clear this attribute you can use `clearEmailAttribute`.
119+
120+
```dart
121+
final bugseeManager = Get.I.get<BugseeManager>();
122+
bugseeManger.addEmailAttribute("johndoe@nventive.com");
123+
//some other code..
124+
bugseeManger.clearEmailAttribute();
125+
```
126+
127+
for other attributes you can use `addAttributes` with a map where key is the attribute name and value is the attribute value. To clear these attributes use `clearAttribute` and pass the attribute name to it.
128+
129+
```dart
130+
final bugseeManager = Get.I.get<BugseeManager>();
131+
bugseeManger.addAttributes({
132+
'name': 'john',
133+
'age': 45,
134+
});
135+
//some other code..
136+
bugseeManger.clearAttribute('name');
137+
```
138+
139+
## 3) Deployment
140+
141+
The Bugsee token is injected directly in the azure devops pipeline when building the Android/iOS app for release mode in the [Android Build Job](../build/steps-build-android.yml) and [iOS Build Job](../build/steps-build-ios.yml) under the `multiDartDefine` parameter.

src/app/.env.dev

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,4 @@ MINIMUM_LEVEL='debug'
44
DAD_JOKES_BASE_URL='https://www.reddit.com/r/dadjokes'
55
APP_STORE_URL_IOS=https://apps.apple.com/us/app/uno-calculator/id1464736591
66
APP_STORE_URL_Android=https://play.google.com/store/apps/details?id=uno.platform.calculator
7-
REMOTE_CONFIG_FETCH_INTERVAL_MINUTES=1
8-
DIAGNOSTIC_ENABLED=true
9-
IS_DATA_OBSCURE=true
7+
REMOTE_CONFIG_FETCH_INTERVAL_MINUTES=1

src/app/.env.prod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,4 @@ MINIMUM_LEVEL='warning'
44
DAD_JOKES_BASE_URL='https://www.reddit.com/r/dadjokes'
55
APP_STORE_URL_IOS=https://apps.apple.com/us/app/uno-calculator/id1464736591
66
APP_STORE_URL_Android=https://play.google.com/store/apps/details?id=uno.platform.calculator
7-
REMOTE_CONFIG_FETCH_INTERVAL_MINUTES=720
8-
DIAGNOSTIC_ENABLED=false
9-
IS_DATA_OBSCURE=true
7+
REMOTE_CONFIG_FETCH_INTERVAL_MINUTES=720

src/app/.env.staging

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ APP_STORE_URL_IOS=https://apps.apple.com/us/app/uno-calculator/id1464736591
66
APP_STORE_URL_Android=https://play.google.com/store/apps/details?id=uno.platform.calculator
77
REMOTE_CONFIG_FETCH_INTERVAL_MINUTES=1
88
DIAGNOSTIC_ENABLED=true
9-
IS_DATA_OBSCURE=true
10-
DISABLE_LOG_COLLECTION=true
11-
FILTER_LOG_COLLECTION=false
12-
ATTACH_LOG_FILE=true
9+
BUGSEE_IS_DATA_OBSCURE=true
10+
BUGSEE_DISABLE_LOG_COLLECTION=true
11+
BUGSEE_FILTER_LOG_COLLECTION=false
12+
BUGSEE_ATTACH_LOG_FILE=true

src/app/lib/access/bugsee/bugsee_configuration_data.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ final class BugseeConfigurationData extends Equatable {
44
/// Gets whether the Bugsee SDK is enabled or not. if [Null] it fallbacks to a new installed app so it will be enabled.
55
final bool? isBugseeEnabled;
66

7-
/// Indicate whether the video capturing feature in Bugsee is enabled or not.
7+
/// Indicates whether the video capturing feature in Bugsee is enabled or not.
88
final bool? isVideoCaptureEnabled;
99

10-
/// Indicate whether bugsee obscure application data in videos and images or not.
10+
/// Indicates whether Bugsee obscures application data in videos and images or not.
1111
final bool? isDataObscured;
1212

13-
/// Indicate whether logs are collected or not.
13+
/// Indicates whether logs are collected or not.
1414
final bool? isLogCollectionEnabled;
1515

16-
/// Indicate whether logs are filtred during reports or not.
16+
/// Indicates whether logs are filtred during reports or not.
1717
final bool? isLogsFilterEnabled;
1818

19-
/// Indicate whether attaching file in the Bugsee report is enabled or not
19+
/// Indicates whether attaching file in the Bugsee report is enabled or not
2020
final bool? attachLogFileEnabled;
2121

2222
const BugseeConfigurationData({

src/app/lib/access/bugsee/bugsee_repository.dart

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ abstract interface class BugseeRepository {
1414
/// Update the current video captured or not flag in shared prefs.
1515
Future setIsVideoCaptureEnabled(bool isVideoCaptureEnabled);
1616

17-
/// Update whether data is obscure in shared prefs.
17+
/// Update whether data is obscured in shared prefs.
1818
Future setIsDataObscure(bool isDataObscure);
1919

2020
/// Update the logCollection flag in shared prefs.
@@ -29,23 +29,24 @@ abstract interface class BugseeRepository {
2929

3030
final class _BugseeRepository implements BugseeRepository {
3131
final String _bugseeEnabledKey = 'bugseeEnabledKey';
32-
final String _videoCaptureKey = 'videoCaptureKey';
33-
final String _dataObscureKey = 'dataObscureKey';
34-
final String _disableLogCollectionKey = 'disableLogCollectionKey';
35-
final String _disableLogFilterKey = 'disableLogFilterKey';
36-
final String _attachLogFileKey = 'attachLogFileKey';
32+
final String _bugseeVideoCaptureKey = 'bugseeVideoCaptureKey';
33+
final String _bugseeDataObscureKey = 'bugseeDataObscureKey';
34+
final String _bugseeDisableLogCollectionKey = 'bugseeDisableLogCollectionKey';
35+
final String _bugseeDisableLogFilterKey = 'bugseeDisableLogFilterKey';
36+
final String _bugseeAttachLogFileKey = 'bugseeAttachLogFileKey';
3737

3838
@override
3939
Future<BugseeConfigurationData> getBugseeConfiguration() async {
4040
final sharedPrefInstance = await SharedPreferences.getInstance();
4141
return BugseeConfigurationData(
4242
isBugseeEnabled: sharedPrefInstance.getBool(_bugseeEnabledKey),
43-
isVideoCaptureEnabled: sharedPrefInstance.getBool(_videoCaptureKey),
44-
isDataObscured: sharedPrefInstance.getBool(_dataObscureKey),
43+
isVideoCaptureEnabled: sharedPrefInstance.getBool(_bugseeVideoCaptureKey),
44+
isDataObscured: sharedPrefInstance.getBool(_bugseeDataObscureKey),
4545
isLogCollectionEnabled:
46-
sharedPrefInstance.getBool(_disableLogCollectionKey),
47-
isLogsFilterEnabled: sharedPrefInstance.getBool(_disableLogFilterKey),
48-
attachLogFileEnabled: sharedPrefInstance.getBool(_attachLogFileKey),
46+
sharedPrefInstance.getBool(_bugseeDisableLogCollectionKey),
47+
isLogsFilterEnabled:
48+
sharedPrefInstance.getBool(_bugseeDisableLogFilterKey),
49+
attachLogFileEnabled: sharedPrefInstance.getBool(_bugseeAttachLogFileKey),
4950
);
5051
}
5152

@@ -70,13 +71,14 @@ final class _BugseeRepository implements BugseeRepository {
7071
final sharedPrefInstance = await SharedPreferences.getInstance();
7172

7273
bool isSaved = await sharedPrefInstance.setBool(
73-
_videoCaptureKey,
74+
_bugseeVideoCaptureKey,
7475
isVideoCaptureEnabled,
7576
);
7677

7778
if (!isSaved) {
7879
throw PersistenceException(
79-
message: 'Error while setting $_videoCaptureKey $isVideoCaptureEnabled',
80+
message:
81+
'Error while setting $_bugseeVideoCaptureKey $isVideoCaptureEnabled',
8082
);
8183
}
8284
}
@@ -86,13 +88,13 @@ final class _BugseeRepository implements BugseeRepository {
8688
final sharedPrefInstance = await SharedPreferences.getInstance();
8789

8890
bool isSaved = await sharedPrefInstance.setBool(
89-
_dataObscureKey,
91+
_bugseeDataObscureKey,
9092
isDataObscured,
9193
);
9294

9395
if (!isSaved) {
9496
throw PersistenceException(
95-
message: 'Error while setting $_dataObscureKey $isDataObscured',
97+
message: 'Error while setting $_bugseeDataObscureKey $isDataObscured',
9698
);
9799
}
98100
}
@@ -102,14 +104,14 @@ final class _BugseeRepository implements BugseeRepository {
102104
final sharedPrefInstance = await SharedPreferences.getInstance();
103105

104106
bool isSaved = await sharedPrefInstance.setBool(
105-
_disableLogCollectionKey,
107+
_bugseeDisableLogCollectionKey,
106108
isLogCollected,
107109
);
108110

109111
if (!isSaved) {
110112
throw PersistenceException(
111113
message:
112-
'Error while setting $_disableLogCollectionKey $isLogCollected',
114+
'Error while setting $_bugseeDisableLogCollectionKey $isLogCollected',
113115
);
114116
}
115117
}
@@ -119,14 +121,14 @@ final class _BugseeRepository implements BugseeRepository {
119121
final sharedPrefInstance = await SharedPreferences.getInstance();
120122

121123
bool isSaved = await sharedPrefInstance.setBool(
122-
_disableLogFilterKey,
124+
_bugseeDisableLogFilterKey,
123125
isLogFilterEnabled,
124126
);
125127

126128
if (!isSaved) {
127129
throw PersistenceException(
128130
message:
129-
'Error while setting $_disableLogFilterKey $isLogFilterEnabled',
131+
'Error while setting $_bugseeDisableLogFilterKey $isLogFilterEnabled',
130132
);
131133
}
132134
}
@@ -136,13 +138,13 @@ final class _BugseeRepository implements BugseeRepository {
136138
final sharedPrefInstance = await SharedPreferences.getInstance();
137139

138140
bool isSaved = await sharedPrefInstance.setBool(
139-
_attachLogFileKey,
141+
_bugseeAttachLogFileKey,
140142
attachLogFile,
141143
);
142144

143145
if (!isSaved) {
144146
throw PersistenceException(
145-
message: 'Error while setting $_attachLogFileKey $attachLogFile',
147+
message: 'Error while setting $_bugseeAttachLogFileKey $attachLogFile',
146148
);
147149
}
148150
}

src/app/lib/business/bugsee/bugsee_manager.dart

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ abstract interface class BugseeManager {
4444
Future<void> logException({
4545
required Exception exception,
4646
StackTrace? stackTrace,
47-
Map<String, dynamic>? traces,
48-
Map<String, Map<String, dynamic>?>? events,
47+
Map<String, dynamic> traces,
48+
Map<String, Map<String, dynamic>?> events,
4949
});
5050

5151
/// Manually update the current BugseeEnabled flag in shared prefs and in current manager singleton.
@@ -141,16 +141,16 @@ final class _BugseeManager implements BugseeManager {
141141
configurationData = configurationData.copyWith(
142142
isLogCollectionEnabled: configurationData.isLogCollectionEnabled ??
143143
bool.parse(
144-
dotenv.env['DISABLE_LOG_COLLECTION'] ?? 'true',
144+
dotenv.env['BUGSEE_DISABLE_LOG_COLLECTION'] ?? 'true',
145145
),
146146
isLogsFilterEnabled: configurationData.isLogsFilterEnabled ??
147147
bool.parse(
148-
dotenv.env['FILTER_LOG_COLLECTION'] ?? 'true',
148+
dotenv.env['BUGSEE_FILTER_LOG_COLLECTION'] ?? 'true',
149149
),
150150
isDataObscured: configurationData.isDataObscured ??
151-
bool.parse(dotenv.env['IS_DATA_OBSCURE'] ?? 'true'),
151+
bool.parse(dotenv.env['BUGSEE_IS_DATA_OBSCURE'] ?? 'true'),
152152
attachLogFileEnabled: configurationData.attachLogFileEnabled ??
153-
bool.parse(dotenv.env['ATTACH_LOG_FILE'] ?? 'true'),
153+
bool.parse(dotenv.env['BUGSEE_ATTACH_LOG_FILE'] ?? 'true'),
154154
);
155155

156156
launchOptions = _initializeLaunchOptions();
@@ -259,22 +259,16 @@ final class _BugseeManager implements BugseeManager {
259259
Future<void> logException({
260260
required Exception exception,
261261
StackTrace? stackTrace,
262-
Map<String, dynamic>? traces,
263-
Map<String, Map<String, dynamic>?>? events,
262+
Map<String, dynamic> traces = const {},
263+
Map<String, Map<String, dynamic>?> events = const {},
264264
}) async {
265265
if (_currentState.isBugseeEnabled) {
266-
if (traces != null) {
267-
for (var trace in traces.entries) {
268-
await Bugsee.trace(trace.key, trace.value);
269-
}
266+
for (var trace in traces.entries) {
267+
await Bugsee.trace(trace.key, trace.value);
270268
}
271-
272-
if (events != null) {
273-
for (var event in events.entries) {
274-
await Bugsee.event(event.key, event.value);
275-
}
269+
for (var event in events.entries) {
270+
await Bugsee.event(event.key, event.value);
276271
}
277-
278272
await Bugsee.logException(exception, stackTrace);
279273
}
280274
}

0 commit comments

Comments
 (0)