Skip to content

Supportability for iOS

The SAP Mobile Platform SDK provides a set of features and APIs (collectively referred to as supportability) that captures errors, traces and logs events and performance, then uploads this information to SAP Mobile Platform Server or SAP Cloud Platform mobile service for development and operations, where it can be analyzed from the cockpit.

Define client log and trace policies in the administrator cockpit. For detailed information see Managing Application Logs and Traces (for SAP Mobile Platform Server) or Application Log and Trace Files (for SAP Cloud Platform mobile service for development and operations).

  • Enabling devices to upload logs to the server devices do not upload their logs and traces to the server by default. The server administrator must first enable log and trace retrieval from within the administration cockpit for each device.

  • Enabling end-to-end tracing on the server the administrator must enable end-to-end tracing on the server from within the administration cockpit.

Logging and Tracing Overview

SAP Mobile Platform provides supportability through logs and traces that enable administrators, developers, and support professionals to troubleshoot application issues. All logs have a common format and are stored in the SAP Mobile Platform Server database. All log entries for a particular business or application flow (such as an OData request or a registration) are correlated across the client and server stack. This enables you to visualize and understand the end-to-end flow, which helps in identifying the source of an application problem. In addition to logs and traces, you can activate end to end tracing on a per user/device basis to enable support to perform end-to-end diagnostics to identify performance or functional application problems.

System logs collect log messages that allow administrators and support professionals to identify problem areas. Developers can identify code problems by capturing debug level log messages. Set the log level for individual logging components to specify the amount of information captured.

Application tracing captures additional business data for a request (such as message data, payloads, HTTP headers, and URIs), which you can use to troubleshoot application problems. The business data captured in application traces is determined by the application developer. Enable tracing for individual logging components on an as-needed basis.

alt text

E2E tracing sessions are conducted by administrators and device users. E2E trace information is recorded in a Business Transaction XML (BTX) file on the client, which is uploaded to the server, and then uploaded to SAP Solution Manager for analysis.

Getting Started with Supportability

Set up your iOS environment and get familiar with logging and tracing components.

  • Environment Set Up

    After the libraries and resources are extracted from the SAP Mobile Platform SDK installer, make sure your application/project references these libraries:

    • libSupportability.a
    • libE2ETrace2.a
    • libClientLog.a
    • libHTTPConversation.a
    • libHTTPConvAuthFlows.a
    • libMobilePlace.a
    • libPerformanceLib.a
    • (iOS) Foundation.framework - (iOS) UIKit.framework
    • (iOS) CoreGraphics.framework
    • (iOS) libsqlite3.dylib (the default logger stores its data in an SQLite database)
    • (iOS) Security.framework

    Tip: The SAP Mobile Platform SDK installer includes a master CocoaPod spec file, which can be used to automatically set up the project dependencies.

  • Requirements

    • Minimum iOS SDK version: previous the latest major SDK version
    • Minimum iOS deployment target version: previous the latest SDK major version
    • Minimum XCode version: previous the latest XCode major version
  • Supportability Component: provides an additional level of abstraction; prevents developers from interacting directly with the underlying sub-components.

  • ClientLog Component: includes a set of logging related management and the consumer interfaces and classes.
  • E2ETrace2 Component: includes a set of E2E tracing related management and the consumer interfaces and classes.
  • Performance Component: intended for both framework and application performance measurements.

Exposed protocols include:

  • SAPClientLogger: defines the interface for client loggers. The ClientLog library provides a default implementation with the same name.
  • SAPE2ETrace: defines the interface for E2E tracing. The E2ETraceManager library provides a default implementation with the same name.

Public interfaces include:

  • SAPSupportabilityFacade: the Supportability component is an abstraction on top of the underlying E2ETrace and ClientLogger components. Clients retrieve the required management and consumer instances using the APIs and factory methods that are exposed by this wrapper.
  • SAPClientLogManager: the central access point to loggers; provides setters to common properties used by various loggers.
  • SAPE2ETraceManager: provides access to E2E tracing related features.

