Saturday, September 14, 2019

Sentry integration to an Android application - Sentry logging

Date : 14 / 09 / 2019

Hi,

In my recent project I had to integrate logging mechanism for the android project. Although I have used Google Analytics, Crashlytics and Appcenter Analytics it was not enough for this project. Because the administer should be provided with a one-stop dashboard for view both android app and backend server errors.

So we implemented sentry for both backend and android app. The documentation is not giving enough information so I'm writing this blog to easily integrate Sentry to your android project.

First implement following libraries in your app.gradle file, inside dependencies block.

//sentry loggingimplementation 'io.sentry:sentry-android:1.7.27'
implementation 'io.sentry:sentry-logback:1.7.27'

The sentry-android is of cause for integrating sentry to android, and the sentry-logback is for capture and send log report to the sentry. We can ignore this sentry-logback and use ,

try(exception: Exception){
  // some code which returns exception 
}catch { 
  Sentry.capture(exception) 
}

But this only capture exceptions. We need more than that. we need info, warning, errors and get that stacktrace. That's why we use sentry-logback. There are many substitute libraries like Timber, Log4j and many others. You can try them too. 

Next click src -> main. Right click on main folder, then New -> Directory. Name it as "resources". This name is important because by default Sentry is looking at this directory to get values. Create logback.xml and sentry.properties files inside this directory.




You need url to put inside sentry.properties file. I think you already created a Sentry project. You can have multiple projects inside a project. Let's say We have a project called SamProj. Inside it we have AndroidSamp and WebSamp. Goto settings in SamProj. select projects tab -> select AndroidSamp. Then select Client Keys (DSN). Copy that DSN.

paste your dsn code in your sentry.properties file as below. following code is just a sample.

dsn=https://asdadadddada333444sdfsdfsdfe@sentry.io/234234435
anr.enabled = true

Next in logback.xml paste the following code. You can use this as it is. 


<configuration>    <!-- Configure the Console appender -->    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">        <encoder>            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>        </encoder>    </appender>
    <!-- Configure the Sentry appender, overriding the logging threshold to the WARN level -->    <appender name="Sentry" class="io.sentry.logback.SentryAppender">        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">            <level>INFO</level>        </filter>    </appender>
    <!-- Enable the Console and Sentry appenders, Console is provided as an example of a non-Sentry logger that is set to a different logging threshold -->    <root level="INFO">        <appender-ref ref="Console" />        <appender-ref ref="Sentry" />    </root></configuration>


Ok. Now configuration part is done. Let's move to the codes.

There was a requirement to get logs separately by environment. Which means by Production environment(actual user devices) and development environment(test devices). So we created a gradle build config variable for that purpose.

In app.gradle,


buildTypes {
    release {
        it.buildConfigField 'String', "ENVIRONMENT", '"release"'    }
    debug {
        it.buildConfigField 'String', "ENVIRONMENT", '"development"'    }
}


Create an Application class, and paste the following code.

class MainApplication: Application() {
    override fun onCreate() {
        super.onCreate()
        Sentry.init(AndroidSentryClientFactory(this))
        Sentry.getStoredClient().environment = BuildConfig.ENVIRONMENT    }
}

Don't forget to register this application class in your manifest file.

<application
android:name=".MainApplication">

Next create a BaseActivity. All your other activities must extend from this class so we can change one file and that change will propagate to child classes. If not you can directly code in your activity classes but that's not a good practice since then every time you are duplicating codes.


open class BaseActivity: AppCompatActivity() {

    private val logger = LoggerFactory.getLogger(this::class.java.simpleName)

    fun logApplicationError(error: String){
        logger.error(error)
    }

    fun logApplicationException(error: Exception){
        logger.error("Exception caught", error)
    }

    fun logApplicationInfo(info: String){
        logger.info(info)
    }

}

Now all you have to do is call these methods from your child activities and if any error or exception occurs, It will send that log to Sentry.


class QRCodeActivity : BaseActivity() { // make sure to extend from BaseActivity

private fun textToQRImage(text: String): Bitmap? {
    val bitMatrix: BitMatrix
    try {
        bitMatrix = MultiFormatWriter().encode(
            text,            BarcodeFormat.QR_CODE,            QRCodeSize, QRCodeSize, null        )

    } catch (exception: IllegalArgumentException) {
        logApplicationException(exception)
        return null    }
}

}


That's it. Now in your Sentry dashboard, you can filter error logs by environment with other data when errors happens and take necessary actions. There is a section in Sentry documentation that we should add another library with related to ProGuard.
https://docs.sentry.io/clients/java/integrations/#proguard

I still didn't use this and if anyone know what is the use of this please tell us in the comment section. I will update this blog when I got my hands dirty with sentry proguard integration.

That's it. Hope you can find something useful.

Happy coding !!!!