Skip to content
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

Bump bugsnag-android to v6. Changed redactedKeys and discardClasses types to RegExp #249

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ class DiscardClassesScenario extends Scenario {

await bugsnag.start(
endpoints: endpoints,
discardClasses: const {
'_Exception',
}
discardClasses: {
RegExp('_Exception'),
},
);
try {
throw Exception('this should be discarded');
Expand Down
54 changes: 27 additions & 27 deletions features/fixtures/app/lib/scenarios/start_bugsnag_scenario.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,33 @@ class StartBugsnagScenario extends Scenario {
@override
Future<void> run() async {
await bugsnag.start(
apiKey: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
appType: 'test',
appVersion: '1.2.3',
versionCode: 4321,
context: 'awesome',
user: BugsnagUser(
id: '123',
name: 'From Config',
),
redactedKeys: {'secret'},
releaseStage: 'testing',
enabledReleaseStages: {'testing'},
enabledBreadcrumbTypes: {BugsnagEnabledBreadcrumbType.error},
endpoints: endpoints,
featureFlags: const [
BugsnagFeatureFlag('demo-mode'),
BugsnagFeatureFlag('sample-group', '123'),
],
metadata: const {
'custom': {
'foo': 'bar',
'secret': 'should be hidden',
'password': 'not redacted'
}
},
sendThreads: BugsnagThreadSendPolicy.never
);
apiKey: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
appType: 'test',
appVersion: '1.2.3',
versionCode: 4321,
context: 'awesome',
user: BugsnagUser(
id: '123',
name: 'From Config',
),
redactedKeys: {RegExp('secret')},
releaseStage: 'testing',
enabledReleaseStages: {'testing'},
enabledBreadcrumbTypes: {BugsnagEnabledBreadcrumbType.error},
endpoints: endpoints,
featureFlags: const [
BugsnagFeatureFlag('demo-mode'),
BugsnagFeatureFlag('sample-group', '123'),
],
metadata: const {
'custom': {
'foo': 'bar',
'secret': 'should be hidden',
'password': 'not redacted'
}
},
sendThreads: BugsnagThreadSendPolicy.never,
);
await bugsnag.notify(
Exception('Exception with attached info'),
StackTrace.current,
Expand Down
2 changes: 1 addition & 1 deletion packages/bugsnag_flutter/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ android {
}

dependencies {
implementation 'com.bugsnag:bugsnag-android:5.31.3'
implementation 'com.bugsnag:bugsnag-android:6.6.0'
testImplementation 'junit:junit:4.12'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

class BugsnagFlutter {

Expand Down Expand Up @@ -120,11 +121,11 @@ Void start(@Nullable JSONObject args) throws Exception {
configuration.setPersistUser(arguments.optBoolean("persistUser", configuration.getPersistUser()));

if (arguments.has("redactedKeys")) {
configuration.setRedactedKeys(unwrap(arguments.optJSONArray("redactedKeys"), new HashSet<>()));
configuration.setRedactedKeys(regexSetFromArray(arguments.optJSONArray("redactedKeys")));
}

if (arguments.has("discardClasses")) {
configuration.setDiscardClasses(unwrap(arguments.optJSONArray("discardClasses"), new HashSet<>()));
configuration.setDiscardClasses(regexSetFromArray(arguments.optJSONArray("discardClasses")));
}

if (arguments.has("enabledReleaseStages")) {
Expand Down Expand Up @@ -344,6 +345,29 @@ Void markLaunchCompleted(@Nullable Void args) {
return null;
}

Set<Pattern> regexSetFromArray(JSONArray array) throws JSONException {
HashSet<Pattern> result = new HashSet<>(array.length());
for (int i = 0; i < array.length(); i++) {
JSONObject element = array.getJSONObject(i);
String pattern = getString(element, "pattern");
int flags = 0;
if (element.optBoolean("isDotAll")) {
flags |= Pattern.DOTALL;
}
if (!element.optBoolean("isCaseSensitive")) {
flags |= Pattern.CASE_INSENSITIVE;
}
if (element.optBoolean("isMultiLine")) {
flags |= Pattern.MULTILINE;
}

if (pattern != null) {
result.add(Pattern.compile(pattern, flags));
}
}
return result;
}

JSONObject getLastRunInfo(@Nullable Void args) throws JSONException {
LastRunInfo lastRunInfo = Bugsnag.getLastRunInfo();
return (lastRunInfo == null) ? null : new JSONObject()
Expand Down
29 changes: 27 additions & 2 deletions packages/bugsnag_flutter/ios/Classes/BugsnagFlutterPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,31 @@ static void DyldImageAdded(const struct mach_header *mh, intptr_t slide) {
return [value isKindOfClass:[NSString class]] ? value : nil;
}

static NSArray *jsonToRegularExpressions(NSArray *source) {
NSMutableArray *result = [NSMutableArray new];
for (NSDictionary *element in source) {
NSString *pattern = element[@"pattern"];
NSInteger options = 0;
if ([element[@"isDotAll"] boolValue]) {
options |= NSRegularExpressionDotMatchesLineSeparators;
}
if (![element[@"isCaseSensitive"] boolValue]) {
options |= NSRegularExpressionCaseInsensitive;
}
if ([element[@"isMultiLine"] boolValue]) {
options |= NSRegularExpressionAnchorsMatchLines;
}
NSError *error = nil;
NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:pattern options:options error:&error];
if (expression != nil) {
[result addObject: expression];
} else if (error) {
NSLog(@"Error encountered while parsing regular expression %@", error);
}
}
return result;
}

@interface BugsnagEvent (BugsnagFlutterPlugin)

@property (nullable, nonatomic) NSArray *projectPackages;
Expand Down Expand Up @@ -278,12 +303,12 @@ - (void)start:(NSDictionary *)arguments {

NSArray *redactedKeys = arguments[@"redactedKeys"];
if ([redactedKeys isKindOfClass:[NSArray class]]) {
configuration.redactedKeys = [NSSet setWithArray:redactedKeys];
configuration.redactedKeys = [NSSet setWithArray: jsonToRegularExpressions(redactedKeys)];
}

NSArray *discardClasses = arguments[@"discardClasses"];
if ([discardClasses isKindOfClass:[NSArray class]]) {
configuration.discardClasses = [NSSet setWithArray:discardClasses];
configuration.discardClasses = [NSSet setWithArray: jsonToRegularExpressions(discardClasses)];
}

NSArray *enabledReleaseStages = arguments[@"enabledReleaseStages"];
Expand Down
77 changes: 40 additions & 37 deletions packages/bugsnag_flutter/lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'config.dart';
import 'enum_utils.dart';
import 'last_run_info.dart';
import 'model.dart';
import 'regexp_json.dart';

final _notifier = {
'name': 'Flutter Bugsnag Notifier',
Expand All @@ -21,7 +22,6 @@ final _notifier = {
};

abstract class BugsnagClient {

final Map<String, Stopwatch> _openNetworkRequests = {};

/// An utility error handling function that will send reported errors to
Expand Down Expand Up @@ -256,45 +256,47 @@ abstract class BugsnagClient {
}

void _onRequestStarted(String requestId) {
final stopwatch = Stopwatch()..start();
_openNetworkRequests[requestId] = stopwatch;
final stopwatch = Stopwatch()..start();
_openNetworkRequests[requestId] = stopwatch;
}

void _onRequestComplete(String requestId, dynamic data) {
final stopwatch = _openNetworkRequests.remove(requestId);
if (stopwatch != null && data is Map<String, dynamic>) {
final duration = stopwatch.elapsedMilliseconds;
final String? clientName = data["client"];
if (clientName == null) return;

String params = "";
final url = data["url"];
final splitUrl = url.split("?");
if (splitUrl != null && splitUrl.length > 1) {
params = splitUrl.last;
}
final int? statusCode = data["status_code"];
if (statusCode == null) return;

final String status = statusCode < 400 ? "succeeded" : "failed";
// Assuming leaveBreadcrumb is a predefined method to log the event
leaveBreadcrumb("$clientName request $status", metadata: {
final stopwatch = _openNetworkRequests.remove(requestId);
if (stopwatch != null && data is Map<String, dynamic>) {
final duration = stopwatch.elapsedMilliseconds;
final String? clientName = data["client"];
if (clientName == null) return;

String params = "";
final url = data["url"];
final splitUrl = url.split("?");
if (splitUrl != null && splitUrl.length > 1) {
params = splitUrl.last;
}
final int? statusCode = data["status_code"];
if (statusCode == null) return;

final String status = statusCode < 400 ? "succeeded" : "failed";
// Assuming leaveBreadcrumb is a predefined method to log the event
leaveBreadcrumb(
"$clientName request $status",
metadata: {
"duration": duration,
"method": data["http_method"],
"url": splitUrl.first,
if(params.isNotEmpty)
"urlParams": params,
if(data["request_content_length"] != null && data["request_content_length"] > 0)
"requestContentLength": data["request_content_length"],
if(data["response_content_length"] != null && data["response_content_length"] > 0)
"responseContentLength": data["response_content_length"],
if (params.isNotEmpty) "urlParams": params,
if (data["request_content_length"] != null &&
data["request_content_length"] > 0)
"requestContentLength": data["request_content_length"],
if (data["response_content_length"] != null &&
data["response_content_length"] > 0)
"responseContentLength": data["response_content_length"],
"status": statusCode,
},
type: BugsnagBreadcrumbType.request,
);
}
},
type: BugsnagBreadcrumbType.request,
);
}
}

}

mixin DelegateClient implements BugsnagClient {
Expand Down Expand Up @@ -742,8 +744,8 @@ class Bugsnag extends BugsnagClient with DelegateClient {
int launchDurationMillis = 5000,
bool sendLaunchCrashesSynchronously = true,
int appHangThresholdMillis = appHangThresholdFatalOnly,
Set<String> redactedKeys = const {'password'},
Set<String> discardClasses = const {},
Set<RegExp>? redactedKeys,
Set<RegExp> discardClasses = const {},
Set<String>? enabledReleaseStages,
Set<BugsnagEnabledBreadcrumbType>? enabledBreadcrumbTypes,
BugsnagProjectPackages projectPackages =
Expand Down Expand Up @@ -786,8 +788,10 @@ class Bugsnag extends BugsnagClient with DelegateClient {
'launchDurationMillis': launchDurationMillis,
'sendLaunchCrashesSynchronously': sendLaunchCrashesSynchronously,
'appHangThresholdMillis': appHangThresholdMillis,
'redactedKeys': List<String>.from(redactedKeys),
'discardClasses': List<String>.from(discardClasses),
'redactedKeys': List<dynamic>.from(redactedKeys?.map((e) => e.toJson()) ??
{RegExp('password').toJson()}),
'discardClasses':
List<dynamic>.from(discardClasses.map((e) => e.toJson())),
if (enabledReleaseStages != null)
'enabledReleaseStages': enabledReleaseStages.toList(),
'enabledBreadcrumbTypes':
Expand Down Expand Up @@ -833,7 +837,6 @@ class Bugsnag extends BugsnagClient with DelegateClient {

return const <String>{};
}

}

/// In order to determine where a crash happens Bugsnag needs to know which
Expand Down
8 changes: 8 additions & 0 deletions packages/bugsnag_flutter/lib/src/regexp_json.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
extension RegExpJSON on RegExp {
dynamic toJson() => <String, dynamic>{
'pattern': pattern,
'isDotAll': isDotAll,
'isCaseSensitive': isCaseSensitive,
'isMultiLine': isMultiLine,
};
}