Add Logging to your iOS Application

Add logging to your native iOS OData app, upload logs to the server, and view the logs.

Logging

Use logging features provided by supportability and logging libraries.

  • Logger Creation and Setup

    The Supportability framework provides advanced logging capabilities. Instantiate dedicated and custom logger objects using the SAPLogManager factory method. The various SDK components use specific loggers internally.

1.. Add this to import the required public headers:

     #import "SAPSupportabilityFacade.h"
     #import "SAPClientLoggerDefault.h"
     #import "SAPClientLogManager.h"

2.. Query the default client log manager:

     // Get ClientLogManager instance
     id<SAPClientLogManager> logManager = [[SAPSupportabilityFacade  sharedManager] getClientLogManager];

By default, loggers persist their logs in a local database. If you need the logs to also appear in the console, you can override the default using the SAPClientLogManager setLogDestination: API. The API expects values defined in an E_CLIENT_LOG_DESTINATION enum, and can be combined using the OR operator:

     // Set the log destination for all loggers 
    // optional: default is FILESYSTEM
     [logManager setLogDestination:(CONSOLE | FILESYSTEM)];

Note: This is a global setting that is applied to all loggers (both currently active and for those created later).

3.. Set the log output destination and log level. Two log destinations are supported:

  • Console logs are displayed in Xcode debug console

  • FileSystem logs are persisted in the clients local storage (SQLite database in this case)

The SAPClientLogManager setLogLevel: method allows setting of the global log level. The default log level is ERROR, which makes your logs less verbose; however, if you need more detailed log messages, you can lower the log level to WarningClientLogLevel or DebugLogLevel:

    // Set the desired log level on all the loggers
         [logManager setLogLevel:WarningClientLogLevel];

    // Set the desired log level for a particular logger // optional: default is ErrorClientLogLevel
        [logManager setLogLevel:InfoClientLogLevel forIdentifier:LOGGER_PARSER];
        [logManager setLogLevel:FatalClientLogLevel forIdentifier:LOGGER_SERVER];

    // Set the log destination for a particular logger
     [logManager setLogDestination:FILESYSTEM forIdentifier:LOGGER_SERVER];
    ```

To instantiate a custom logger, provide a unique ID to every particular custom logger. Reusing the same logger ID produces the same logger instance:

```objc
   static NSString* kCustomLogger = @"CustomLogger";
   // instantiate a custom logger
      SAPClientLogger* customLogger = [logManager getLogger: kCustomLogger];

4.. Log a warning using the newly created logger:

   [customLogger logWarning:@"Warning message goes here"];  

In a production application, create a logger property to use throughout the given implementation file:

   @property (strong, nonatomic) SAPClientLogger* logger;
   //instantiate the custom logger
     self.logger = [logManager getLogger: kCustomLogger];
  • Fine Tune Logger Settings

The global setting applies for all existing logger instances, and provides default values for loggers to be created later. (If no global setting is made, the original defaults apply; ERROR for log level and FILESYSTEM for log destination.)

Global settings can be overridden on a per logger basis, which is useful if you want to silence some components, while enabling logging for a group of selected loggers (for example, to identify issues in a specific part of the project, or focus on a small subset of components).

     // set log level to DEBUG for this very logger
        [logManager setLogLevel:DebugClientLogLevel forIdentifier:kCustomLogger];
    // do not persist logs, just display them in XCode's console window
       [logManager setLogDestination:CONSOLE forIdentifier:kCustomLogger];
  • Client Log Levels

The default log level setting is ERROR. The following log levels are supported:

FatalClientLogLevel > ErrorClientLogLevel > WarningClientLogLevel > InfoClientLogLevel > DebugClientLogLevel

