Android Logs installation

The PostHog Android SDK has built-in support for capturing structured Logs from Android apps. The SDK handles the OTLP encoding, batching, on-disk persistence across app restarts, and lifecycle integration. You just call PostHog.logger.{trace,debug,info,warn,error,fatal}(...).

Manual capture only. Logs are emitted by your code. The SDK does not autocapture system log streams (Log.d, Logcat, Timber).

Minimum version: com.posthog:posthog-android@3.46.0 or later. Bump the dependency in your build.gradle (or build.gradle.kts) and re-sync.

  1. Install posthog-android

    Required

    If you haven't installed posthog-android yet, follow the steps below. For full details, see the Android SDK guide.

    The best way to install the PostHog Android library is with a build system like Gradle. This ensures you can easily upgrade to the latest versions.

    All you need to do is add the posthog-android module to your App's build.gradle or build.gradle.kts:

    dependencies {
    implementation 'com.posthog:posthog-android:3.+'
    }

    Configuration

    The best place to initialize the client is in your Application subclass.

    Kotlin
    import android.app.Application
    import com.posthog.android.PostHogAndroid
    import com.posthog.android.PostHogAndroidConfig
    class SampleApp : Application() {
    companion object {
    const val POSTHOG_API_KEY = "<ph_project_token>"
    // usually 'https://us.i.posthog.com' or 'https://eu.i.posthog.com'
    const val POSTHOG_HOST = "https://us.i.posthog.com"
    }
    override fun onCreate() {
    super.onCreate()
    val config = PostHogAndroidConfig(
    apiKey = POSTHOG_API_KEY,
    host = POSTHOG_HOST
    )
    PostHogAndroid.setup(this, config)
    }
    }

  2. Configure logs in your PostHogAndroidConfig

    Required

    Configure Logs through config.logs before calling PostHogAndroid.setup(...). All fields are optional; defaults are tuned for mobile (cellular bandwidth, battery, app lifecycle).

    Kotlin
    import com.posthog.android.PostHogAndroid
    import com.posthog.android.PostHogAndroidConfig
    class SampleApp : Application() {
    override fun onCreate() {
    super.onCreate()
    val config = PostHogAndroidConfig(
    apiKey = "<ph_project_token>",
    host = "https://us.i.posthog.com",
    ).apply {
    logs.serviceName = "my-app" // OTLP service.name – shown in the Logs UI
    logs.environment = "production" // OTLP deployment.environment
    logs.serviceVersion = "1.2.3" // OTLP service.version
    }
    PostHogAndroid.setup(this, config)
    }
    }

    These resource attributes are captured at setup(...) and apply to every batch. Mutating config.logs.serviceName, environment, serviceVersion, or resourceAttributes after setup has no effect.

  3. Capture logs

    Required

    Use PostHog.logger for the per-level convenience API.

    Kotlin
    import com.posthog.PostHog
    import com.posthog.logs.PostHogLogSeverity
    // Per-level convenience methods
    PostHog.logger.info("checkout completed", mapOf("order_id" to "ord_789", "amount_cents" to 4999))
    PostHog.logger.warn("payment retry", mapOf("attempt" to 2))
    PostHog.logger.error("payment failed", mapOf("code" to "E001"))
    // Generic entry point for a runtime severity (e.g. mapping a Timber priority)
    PostHog.logger.log("rendered cart", severity = PostHogLogSeverity.DEBUG)

    Available severity levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL.

    Records are buffered, batched, persisted to disk, and flushed automatically – every 30 seconds, when the buffer hits the threshold, when the app moves to the background, or on PostHog.flush(). flush() drains events, Session Replay, and Logs together.

    Each record is automatically tagged with the current distinct ID, session ID, current screen, app foreground/background state, and active Feature Flags at the moment of capture.

    From Java:

    Java
    import com.posthog.PostHog;
    import com.posthog.logs.PostHogLogSeverity;
    import java.util.Map;
    PostHog.Companion.getLogger().info("checkout opened", null);
    PostHog.Companion.getLogger().error(
    "payment failed",
    Map.of("amount_cents", 1999, "currency", "USD")
    );
  4. Test your setup

    Recommended
    1. Capture a test log from your app:
      Kotlin
      PostHog.logger.info("hello from Android")
      PostHog.flush()
    2. Open the PostHog Logs UI.
    3. Filter by service.name = 'my-app' (or whatever value you set above).

    You should see your record arrive within a few seconds.

    View your Logs in PostHog
  5. Tune buffering, rate cap, and resource attributes

    Optional

    The logs config has knobs for high-volume apps:

    Kotlin
    val config = PostHogAndroidConfig(apiKey = "<ph_project_token>").apply {
    logs.serviceName = "my-app"
    logs.flushIntervalSeconds = 5 // default 30
    logs.maxBufferSize = 200 // default 1000
    logs.maxBatchSize = 50 // default 50
    logs.flushAt = 20 // default 20
    logs.rateCapMaxLogs = 5000 // default 500
    logs.rateCapWindowSeconds = 60 // default 10
    logs.resourceAttributes = mapOf("host.name" to "device-01")
    }
    PostHogAndroid.setup(this, config)

    Full configuration reference:

    FieldDefaultWhat it does
    serviceNameapp package idOTLP service.name resource attribute
    serviceVersionBuildConfig.VERSION_NAMEOTLP service.version resource attribute
    environmentnullOTLP deployment.environment resource attribute
    resourceAttributes{}Extra OTLP resource attributes (SDK keys win on collision)
    flushIntervalSeconds30Periodic flush interval
    flushAt20Buffer threshold that triggers an automatic flush
    maxBatchSize50Max records per outbound POST (halved on 413)
    maxBufferSize1000Max records held on disk before FIFO eviction
    rateCapMaxLogs500Max records per rateCapWindowSeconds window. Set to 0 to disable.
    rateCapWindowSeconds10Rate-cap tumbling window length

    serviceName, serviceVersion, environment, resourceAttributes, flushAt, and maxBatchSize are captured at setup(...); mutating them later has no effect. flushIntervalSeconds, maxBufferSize, and rate-cap fields are re-read at runtime. Defaults are tuned for cellular-aware mobile apps. Raise rateCapMaxLogs and maxBufferSize for high-volume scenarios.

  6. Filter or redact with beforeSend

    Optional

    beforeSend runs synchronously before the rate cap, so dropped records don't consume the per-window budget. Use it for redaction, sampling, or filtering by level. Each hook receives an immutable PostHogLogRecord and returns either a (possibly modified) record or null to drop it.

    Kotlin
    config.logs.addBeforeSend { record ->
    // Drop debug logs in production
    if (record.level == PostHogLogSeverity.DEBUG) return@addBeforeSend null
    // Redact secrets in the body
    record.copy(body = record.body.replace(Regex("api_key=\\S+"), "api_key=[REDACTED]"))
    }

    Call addBeforeSend multiple times to compose a chain – hooks are evaluated left-to-right (registration order). Returning null from any hook short-circuits and drops the record. A hook that throws is treated the same as returning null (the record is dropped, the exception is logged via the SDK's internal debug logger). Returning a record with a blank body also drops the record.

    addBeforeSend and removeBeforeSend are live – added or removed hooks take effect on the next captureLog call.

    From Java, register a PostHogBeforeSendLog SAM:

    Java
    config.getLogs().addBeforeSend(record ->
    record.getBody().contains("secret") ? null : record
    );
  7. Next steps

    Checkpoint
    What you can do with your logs

    ActionDescription
    Why you need logsWhat logs show you that nothing else does
    Search logsUse the search interface to find specific log entries
    Filter by levelFilter by INFO, WARN, ERROR, etc.
    Link session replayConnect logs to users and session replays by passing posthogDistinctId and sessionId
    Logging best practicesLearn what to log, how to structure logs, and patterns that make logs useful in production

    Troubleshoot common issues

Community questions

Was this page useful?

Questions about this page? or post a community question.