Skip to content

Uploading Logs

SAP BTP SDK for Android version 4.0 introduces a simplified LoggingService to manage all logging-related tasks. As a result, the Logging API is now deprecated. This reflects the fact that the mechanism to upload logs was also set to deprecated in the Google Android SDK.

The log upload API in LoggingService looks like this:

fun upload(
    owner: LifecycleOwner? = null,
    uploadType: LogUploadType? = null,
    listener: ServiceListener<Boolean>? = null,
    progressReporter: ((Int) -> Unit)? = null
    ) { ... }

All the arguments can be null, meaning that the client code does not have to care about upload progress, nor the result. To observe the result, both owner and listener must be provided, where owner could be an activity or a fragment that has a lifecycle (so that the activity or fragment is destroyed by the system). The registered listener will also be removed automatically to avoid wasting system resources.

Sample client code:

SDKInitializer.getService(LoggingService::class)?.also { logging ->
    logging.upload(owner = this, listener = object : ServiceListener<Boolean> {
        override fun onServiceDone(result: ServiceResult<Boolean>) {
            val msg = if (result is ServiceResult.SUCCESS) {
                getString(R.string.log_upload_done)
            } else {
                (result as ServiceResult.FAILURE).message
            }
            Toast.makeText(this@MainBusinessActivity, msg, Toast.LENGTH_LONG).show()
        }
    }) {
        //the upload progress reporter.
        logger.debug("Log uploaded $it%")
    }
}

Note: The following sections are marked as deprecated in SAP BTP SDK for Android version 4.0.

You can add code to upload logs to the SAP mobile service cockpit for review. In your application, call:

Logging.upload();
Logging.upload()

To be notified when the upload is complete, or to get upload progress, implement Logging.UploadListener:

class MyClass implements Logging.UploadListener {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);

    // Logging event listeners
    public void onError(Throwable throwable) {
        if (throwable instanceof HttpException) {
            if (((HttpException)throwable).code() == 403) {
                Toast.makeText(getApplicationContext(),"Client log upload is not enabled for the application", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(),throwable.getMessage(), Toast.LENGTH_SHORT).show();
            }
        } else if (throwable instanceof IOException) {
            Toast.makeText(getApplicationContext(),throwable.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    public void onSuccess() {
        Toast.makeText(getApplicationContext(),"Upload sucessful", Toast.LENGTH_SHORT).show();
    }

    public void onProgress(int percent) {
        LOGGER.debug("Upload progress: " + percent);
    }
}
class MyClass : Logging.UploadListener {

    // Logging event listeners
    override fun onError(throwable: Throwable) {
        if (throwable is HttpException) {
            if (throwable.code() == 403) {
                Toast.makeText(
                    applicationContext,
                    "Client log upload is not enabled for the application",
                    Toast.LENGTH_SHORT
                ).show()
            } else {
                Toast.makeText(applicationContext, throwable.message, Toast.LENGTH_SHORT).show()
            }
        } else if (throwable is IOException) {
            Toast.makeText(applicationContext, throwable.message, Toast.LENGTH_SHORT).show()
        }
    }

    override fun onSuccess() {
        Toast.makeText(applicationContext, "Upload sucessful", Toast.LENGTH_SHORT).show()
    }

    override fun onProgress(percent: Int) {
        LOGGER.debug("Upload progress: " + percent)
    }

    companion object {
        private val LOGGER = LoggerFactory.getLogger(MyClass::class.java)
    }
}

And add that implementation to the listener list by calling:

Logging.addUploadListener(uploadListener);
Logging.addUploadListener(uploadListener)

If automatic upload is enabled by LogService and triggered at some time, the listeners will be notified too.

Setting the Upload Type

By default, when a log upload is requested, the current log data is removed from the log and stored temporarily. The temporary store is then uploaded. If the upload succeeds, the temporary store is deleted.

If the upload is canceled or does not succeed, the default behavior is to delete the old temporary store the next time an upload is called.

You can control what happens to the temporary store when you upload the log by adding a parameter to Logging.uploadLog(); that sets the upload type. The types are:

  • Logging.UploadType.DEFAULT Upload the current contents of the log. If there is an old log that did not get uploaded from a previous upload attempt, the old log is deleted and not uploaded.

  • Logging.UploadType.LAST If there is an old log that did not get uploaded from a previous upload attempt, upload the old log but do not upload the contents of the current log.

  • Logging.UploadType.MERGE Upload the current contents of the log, and if there is an old log that did not get uploaded from a previous upload attempt, also upload the old log. You should use some caution with this choice: if repeated attempts to upload the log with this upload type fail, the application could run out of storage space.

DEFAULT type is used by automatic upload supported by LogService.

You can test for the presence of logs that did not get uploaded by calling Logging.hasPendingLog().

Note

false is always returned if an upload is in progress.

Correlation IDs

Correlation IDs are sent by the client to the mobile services server and are used to trace client requests on the server. A correlation ID will automatically be added to log records when logger requests are made from a given thread during the request/response flow.

The request must be sent using an OkHttpClient configured with the CorrelationInterceptor.

CorrelationInterceptor correlationInterceptor = new CorrelationInterceptor();

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .addInterceptor(correlationInterceptor)
    .build();
val correlationInterceptor = CorrelationInterceptor()

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(correlationInterceptor)
    .build()

Configuring Logging in SAP Mobile Services

Your application must be configured in the SAP mobile service cockpit to accept uploaded logs. To do that:

  1. From the cockpit, configure your application and select Client Policies.
  2. Scroll down to Log Policy and select Log Upload.

See Defining Client Log Policy for more information.

To view the log in the cockpit, select Logs > Log and Trace, then select your log from the list. You may have to adjust the time range filter to see your log.


Last update: December 8, 2021