Lower levels automatically enable higher ones. For example, setting the log level to WARNING means that all WARNINGs, ERRORs and FATAL log messages are logged, while INFO and DEBUG messages are not:

FATAL > ERROR > WARNING > INFO > DEBUG

Setting the log level to FATAL silences all other log levels:

FATAL > ERROR > WARNING > INFO > DEBUG

Enabling the DEBUG log level is equivalent to enabling ALL log levels:

FATAL > ERROR > WARNING > INFO > DEBUG

Tip

  • Enable WARNING or even DEBUG log levels during development

  • Log only ERRORs and FATAL conditions in production mode, since (verbose) logging deteriorates system performance

  • Alternatively, clients may want to add the ability to switch on verbose logging (WARNINGS, DEBUG, or INFO)

  • Retrieving Logs

    SAPClientLogManager provides stream-based APIs to retrieve the logs that have been produced by the app.

    Use SAPClientLogManager getLogEntries: outputStream: error: to sequentially retrieve all logs with the specified and more critical level. Alternatively, use getLogEntriesForLogger: withLevel: outputStream: error: to fetch logs for a given logger.

A valid NSOutputStream should be passed to the APIs. This code shows a practical approach where the output stream is mapped to a file:

   NSError* error = nil;
   NSString* path = [NSString stringWithFormat:@"%@/%@", NSTemporaryDirectory(), @"logData.txt"];
   // map stream to file
   NSOutputStream* logStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];

   BOOL hasLogs = [m_LogManager getLogEntries:ErrorClientLogLevel outputStream:&logStream error:&error];
     if( error )
      {
         // eror handling
    }
    else
     {
         if( [[NSFileManager defaultManager] fileExistsAtPath:path] )
          {
             // process file content ...
             // finally remove the file
                 [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
        }
     }

For cases when the expected amount of log entries is low, passing nil for NSOutputStream is an option; this maps the stream to memory rather than to a file. Use with caution, since large amounts of log data may lead to low memory conditions and might even crash the app. This code shows how to stream log data to memory:

    // If the expected log amount is moderate, pass nil for NSOutputStream
    // logs will be streamed to memory, and can be obtained from the stream via NSStreamDataWrittenToMemoryStreamKey key
    NSOutputStream* memoryMappedStream = nil;
    NSError* error = nil;
    if( [m_LogManager getLogEntries:ErrorClientLogLevel outputStream:&memoryMappedStream error:&error] )
    {
        NSData* logData = [memoryMappedStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
        // process the contents
    }
    else
    {
        NSLog(@"Error in getting log data: %@", [error description]);
    }   
  • Client-side Log Persistence

Since it is difficult to predict how many logs will be produced by an app, persisting them on the client is intentionally temporary. Logs are removed:

* After being successfully uploaded to the server or

* Once their expiration date is reached. By default log entries are purged after one week.
  • Uploading Logs to the Server

    SAP Mobile Platform Server supports uploading of client log files. Access is controlled by the security configuration associated with the given application. Therefore, prior to uploading logs, the Server Administer must set the appropriate settings. See http://help.sap.com/saphelp_smp307svr/helpdata/en/e6/46aa8668a64efb94ec30e198a0b009/content.htm (or perform a Web search for “SMP” “Defining Client Log and Trace Policies”). Additionally, the application connection ID must be passed as a “X-SMP-APPCID” header field.

The following code attempts to upload client logs to SAP Mobile Platform Server. You must supply a valid SAP Mobile Platform Server log upload URL in the form “http(s)://host:port/clientlogs”, and the client log policy must be configured to accept log uploads on the Management Cockpit. See http://help.sap.com/saphelp_smp307svr/helpdata/en/e6/46aa8668a64efb94ec30e198a0b009/content.htm for details.

    NSURL* uploadURL = [NSURL URLWithString:@<url>:<port#>];

    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString   stringWithFormat:@"http://%@:%li/clientlogs", uploadURL.host, (long)uploadURL.port.integerValue]]];

    NSString* appCID = [Settings sharedInstance].appCID;
    [request setValue:appCID forHTTPHeaderField:@"X-SMP-APPCID"];

    SupportabilityUploader* uploader = [[SupportabilityUploader alloc] initWithHttpConversationManager:[SAPStoreManager sharedInstance].conversationManager urlRequest:request];


    [[[SAPSupportabilityFacade sharedManager] getClientLogManager] uploadClientLogs:uploader completion:^(NSError* error) {
     if ( !error )
      {
           LOGDEB( @"Log upload completed succesfully" );
            UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString( @"Log Upload", nil )         message:NSLocalizedString( @"Log upload completed succesfully", nil ) delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];

             dispatch_async(dispatch_get_main_queue(), ^{
              [alertView show];
              });
        }
       else
        {
           LOGWAR( @"Log upload failed: %@", error.localizedDescription );
                UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString( @"Log Upload", nil ) message:[NSString stringWithFormat:@"Log upload failed: %@", error.localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];

             dispatch_async(dispatch_get_main_queue(), ^{
                  [alertView show];
             });
        }

        [sUploadLogLock unlock];
        }];

