Skip to content

Latest commit

 

History

History
318 lines (242 loc) · 12.8 KB

File metadata and controls

318 lines (242 loc) · 12.8 KB

mobile-android-logging

On Branch Push (main) Quality Gate Status

An Android package usable for HTTP logging and passing analytics to a third-party SDK. Also included is an abstraction class for using Google's Firebase analytics platform.

Functionality provided by Logging module

The main functionality of the logging module is to provide a single source of truth for standardised code logging functionality that makes it simple to switch logging framework if desired.

The three packages have the following functionality:

  1. The :api module contains logger interfaces.
  2. The :impl module contains logger implementations and their associated Hilt modules for dependency injection into their constructors.
  3. The :testdouble module contains fake logger implementations for testing and their Hilt modules for dependency injection into their constructors.

Getting started with Logging module dependencies

Without a version catalog

In your build.gradle.kts files, for each module (and for each build flavor), add dependencies needed for example:

// Refer to the uploaded packages for the latest version:
// https://github.com/orgs/govuk-one-login/packages?repo_name=mobile-android-logging
val loggingLibraryVersion: String by rootProject.extra("1.2.3")

// Both `impl` and `testdouble` artifacts provide the `api` module as a gradle `api` dependency.
implementation("uk.gov.logging:logging-impl:$loggingLibraryVersion")
testImplementation("uk.gov.logging:logging-testdouble:$loggingLibraryVersion")
androidTestImplementation("uk.gov.logging:logging-testdouble:$loggingLibraryVersion")

With a version catalog (recommended)

To migrate to using version catalogs, see Gradle Version Catalogs.

In your root ./gradle/libs.versions.toml file:

[versions]
gov-logging = "1.2.3" # https://github.com/orgs/govuk-one-login/packages?repo_name=mobile-android-logging

[libraries]
uk-gov-logging-api = { module = "uk.gov.logging:logging-api", version.ref = "gov-logging"}
uk-gov-logging-impl = { module = "uk.gov.logging:logging-impl", version.ref = "gov-logging"}
uk-gov-logging-testdouble = { module = "uk.gov.logging:logging-testdouble", version.ref = "gov-logging"}

Then in your build.gradle.kts files:

// Both `impl` and `testdouble` artifacts provide the `api` module as a gradle `api` dependency.
implementation(libs.uk.gov.logging.impl)
testImplementation(libs.uk.gov.logging.testdouble)
androidTestImplementation(libs.uk.gov.logging.testdouble)

Hilt Configuration

Despite there being usable Hilt modules in the logging module, these are mainly for Android Logging. You should also create your own Singleton scoped Hilt modules to provide the specific analytics logger implementation that you need, and also to provide the Firebase analytics implementation. For example:

@InstallIn(SingletonComponent::class)
@Module
object AnalyticsSingletonModule {
    @Provides
    @Singleton
    fun providesAnalyticsLogger(
        analyticsLogger: FirebaseAnalyticsLogger
    ): AnalyticsLogger = MemorisedAnalyticsLogger(analyticsLogger)
}

@InstallIn(SingletonComponent::class)
@Module
class FirebaseSingletonModule {
    @Provides
    @Singleton
    fun providesFirebaseAnalytics(): FirebaseAnalytics = Firebase.analytics
}

Android Logger V2

Android Logger v2 offers a set of convenience functions for logging messages and exceptions. It also allows developers to record exceptions either with custom error keys or without custom error keys.

The logger provides the following functions:

debug–Logs debug messages.

info–Logs informational messages.

Error Logging

An error function that allows the consumer to set custom error keys, set a tag, and record an exception.

An error function that allows the consumer to log an error, set a tag, and record an exception, without setting custom error keys.

An error function that allows the consumer to log only error messages and set a tag, without recording an exception or custom error keys.

warning–Logs warning messages and allows the user to set tags.

class AndroidLogger(
    private val crashLogger: CrashLogger,
) : Logger {
    override fun debug(
        tag: String,
        message: String,
    ) {
        if (BuildConfig.DEBUG) {
            Log.d(tag, message)
        }
        crashLogger.log("I : $tag : $message")
    }

    override fun info(
        tag: String,
        message: String,
    ) {
        if (BuildConfig.DEBUG) {
            Log.i(tag, message)
        }
        crashLogger.log("I : $tag : $message")
    }

    override fun error(
        tag: String,
        message: String,
        throwable: Throwable,
        errorKeys: ErrorKeys,
    ) {
        if (BuildConfig.DEBUG) {
            Log.e(tag, message, throwable)
        }
        crashLogger.log(throwable, errorKeys)
    }

    override fun error(
        tag: String,
        message: String,
        throwable: Throwable,
    ) {
        if (BuildConfig.DEBUG) {
            Log.e(tag, message, throwable)
        }
        crashLogger.log(throwable)
    }

    override fun error(
        tag: String,
        message: String,
    ) {
        if (BuildConfig.DEBUG) {
            Log.e(tag, message)
        }
        crashLogger.log("E: $tag : $message")
    }

    override fun warning(
        tag: String,
        message: String,
    ) {
        if (BuildConfig.DEBUG) {
            Log.w(tag, message)
        }
        crashLogger.log("W: $tag : $message")
    }
}

