Logging

This section covers how Instabug automatically attaches the console logs, the verbose logs, all the steps done by the user until a bug report is sent.

You can send a variety of logs types with each crash or bug report. They will appear within each report on your Instabug dashboard as shown below. The cut-off point of the log collection is when Instabug is invoked.

Logs - Dashboard

Logs - Dashboard

Console Logs

Instabug captures all console logs and displays them on your dashboard with each report. Note that the maximum number of console logs sent with each report is 1K for the number of the statements and unlimited number of characters for each statement.

Console Logs on iOS 10

Due to the changes Apple recently made to how logging works, we can only capture console logs on iOS versions prior to 10.

To work around this issue, you can add the following snippet to your AppDelegate file but outside the scope of the class to allow Instabug to capture logs automatically on iOS 10.

inline void NSLog(NSString *format, ...) {
    va_list arg_list;
    va_start(arg_list, format);
    IBGNSLogWithLevel(format, arg_list, IBGLogLevelDefault);
    va_end(arg_list);
}

Alternatively, you can replace all your NSLog() and print() statements with IBGLog.

Another workaround in case you are using Swift 3.0 or above is to send the console logs through the following print method. Make sure to define this method outside AppDelegate class scope in the correct place such that it becomes accessible in all your classes.

public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
    let output = items.map { "\($0)" }.joined(separator: separator)
    
    Swift.print(output, terminator: terminator);
    Instabug.ibgLog(output)
}

Instabug Logs

Instabug Log is similar to NSLog() and print(), but it has the added benefit of having different verbosity levels. This lets you filter logs based on their verbosity level when viewing a report on your Instabug dashboard. Note that the maximum number of Instabug logs sent with each report is 1k.

Use Instabug Log through the following methods.

IBGLogVerbose(@"Verbose log message");
IBGLogDebug(@"Debug log message");
IBGLogInfo(@"Info log message");
IBGLogWarn(@"Warn log message");
IBGLogError(@"Error log message");
IBGLog.log("Log statement")
IBGLog.logVerbose("Verbose statement")
IBGLog.logInfo("Info statement")
IBGLog.logWarn("Warning statement")
IBGLog.logDebug("Debug statement")
IBGLog.logError("Error statement")

Instabug Logs with 3rd Party Loggers

If you use CocoaLumberjack or XCGLogger, you can easily route all your logs to Instabug using our log destinations. Check the instructions on GitHub for CocoaLumberjack and XCGLogger.

Visual Repro Steps

The set of steps that the user commits while they are in a specific view are grouped together. When the user navigates to a new view, this action is reflected in the repro steps and all the interactions happening within the new view are grouped together until the user navigates away from this view.

This feature is enabled by default starting from the Silver Plan. You can control it through the following API.

Instabug.reproStepsMode = IBGUserStepsModeEnable;
Instabug.reproStepsMode = .enable

Here are the possible arguments.

IBGUserStepsModeEnable;
IBGUserStepsModeEnabledWithNoScreenshots;
IBGUserStepsModeDisable;
.enable
.enabledWithNoScreenshots
.disable

User Steps

Instabug can help you reproduce issues by tracking each step the user has taken until a report is sent. Note that the maximum number of user steps sent with each report is 1k.

User steps will be formatted as follows:
Event in label of type class in controller.

The type of events captured are tap, long press, force touch, swipe, scroll and pinch.
The label would be the label of the object that contains the event.
Class would be the class of the object that contains the event.
Controller would be the view that contained the event.

You can disable it by disabling user steps using the following method:

Instabug.trackUserSteps = NO;
Instabug.trackUserSteps = false

To be able to capture the user steps, we need to do some method swizzling. We take an approach to swizzling that is absolutely safe and does not impact your app negatively in any way. For more details, you can check the following link.

User Events

You can log custom user events throughout your application. Events are automatically going to be included with each report.

[Instabug logUserEventWithName:@"Skipped Walkthrough"];
Instabug.logUserEvent(withName: "Skipped Walkthrough")

Network Logs