Add Tracing to your iOS Application

Add tracing to your native iOS OData app, upload the BTX that contains all collected trace data to the server, and view the trace information.

End-to-End Tracing

The E2ETracing component collects end-to-end step and request trace data including performance measurements and user actions. SDK libraries are instrumented for E2ETrace enablement. Furthermore, the E2ETrace component exposes APIs which allows developers to perform various, E2E tracing related tasks on the client.

  • E2E Transactions, Steps and Requests

    To perform E2E tracing, start an end-to-end transaction. A transaction defines the boundaries of an E2E tracing session.

    ! Restriction Only one E2E transaction can be active at a time. Starting a new transaction while an active one is running causes the latter to terminate (including its active steps and requests).

Within the context of an active E2E trace transaction, you can start several E2E steps. Steps can be logically mapped to a given use case or business scenarios. For example, an E2E step might be assigned to the phase of presenting your main screen and populating it with data.

Steps can in turn consist of one to many E2E requests. An E2E request is an atomic logical entity generally bound to one network request. An E2E request could be mapped, for example, a GET request for fetching data, or a POST request that updates an entity on the server.

A Business Transaction XML is generated as a result of running an E2E trace session. This XML file contains E2E trace relevant step and request data in a standard format. The framework allows for uploading the generated BTX to the SAP Mobile Platform Server, where BTX files are archived for further analysis.

  • Basic E2E Tracing

    These steps enable basic E2E trace enablement of an app. Custom E2E tracing is described later.

    1.. Instantiate the E2E trace manager instance:

    //get the E2ETraceManager instance
    id<SAPE2ETraceManager> E2ETraceManager = [[SAPSupportabilityFacade sharedManager] getE2ETraceManager];

Start a transaction; which is mandatory because E2E steps and requests must start within the context of an E2E transaction. Only one E2E transaction can be active at a time.

    NSError* errorStartTransaction = nil;
    id<SAPE2ETraceTransaction> E2ETransaction = [E2ETraceManager startTransaction:@"SampleTransaction"      error:&errorStartTransaction];

The call to E2ETraceManager startTransaction: creates and starts an E2E transaction. If there was an active transaction when calling the API, the old transaction ends and a new instance is created. You can query the active transaction (if any) using the E2ETraceManager getActiveTransaction API.

    id<SAPE2ETraceTransaction> activeTransaction = [self.traceManager getActiveTransaction];

An E2E transaction can have two states: SAP_E2E_Trace_Status_Started or SAP_E2E_\Trace_Status_Ended.

! Remember If a transaction is ended, it cannot perform further E2E steps and requests.