Android Logger V3

Android Logger v3 introduces a structured, entry-based logging API. Instead of calling individual log methods with separate String parameters, v3 enforces consistency in log configuration by using LogEntry sealed types to represent log events, with data classes predefined for each log level for the consumer's convenience.

Entries in the logger are processed through the log function. The sub-functions are still available for flexibility.

It also supports CustomKey types for setting typed key-value pairs on crash reports.

Logger The Logger interface is a functional interface with convenience sub-functions (debug, info, warning, error).

The logger provides the following sub-functions:

debug–Logs debug messages with a tag.

info–Logs informational messages with a tag.

warning–Logs warning messages with a tag.

verbose–Logs verbose messages with a tag.

Error Logging An error function that allows the consumer to log an error, set a tag, parse the exception, and set typed custom keys (StringKey, IntKey, DoubleKey, BooleanKey).

For examples showing how to use the Logger, see LoggerExample

LogEntry

LogEntry.Message interface extends the LogEntry sealed class, allowing sub data classes to extend the Message interface for predefined log levels:

Entry Description
LogEntry.Verbose Sub data class for verbose entries
LogEntry.Debug Sub data class for debug entries
LogEntry.Info Sub data class for info entries
LogEntry.Warn Sub data class for warn entries

LogEntry.Exception interface extends the LogEntry sealed class, the Error data class extends Exception interface specifically for the error log level:

Entry Description
LogEntry.Error Error entry with a tag and optional custom keys

V3 Logger Implementations

Implementation Description
MultiLogger Funnel that passes all entries to a collection of sub-loggers. Each sub-logger is responsible for filtering and processing the entry types it cares about.
LogcatLogger Logs LogEntry to Android log functions locally. Designed as a Logger specifically for log entries not ready to be logged remotely.
CrashlyticsLogger Filters out isLocalOnly entries and logs entries to Firebase crashlytics. Use for remote crash reporting and error tracking.

V3 Test Loggers

MemorisedLogger Stores entries in memory for assertions. Optionally decorates a sub-logger. Use in test fixtures with Hamcrest-matchers.

Releases

The packages can be found at here at Logging Module Packages.

The tags can be found at here at Logging Module Tags.

GA4 Migration Guide

There has been a breaking change on some of the required parameters. We support both the original and the new GA4. However you will need to import the correct type based on your need.

// GA4 type
import uk.gov.logging.api.analytics.parameters.v2.RequiredParameters
// Original type
import uk.gov.logging.api.analytics.parameters.RequiredParameters

If you need to use both GA4 and the original one in the same file, you will need to import as an alias

import uk.gov.logging.api.analytics.parameters.RequiredParameters
import uk.gov.logging.api.analytics.parameters.v2.RequiredParameters as RequiredParametersV2

Legacy

Implementation Guide One Login Mobile Application Data Schema V1.0.

GA4

Updating gradle-wrapper

Gradle secure hash algorithm (SHA) pinning is in place through the distributionSha256Sum attribute in gradle-wrapper.properties. This means the gradle-wrapper must upgrade through the ./gradlew wrapper command. Example gradle-wrapper.properties

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=2db75c40782f5e8ba1fc278a5574bab070adccb2d21ca5a6e5ed840888448046
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true

Use the following command to update the gradle wrapper. Run the same command twice, reason.

./gradlew wrapper --gradle-version=8.10.2 --distribution-type=bin --gradle-distribution-sha256-sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26

Flags:

  • gradle-version self explanatory
  • distribution-type set to bin short for binary refers to the gradle bin, often in this format gradle-8.10.2-bin.zip
  • gradle-distribution-sha256-sum the SHA 256 checksum from this page, pick the binary checksum for the version used

The gradle wrapper update can include:

  • gradle-wrapper.jar
  • gradle-wrapper.properties
  • gradlew
  • gradlew.bat

You can use the following command to check the SHA 256 checksum of a file

shasum -a 256 gradle-8.10.2-bin.zip

Hotfix changes

There are GitHub Actions workflows for a hotfix pull request and merging a hotfix to a temporary hotfix branch.

The temporary hotfix branch is currently expected to be named "temp/hotfix". If a different name is desired please edit the value under "branches:" in .github/workflows/on_push_hotfix.yml. The hotfix branch name should be in the format "hotfix/M.m.p".

Once the hotfix PR has been approved and the "Squash and merge" button pressed, the merge title must be in the format "Merge pull request #xxx from govuk-one-login/release/M.m.p" to allow for the correct version to be extracted and used as a tag.