Instabug automatically logs all network requests performed by your app. Requests details, along with their responses, are going to be sent with each report. Instabug will also show an alert in the bug report when it finds that some network requests have timed-out or have taken too long to complete. Note that the maximum number of network logs sent with each report is 100.

Omitting Requests From Logs

You can omit requests from being logged based on either their request or response details.

+ [Instabug setNetworkLoggingRequestFilterPredicate:responseFilterPredicate:] allows you to specify 2 predicates that are going to be evaluated against every request and response to determine if the request should be included in logs or not.

NSString *path = @"/products";
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:@"URL.path MATCHES %@", path];
NSPredicate *responsePredicate = [NSPredicate predicateWithFormat:@"statusCode >= %d AND statusCode <= %d", 200, 399];
[Instabug setNetworkLoggingRequestFilterPredicate:requestPredicate responseFilterPredicate:responsePredicate];
let path = "/products"
let requestPredicate = NSPredicate(format: "URL.path MATCHES %@", path)
let responsePredicate = NSPredicate(format: "statusCode >= %d AND statusCode <= %d", 200, 399)
Instabug.setNetworkLoggingRequestFilterPredicate(requestPredicate, responseFilterPredicate: responsePredicate)

The code above will exclude all requests made to URLs that have /products path. It will also exclude all responses that has a success and redirection status code, thus only including requests with 4xx and 5xx responses.

requestFilterPredicate is evaluated against an NSURLRequest, while responseFilterPredicate is evaluated against an NSHTTPURLResponse.

Disabling Requests Logging

Network requests logging is enabled by default if it's included in your plan. To disable it, use the following method.

IBGNetworkLogger.enabled = NO;
NetworkLogger.enabled = false

Obfuscating Data in Logs

Requests

You can obfuscate user sensitive data in requests, like authentication tokens for example, without filtering out the whole request.

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.requestObfuscationHandler = { (request) -> URLRequest in
    var myRequest:NSMutableURLRequest = request.mutableCopy()
    let urlString = request.url?.absoluteString
    urlString = obfuscateAuthenticationTokenInString()
    let obfuscatedURL = URL(string: urlString)
    myRequest.url = obfuscatedURL
    return myRequest
}

Responses

As with requests, the response object, as well as the response data, could be modified for obfuscation purposes before they are logged.

[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.setResponseObfuscationHandler { (data, response, completion) in
    if let data = data {
        let modifiedData = self.modify(data: data)
        let modifiedResponse = self.modify(response: response)
        
        completion(modifiedData, modifiedResponse)
    }
}

Requests Not Appearing in Logs

If your networking requests aren't being logged automatically, that probably means you're using a custom NSURLSession or NSURLSessionConfiguration.

To enable logging for your NSURLSession, add the following code.

NSURLSessionConfiguration *configuration = NSURLSessionConfiguration.defaultSessionConfiguration;
[IBGNetworkLogger enableLoggingForURLSessionConfiguration:configuration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
let configuration = URLSessionConfiguration.default
NetworkLogger.enableLogging(for: configuration)
let session = URLSession(configuration: configuration)

AFNetworking

To enable logging for AFNetworking, create the following class.

// IBGAFURLSessionManager.h

#import <AFNetworking/AFNetworking.h>

@interface IBGAFURLSessionManager : AFURLSessionManager

@end

// IBGAFURLSessionManager.m
  
#import "IBGAFURLSessionManager.h"
#import <Instabug/Instabug.h>

@implementation IBGAFURLSessionManager

- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration {
		[IBGNetworkLogger enableLoggingForURLSessionConfiguration:configuration];
    
    return [super initWithSessionConfiguration:configuration];
}

@end

Then use IBGAFURLSessionManager to create your requests.

Alamofire

To enable logging for Alamofire, create the following class.

import Alamofire
import Instabug

class IBGSessionManager: Alamofire.SessionManager {
    static let sharedManager: IBGSessionManager = {
        let configuration = URLSessionConfiguration.default
				NetworkLogger.enableLogging(for: configuration)
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
        let manager = IBGSessionManager(configuration: configuration)
        return manager
    }()
}

Then use IBGAFURLSessionManager to create your requests.