Repro Steps and Auto-masking Screenshots
How to automatically mask the screens reported in Repro Steps
Repro Steps is your go-to tool for understanding what led to a bug or crash in your app. This guide will help you make the most of Repro Steps.
What are "Repro Steps"?
Repro Steps capture and present all the actions a user takes in your app before encountering a bug or crash. These steps are grouped by the app view, and you can view user steps alongside relevant screenshots. It's like having a play-by-play visual and written record of the user's actions leading to the problem.
Where can you find “Repro Steps”?
To find the Repro Steps, simply scroll down below the bug details. If you are on the crash reporting tool, you will find them in the occurrences page of a crash below the crash details. Check the screenshot below to find them
Understanding the Setup
Repro Steps are organized by the different screens in your app that users interact with. Each screen view visited by the user contains a set of chronological steps, giving you a clear picture of what led to the reported issue.
Understanding Logged Data and Captured Events
Logged data captures user interactions, such as swipes, scrolls, taps, and more. It provides insights into how users engage with your app. The captured events track app states, including background/foreground transitions, activity changes, and memory warnings. To know more about the logged data, you can find the details here.
How can you enable Repro steps
Repro Steps is by default enabled with screenshots for bug reporting, and enabled without screenshots for crash reporting. So do you need to change anything here? If yes, you can use the following properties:
Instabug.setReproStepsFor(.all, with: .enable)
[Instabug setReproStepsFor:IBGIssueTypeAll withMode:IBGUserStepsModeEnable];
You can change the value of the Issue Type.
//Bug Reporting and Crash Reporting
.all
//Bug Reporting Only
.bug
//Crash Reporting Only
.crash
//Bug Reporting and Crash Reporting
IBGIssueTypeAll
//Bug Reporting Only
IBGIssueTypeBug
//Crash Reporting Only
IBGIssueTypeCrash
You can change the value of the User Steps Mode.
//Enable with Screenshots
.enable
//Enable with NO Screenshots
.enabledWithNoScreenshots
//Disable
.disable
//Enable with Screenshots
IBGUserStepsModeEnable
//Enable with NO Screenshots
IBGUserStepsModeEnabledWithNoScreenshots
//Disable
IBGUserStepsModeDisable
Examples
Enable with Screenshots for both Bug Reporting and Crash Reporting:
Instabug.setReproStepsFor(.all, with: .enable)
[Instabug setReproStepsFor:IBGIssueTypeAll withMode:IBGUserStepsModeEnable];
Enable with No Screenshots for Crash Reporting.
Instabug.setReproStepsFor(.crash, with: .enabledWithNoScreenshots)
[Instabug setReproStepsFor:IBGIssueTypeCrash withMode:IBGUserStepsModeEnabledWithNoScreenshots];
Completely disable for both Bug Reporting and Crash Reporting.
Instabug.setReproStepsFor(.all, with: .disable)
[Instabug setReproStepsFor:IBGIssueTypeAll withMode:IBGUserStepsModeDisable];
User Privacy
We understand user privacy is of key to you, specially having our SDK on production. In this guide, we will show you how to mark views containing sensitive information as private. By doing so, these views will be masked on screenshots captured to your users' devices (before being sent to Instabug servers) and automatically appear with a black overlay (over whole screen, labels, texts or media) in any screenshot on Instabug's dashboard to protect your user’s sensitive data.
Repro steps screenshots are disabled by default on crash reporting.
-
Identify the View: Determine which view in your app contains sensitive information that should be kept private. For example, a payment details screen or a user's personal profile.
-
Identify Sensitive Information: Determine which information on each view that you need to hide. Do you want to hide the whole screen? text fields? media? labels?
-
Deciding on the content of the screenshots
Instabug provides you with multiple ways to easily protect your users sensitive data, whether you want to hide the content of a whole specific screen or certain text fields, labels and media, we got you covered!-
Auto masking
You can automatically mask sensitive data when screenshots are captured, while protecting the user's privacy by default using auto masking feature. Keep in mind that this feature affects both Bug and Crash Reporting.
-
Masking text inputs
You can use this to hide any text fields captured on all your screenshotsInstabug.setAutoMaskScreenshots([.textInputs])
[Instabug setAutoMaskScreenshots: IBGAutoMaskScreenshotOptionTextInputs];
-
Masking labels
This can be utilized to hide text labels including buttons and titlesInstabug.setAutoMaskScreenshots([.labels])
[Instabug setAutoMaskScreenshots: IBGAutoMaskScreenshotOptionLabels];
-
Masking images and videos
If your users might be sharing private images or videos on the app, in which you wouldn't want access to, you can utilize the following property to hide it.Instabug.setAutoMaskScreenshots([.media])
[Instabug setAutoMaskScreenshots: IBGAutoMaskScreenshotOptionMedia];
-
Masking nothing
You can also disable auto-masking all together if you need using the following propertyInstabug.setAutoMaskScreenshots([.maskNothing])
[Instabug setAutoMaskScreenshots: IBGAutoMaskScreenshotOptionMaskNothing];
-
Mix and match
You can combine different masking options according to your needs. For example, if you want to mask both text Inputs and labels, you can use the following APIInstabug.setAutoMaskScreenshots([.textInputs, .labels])
-
-
Hide a whole screen
In case you need to add an extra layer of privacy and prevent sensitive information from being captured in screenshots. This property will hide the overall screen when the view appears in a screenshotview.instabug_privateView = true
view.instabug_privateView = YES;
-
Set Private View
You can use this API to set any particular view as private so that it is always hidden in screenshots.
view.instabug_privateView = true
view.instabug_privateView = YES;
//Should be added in the activity with the view, takes any number of views
Instabug.addPrivateViews(view1, view2, view3);
//Should be added in the activity with the view, takes any number of views
Instabug.addPrivateViews(view1, view2, view3)
//Add the following tag to the view you'd like to hide
<Text ref={c => this.textView = c} style={styles.welcome}>This is a private view!</Text>
//Then call the following API
Instabug.setPrivateView(this.textView);
You can also remove a view from the list of private views using this API.
//Should be added in the activity with the view, takes any number of views
Instabug.removePrivateViews(view1, view2, view3);
//Should be added in the activity with the view, takes any number of views
Instabug.removePrivateViews(view1, view2, view3)
Network Masking
Instabug automatically logs all network requests performed by your app from the start of the session. Requests details, along with their responses, are sent with each report. Instabug will also show you an alert at the top of the bug report in your dashboard when network requests have timed-out or taken too long to complete. Note that the maximum number of network logs sent with each report is 1,000.
Logging HttpUrlConnection
requests for Android
HttpUrlConnection
requests for AndroidTo log network requests, use InstabugNetworkLog
then use the following method at the HttpUrlConnection
, requestBody
and responseBody
referenced here.
Importing Network Logger for React Native
First, you'll need to import the Network Logger in order to be able to start logging network requests. You can use the statement referenced here to import it.
Omitting Requests
You can omit requests from being logged based on their request or response details. You can also specify an expression to be evaluated against every request and response to determine if the request should be included in logs or not.
let path = "/products"
let requestPredicate = NSPredicate(format: "URL.path MATCHES %@", path)
let responsePredicate = NSPredicate(format: "statusCode >= %d AND statusCode <= %d", 200, 399)
NetworkLogger.setNetworkLoggingRequestFilterPredicate(requestPredicate, responseFilterPredicate: responsePredicate)
NSString *path = @"/products";
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:@"URL.path MATCHES %@", path];
NSPredicate *responsePredicate = [NSPredicate predicateWithFormat:@"statusCode >= %d AND statusCode <= %d", 200, 399];
[IBGNetworkLogger setNetworkLoggingRequestFilterPredicate:requestPredicate
responseFilterPredicate:responsePredicate];
// The code above excludes all requests made to URLs that have the /products path.
// It also excludes all responses with success and redirection status codes,
// thus only including requests with 4xx and 5xx responses.
NetworkLogger.omitLog((data) =>
// you can also use other parts of data for filtering (e.g.: responseBody, requestBody)
return data.url.contains('/payment');
);
// The code above excludes all requests made to any URL containing /payment. You also use other parts of data for filtering (e.g.: responseBody, requestBody).
NetworkLogger.setRequestFilterExpression('network.requestHeaders[\'accept\'] === \'application/json\'')
// The code above excludes all requests made to URLs that have request header accept set to application/json.
Obfuscating Data
Requests
You can obfuscate sensitive user data, such as authentication tokens, in requests without filtering out the whole request.
NetworkLogger.setRequestObfuscationHandler { (request) -> URLRequest in
var myRequest: NSMutableURLRequest = request as! NSMutableURLRequest
let urlString = request.url?.absoluteString
urlString = obfuscateAuthenticationTokenInString()
let obfuscatedURL = URL(string: urlString)
myRequest.url = obfuscatedURL
return myRequest.copy() as! URLRequest
}
IBGNetworkLogger.requestObfuscationHandler = ^NSURLRequest * _Nonnull(NSURLRequest * _Nonnull request) {
NSMutableURLRequest *myRequest = [request mutableCopy];
NSString *urlString = request.URL.absoluteString;
urlString = [self obfuscateAuthenticationTokenInString:urlString];
NSURL *obfuscatedURL = [NSURL URLWithString:urlString];
myRequest.URL = obfuscatedURL;
return myRequest;
};
NetworkLogger.obfuscateLog((data) {
data.requestHeaders.remove('Authorization');
return data;
});
NetworkLogger.setNetworkDataObfuscationHandler(async (networkData) => {
networkData.requestHeaders = {
'Content-Type': 'application/json',
'TestHeader': 'some-header2'
};
return networkData;
});
Responses
As with requests, the response object, as well as the response data, can be modified for obfuscation purposes before they are logged.
NetworkLogger.setResponseObfuscationHandler { (data, response, completion) in
if let data = data {
let modifiedData = self.modify(data: data)
let modifiedResponse = self.modify(response: response)
completion(modifiedData, modifiedResponse)
}
}
[IBGNetworkLogger setResponseObfuscationHandler:^(NSData * _Nullable responseData,
NSURLResponse * _Nonnull response,
NetworkObfuscationCompletionBlock _Nonnull returnBlock) {
NSData *modifiedData = [self obfuscateData:responseData];
NSURLResponse *modifiedResponse = [self obfuscateResponse:response];
returnBlock(modifiedData, modifiedResponse);
}];
NetworkLogger.obfuscateLog((data) {
data.responseBody = '';
return data;
});
NetworkLogger.setNetworkDataObfuscationHandler(async (networkData) => {
networkData.response = {};
return networkData;
});
Modifying Requests for Android
In the event that you need to modify a network request prior to sending it to the dashboard, you can follow the below steps:
-
Create a NetworkLogListener object and modify the captured network log as shown below.
val networkLogListener = NetworkLogListener { networkLog: NetworkLogSnapshot -> // Modify the received networkLog parameter return@NetworkLogListener networkLog }
NetworkLogListener networkLogListener = new NetworkLogListener() { @Override public NetworkLogSnapshot onNetworkLogCaptured(NetworkLogSnapshot networkLog) { // Modify the received networkLog parameter return networkLog; // To exclude the network trace from being captured, return null // return null; } };
-
Register the created NetworkLogListener to your InstabugOkHttpInterceptor object. This can be done through two different methods:
-
Pass it to the Constructor:
val instabugOkhttpInterceptor = InstabugOkhttpInterceptor(networkLogListener)
-
Call
registerNetworkLogsListener
method onInstabugOkhttpInterceptor
object.val instabugOkhttpInterceptor = InstabugOkhttpInterceptor() instabugOkhttpInterceptor.registerNetworkLogsListener(networkLogListener) // For the modifications to reflect in APM as well, you can use the below network logs listener APM.registerNetworkLogsListener(networkLogListener)
-
In case you need to remove the network listener, you can use the below method:
instabugOkhttpInterceptor.removeNetworkLogsListener()
// Use the below API to remove APM's network logs listener
APM.registerNetworkLogsListener(null)
instabugOkhttpInterceptor.removeNetworkLogsListener();
// Use the below API to remove APM's network logs listener
APM.registerNetworkLogsListener(null);
Note: Protecting sensitive information and respecting user privacy are vital. By following these steps, you'll create a secure and trustworthy environment for your users.
Updated about 1 month ago