2.. Start an E2E step in the context of the newly created transaction (call E2ETransaction startStep: on a valid E2ETransaction instance.) The transaction must be in status started to start new steps; if the transaction is ended, attempts to start new steps will fail.

    NSError* stepError = nil;
    id<SAPE2ETraceStep> E2EStep = [E2ETransaction startStep:&stepError];

You can also call E2ETraceManager’s getActiveTransaction API to retrieve the active transaction, and start the step using this:

    [[self.traceManager getActiveTransaction] startStep:&stepError];

3.. Close the active step before ending the E2E trace session. Invoke the endStep: method to end E2ETraceSteps:

    errorEndStep = nil;
    [E2EStep endStep:&errorEndStep];

4.. Close the E2E trace session by invoking E2ETraceManager endTransaction:

    [E2ETraceManager endTransaction:E2ETransaction error:&error];

5.. This call ends the active steps and their running requests; additionally, the BTX document is generated and persisted in the applications sandbox. You can also call endTransaction on the transaction instance itself:

    [E2ETransaction endTransaction:&unexpectedError];

! Note: Selected SDK components are instrumented specifically for E2E tracing. Which means that although the above code snippets did not trigger custom steps or requests, the SDK produces trace data. The BTX contains all collected trace data, including performance measurements, network related metric data like count of bytes sent or received, request / response headers, and so on.

  • Custom E2E Tracing

Although basic E2E tracing is sufficient in most cases, some apps may need to generate custom E2E trace data, which the Supportability framework provides. In this case, along with starting and ending an E2E transaction and E2E steps,the developer must also manage requests.

1.. Perform the steps in "Basic E2E Tracing".

2.. Once you have an active E2E transaction and step, create and start an E2E trace request:

      NSError* requestError = nil;
      // created a request within the context of the current step
        SAPE2ETraceRequest* E2ERequest = [E2EStep startRequest:&requestError];

The SAPE2ETraceRequest exposes APIs that allow setting properties required for E2E tracing:

        //Should be called before data is sent.
        (void) markSending() 

        //Should be called as soon as the data was sent. 
        (void) markSent() 

        //Should be called upon receiving first data bytes.
        (void) markReceiving()  

        //Should be called as soon as data was completely received.
        (void) markReceived() 

        //Sets the size of sent data in bytes.
        (void) setByteCountSent:(NSUInteger)byteCount 

        //Sets the size of received data in bytes.
        (void) setByteCountReceived:(NSUInteger)byteCount

        //Sets the request headers.
        (void) setRequestHeaders:(NSString*) requestHeaders_in;

        //Sets the response headers.
        (void) setResponseHeadersDictionary:(NSDictionary*) responseHeaders_in; 

        //Sets the HTTP request line. 
        (void) setRequestLine:(NSString*) requestLine_in;

