How to Customize Logging#

You want HAAPI logs in your existing observability backend (crash reporter, log aggregator) instead of (or in addition to) the system console. HaapiLogger exposes a unified LogSink mechanism that delivers every framework log record — Driver, SDK, and UI Layer — to your handler.

For the concept (severity ordering, follow-up tags, masking semantics), see Logging and Observability . For per-layer tag inventories, see Driver , SDK , UI Layer logging pages.

Prerequisites#

  • A working HAAPI integration.
  • An observability backend with a Kotlin / Swift SDK that accepts log messages (Crashlytics, Sentry, Datadog, internal logger — any sink with a “log this string” API).

Step-by-Step#

1. Configure the logger at app startup#

Set the level, opt in to the tags you want to see, and confirm sensitive-value masking is on.

SDK Layer#

// In AppDelegate.application(_:didFinishLaunchingWithOptions:)
HaapiLogger.followUpTags = DriverFollowUpTag.allCases + SdkFollowUpTag.allCases
HaapiLogger.setLogType(LogType.info)
// isSensitiveValueMasked defaults to true; leave it that way in production.

UI Layer#

Add the UI follow-up tags on top of the Driver + SDK sets:

HaapiLogger.followUpTags = DriverFollowUpTag.allCases
    + SdkFollowUpTag.allCases
    + UIKitFollowUpTag.allCases
HaapiLogger.setLogType(LogType.info)

Without UIKitFollowUpTag.allCases, every UI-layer record is silently suppressed — a common cause of “the SDK looks fine but the screens log nothing”.

2. Implement and register a LogSink#

The sink receives every log record with its level, follow-up tag, message, file, and line. Keep the implementation fast — sinks fire on the calling thread.

final class CrashReporterLogSink: LogSink {
    func log(
        level: LogType,
        tag: FollowUpTag,
        message: String,
        file: String,
        line: Int
    ) {
        // Forward to your crash reporter
        MyCrashReporter.log(
            level: level.rawValue,
            tag: "\(tag)",
            message: message,
            file: file,
            line: line
        )
    }
}

HaapiLogger.add(sink: CrashReporterLogSink())

3. (Optional) Filter by tag prefix inside the sink#

If you only want SDK Layer records in your crash reporter, filter inside log(...):

// Android example — same idea applies on iOS
if (!tag.startsWith("HAAPI_SDK")) return

Verify It Works#

  • Run the app and complete a HAAPI flow.
  • Your crash-reporter dashboard should show records tagged HAAPI_DRIVER_*, HAAPI_SDK_*, and (if at the UI Layer) HAAPI_UI_*.
  • Sensitive values (tokens, nonces) should appear as *****… in the message strings, confirming masking is on.

Pitfalls#

  • isSensitiveValueMasked = false + remote sink. Disabling masking and shipping logs to a remote backend leaks access tokens, refresh tokens, DPoP keys, and dpop-nonce headers off-device. Treat this combination as a hard “never” — keep masking on outside of local debugging.
  • iOS followUpTags is an emission gate. iOS records are dropped unless their tag is in HaapiLogger.followUpTags, which defaults to [] (so nothing logs until you set it). Forgetting to add SdkFollowUpTag.allCases or UIKitFollowUpTag.allCases silently suppresses entire layers. Android has no tag-based gatingenabled and setLevel(...) are the only gates, and tags travel to the sink as labels. If you only want a subset of records on Android, filter inside your LogSink.log(...) by inspecting the tag.
  • Slow sink. The sink fires on the calling thread. A sink that blocks on network I/O or grabs a lock can stall flow progression. Buffer records and ship asynchronously if your backend isn’t local.
  • Forgotten release-mode log level. Android’s setLevel defaults to VERBOSE in DEBUG builds and ERROR in release. iOS does not auto-switch. Pick the level explicitly in production builds to avoid surprises.

Was this helpful?