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”.
SDK Layer#
// In Application.onCreate
HaapiLogger.enabled = true
HaapiLogger.setLevel(HaapiLogger.LogLevel.INFO)
// isSensitiveValueMasked defaults to true; leave it that way in production.UI Layer#
Same call — the UI-layer records flow through alongside Driver and SDK once the logger is enabled. Filter at the sink (next step) or by LogCat tag prefix (logcat -s 'HAAPI_UI_*:V').
React Native has only an SDK Layer surface. getRNLogger is a singleton factory — call it once at startup with the configuration; subsequent calls return the same instance. The configuration is forwarded to the native iOS and Android loggers atomically:
import { getRNLogger, RNLogLevel, initializeForHaapi } from 'identityserver.haapi.reactnative.sdk'
const logger = getRNLogger({
logLevel: RNLogLevel.Info, // Info | Debug | Trace | Warn | Error
isSensitiveValueMasked: true, // keep true outside local development
})
const accessor = await initializeForHaapi(haapiConfig, logger)Records emitted by both native layers (iOS and Android) reach the JS side through a single event channel. Tag prefixes (HAAPI_DRIVER_*, HAAPI_SDK_*) survive the bridge — filter them in your sink, not at the native side.
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()) class CrashReporterLogSink : LogSink {
override fun log(
level: HaapiLogger.LogLevel,
tag: String,
message: String,
file: String,
line: Int
) {
// Forward to your crash reporter
MyCrashReporter.log(level.name, tag, message, file, line)
}
}
HaapiLogger.addSink(CrashReporterLogSink()) import type { RNLogEvent } from 'identityserver.haapi.reactnative.sdk'
const sinkHandle = logger.addSink((event: RNLogEvent) => {
// Forward to your crash reporter / analytics pipeline.
MyCrashReporter.log(event.level, event.tag, event.message, event.file, event.line)
// Keep this fast — sinks fire on the JS thread synchronously.
})
// Later: sinkHandle.remove() to detach the sink.RN sinks run on the JS thread (unlike native sinks, which run on the calling native thread). For heavy work — network I/O, large serialisation — post to a worker or queue and return immediately.
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, anddpop-nonceheaders off-device. Treat this combination as a hard “never” — keep masking on outside of local debugging.- iOS
followUpTagsis an emission gate. iOS records are dropped unless their tag is inHaapiLogger.followUpTags, which defaults to[](so nothing logs until you set it). Forgetting to addSdkFollowUpTag.allCasesorUIKitFollowUpTag.allCasessilently suppresses entire layers. Android has no tag-based gating —enabledandsetLevel(...)are the only gates, and tags travel to the sink as labels. If you only want a subset of records on Android, filter inside yourLogSink.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
setLeveldefaults toVERBOSEinDEBUGbuilds andERRORin release. iOS does not auto-switch. Pick the level explicitly in production builds to avoid surprises.