3.. For example, set up the E2E trace request when starting a network request:

    [E2ERequest setByteCountSent:request.HTTPBody.length];
    [E2ERequest markSending:nil];
    [E2ERequest markSent:nil];
    [E2ERequest setRequestHeadersDictionary:request.allHTTPHeaderFields];
    [E2ERequest setRequestLine:[NSString stringWithFormat:@"%@ %@ %@", request.HTTPMethod, request.URL.absoluteString, @HTTP/1.1"]];

4.. After the network request completes, the response is processed:

    [E2ERequest markReceiving:nil];
    [E2ERequest markReceived:nil];
    [E2ERequest setResponseHeadersDictionary:[(NSHTTPURLResponse*)response allHeaderFields]];
    [E2ERequest setReturnCode:[NSString stringWithFormat:@"%ld", (long)((NSHTTPURLResponse*)response).statusCode]];
    [E2ERequest setByteCountReceived:receivedData.length];

5.. Close the trace request:

    NSError* error = nil;
    [E2ERequest endRequest:&error];

Calling the E2ETraceRequest endRequest causes the E2ETrace request to be marked as ended.

! Remember Further attempts to modify this request will fail.

E2ETraceSteps can be ended by invoking their endStep method, which also ends all their active requests automatically:

    errorEndStep = nil;
    [E2EStep endStep:&errorEndStep];

6.. End the active ETE trace transaction:

    [E2ETransaction endTransaction:&unexpectedError];

This call also traverses down and ends all active steps and their requests.

  • Client-Side BTX Persistence

    The end result of an E2E trace session is the Business Transaction XML (BTX), which is temporarily stored in the app’s file system. The BTX is automatically removed when one of the following conditions are met:

    • the BTX is successfully uploaded to the server, or
    • a new E2E trace session starts
  • Uploading the BTX

    SAP Mobile Platform Server supports uploading E2E trace results ( BTX). Access is controlled by the security configuration associated with the given application. Therefore, prior to uploading the BTX, the Server Administrator must perform some administrative tasks. For detailed instructions on E2E trace and log related settings and configuration see http://help.sap.com/saphelp_smp307svr/helpdata/en/e6/46aa8668a64efb94ec30e198a0b009/content.htm (or perform a Web search for “SMP” “Defining Client Log and Trace Policies”). In addition, the application connection ID must be passed as a “X-SMP-APPCID” header field.

    The following code attempts to upload the BTX to the SAP Mobile Platform Server. You must supply a valid SAP Mobile Platform Server log upload URL in the form “http(s)://host:port/btx”, and application E2E tracing must be enabled in Management Cockpit. See http://help.sap.com/saphelp_smp307svr/helpdata/en/e6/46aa8668a64efb94ec30e198a0b009/content.htm for details.

   NSURL* uploadURL = [NSURL URLWithString:@<upload url>:port"];

   NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%li/btx", uploadURL.host, (long)uploadURL.port.integerValue]]];

   NSString* appCID = [Settings sharedInstance].appCID;
   [request setValue:appCID forHTTPHeaderField:@"X-SMP-APPCID"];

    SupportabilityUploader* uploader = [[SupportabilityUploader alloc] initWithHttpConversationManager:[SAPStoreManager         sharedInstance].conversationManager urlRequest:request];

   [[[SAPSupportabilityFacade sharedManager] getE2ETraceManager] uploadBTX:uploader completion:^(NSError* error) {
    if ( !error )
    {
    LOGDEB( @"BTX upload completed succesfully" );
    UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString( @"BTX Upload", nil ) message:NSLocalizedString( @"BTX upload completed succesfully", nil ) delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];

    dispatch_async(dispatch_get_main_queue(), ^{
        [alertView show];
    });
    }
    else
    {
    LOGWAR( @"BTX upload failed: %@", error.localizedDescription );
    UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString( @"BTX Upload", nil ) message:[NSString stringWithFormat:@"BTX upload failed: %@", error.localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];

    dispatch_async(dispatch_get_main_queue(), ^{
        [alertView show];
        });
    }
      }];
    }

Normally the E2E trace closes once the BTX is uploaded; however, if this does not happen, the framework performs the necessary cleanup when calling the upload API.

! Remember Invoking the upload API closes the active E2E trace session; any pending E2E transactions, steps, or requests will be ended.

  • E2E Trace Implications

    Enabling E2E tracing is useful to track the functionality of apps; however, it does come at a cost which affects performance and memory usage. When E2E tracing is on, additional housekeeping is done by the various SDK components to insert the required values in the generated BTX. During the E2E session, the BTX document is managed in memory, and only gets persisted to disk if uploading it to the server fails. Additionally, E2E tracing automatically switches all loggers to DEBUG mode, meaning that logs become more verbose compared to the default ERROR mode.

    ! Remember When the E2ETrace session is closed, loggers automatically revert to their normal settings.

    Use application E2E tracing only if it is required.

  • Examples

    The Supportability framework includes an extensive set of unit tests that cover many use-cases. Additionally, every public API is documented, and includes usage examples for the less obvious use-cases.