SDK Log Reference

Every line the LinkTrail iOS and Android SDKs log, with its level and when it prints: install, consent, deferred and re-engagement links, events and state.

Both native SDKs print the same lines, at the same level, for the same step, and the React Native and Flutter wrappers print them too, because they call the native SDKs. Every line reads [LinkTrail] [LEVEL] <message>, with LEVEL DEBUG, INFO, WARNING or ERROR. Lines print only when logEnabled is on and the level is at or above logLevel. <...> marks a value filled in at runtime, and <error> is the platform's error description.

Turn them on with iOS LinkTrailOptions(logEnabled: true, logLevel: .debug) and read the Xcode console, or Android LinkTrailOptions(logEnabled = true) and run adb logcat -s LinkTrail. React Native and Flutter: pass logEnabled and logLevel in the options you give configure.

A first launch that works

INFO and up, with consentText
[LinkTrail] [INFO] Configured — base URL: https://backend.linktrail.io
[LinkTrail] [INFO] Install held — awaiting consent decision
[LinkTrail] [INFO] Consent granted
[LinkTrail] [INFO] Sending install event… (consent: true)
[LinkTrail] [INFO] Install attributed (deterministic click token)
[LinkTrail] [INFO] Deferred link routed → path: /promo/summer

Instead of Install attributed (deterministic click token) you may see Install attributed or Organic install (no attribution match). The last line prints only when there is a link to route.

Shared lines (iOS and Android)

StepLevelMessage
Configured (first call)INFOConfigured — base URL: <baseURL>
Already configured (same key and options; the instance is reused)DEBUGAlready configured — reusing the existing instance
Reconfigured (different key or options; the old instance is shut down first)WARNINGReconfigured — previous instance shut down (base URL: <baseURL>)
linkDomains entry empty or invalid, ignored (at configure)WARNINGlinkDomains entry "<value>" is not a valid host; ignored
linkDomains entry with a single label, kept (at configure)WARNINGlinkDomains entry "<value>" is a single label and will match every host under it
Install held (consent not decided yet)INFOInstall held — awaiting consent decision
Consent grantedINFOConsent granted
Consent deniedINFOConsent denied — sending the install with consent: false
Sending installINFOSending install event… (consent: <true|false>)
Attributed, deterministic click tokenINFOInstall attributed (deterministic click token)
AttributedINFOInstall attributed
OrganicINFOOrganic install (no attribution match)
Install not counted (denied), link to routeINFOInstall not counted (no consent) — routing the deferred link only
Install not counted (denied), nothing to routeINFOInstall not counted (no consent) — no deferred link to route
Deferred link routedINFODeferred link routed → path: <path>
Deferred link saved (no onLink yet)DEBUGDeferred link saved until onLink is set → <path>
Saved deferred link delivered to a late onLinkDEBUGDelivering saved deferred link → <path>
Saved deferred link expired (older than 7 days)DEBUGSaved deferred link expired — dropped
Saved deferred link unreadable (dropped)WARNINGSaved deferred link unreadable — dropped: <error>
Install already sentINFOInstall already tracked — skipping network call
Denied install already sentINFODenied install already sent — skipping network call
Install failedERRORInstall tracking failed: <error>
Not a LinkTrail linkDEBUGNot a LinkTrail link — ignoring: <url>
Handling a re-engagement linkDEBUGHandling deep link: <url>
Re-engagement resolved (no consent)INFORe-engagement resolved route-only — consent not granted (not recorded)
Re-engagement resolve failed (no consent)WARNINGRe-engagement resolve failed (no consent) — routing by URL only: <error>
Re-engagement open failed (consent)WARNINGDeep-link open failed — delivering a URL-only link (campaign/customData will be empty): <error>
Re-engagement link deliveredDEBUGDeep link resolved → path: <path>
Deep link handling failedERRORDeep link handling failed: <error>
Event dropped (no consent)INFOEvent '<name>' dropped — consent not granted
Event failed, queuedWARNINGEvent '<name>' failed; queued for retry: <error>
Event rejected (non-retryable status), droppedWARNINGEvent '<name>' rejected (<status>) — not retried
Corrupt queued event skipped (the rest are kept)WARNINGSkipped a corrupt queued event: <error>
Offline queue flushDEBUGFlushing <n> queued event(s)
Queued event still failingDEBUGQueued event <name> still failing
HTTP retryDEBUGRetrying <path> after <seconds>s (status <code>, attempt <n>)
Transport retryDEBUGTransport error, retrying (attempt <n>)
SDK state moved to device-only storage (first launch after the upgrade)DEBUGMoved SDK state to device-only storage (<n> keys)
SDK state unreadable (starts fresh)WARNINGSDK state unreadable — starting fresh: <error>
SDK state write failed (retried on the next change)WARNINGSDK state write failed: <error>
SDK state can't be read yet; install held, nothing overwrittenDEBUGSDK state not readable yet — waiting: <error>

<url> in "Not a LinkTrail link" and "Handling deep link" is the link's scheme, host and path only. The query string, fragment, user-info and port are never logged, so OAuth codes and magic-link tokens stay out of the log. "Not a LinkTrail link" prints for any URL the SDK doesn't claim — see Which links the SDK claims.

Only retryable event failures are queued: transport errors, 408, 429 and 5xx. Any other status goes to onError and is dropped with the "rejected" line, where <status> is the HTTP status (401 for a rejected API key). A queued entry that gets one on a later flush is dropped with the same line, but no onError. More on Tracking Events.

iOS only

StepLevelMessage
Click token read (paste button or .automatic)DEBUGDeferred click token read; pasteboard cleared
Pasted token ignored (clickTokenSource is .none)INFOclickTokenSource is .none — ignoring the pasted token; install stays probabilistic
Paste button tapped after a denial (default grantsConsent)INFOPaste tapped after a denial — consent stays denied (pass grantsConsent: true to override)
SKAdNetwork, iOS 16.1+DEBUGSKAdNetwork: AdAttributionKit available (iOS 16.1+); no explicit registration required
SKAdNetwork registered, iOS 15.4+INFOSKAdNetwork: registered with conversion value 0
SKAdNetwork registered, olderINFOSKAdNetwork: registered for ad network attribution
SKAdNetwork value updated (iOS 16.1+ appends the coarse value)INFOSKAdNetwork: updated conversion value <value>
SKAdNetwork errorERRORSKAdNetwork: <error>
ATT not authorizedINFOATT: not authorized
LinkTrail.resetForTesting() (printed whatever logEnabled says)INFOReset for testing — next launch will send a fresh install event
Call on an instance replaced by a later configure (ignored)DEBUGInstance shut down — ignoring call
retryPolicy.maxAttempts below 1, clampedWARNINGretryPolicy.maxAttempts <value> is below 1; using 1
retryPolicy.baseDelay not positive, defaultedWARNINGretryPolicy.baseDelay <value> is not a positive number; using <default>s
retryPolicy.maxDelay not positive, defaultedWARNINGretryPolicy.maxDelay <value> is not a positive number; using <default>s
retryPolicy.maxDelay below the base delay, raisedWARNINGretryPolicy.maxDelay <max> is below baseDelay <base>; using <base>s
requestTimeout not positive, defaultedWARNINGrequestTimeout <value> is not a positive number; using <default>s

The option-clamp warnings print only when configure creates an instance (the first call or a reconfigure), never on a repeat call that reuses it. On iOS 16.1+ the SKAdNetwork update line also carries the coarse value: SKAdNetwork: updated conversion value <value> (<coarse>).

Android only

StepLevelMessage
Consent changed while the install was in flightINFOConsent changed during the install — re-evaluating
Play Install Referrer unavailableINFOPlay install referrer unavailable — not a Play install, or lookup timed out
Play Install Referrer read (lt_click value redacted)DEBUGPlay install referrer: "<referrer>"
No lt_click token in the referrerINFONo lt_click token in referrer — organic install, no deterministic match
lt_click token found (first 4 characters and length only)DEBUGInstall referrer lt_click token: <first 4>… (<n> chars)
Install referrer connection failedDEBUGInstall referrer failed to connect: <error>
App Set ID unavailableDEBUGApp Set ID unavailable: <error>
App Set ID errorDEBUGApp Set ID error: <error>
Call on an instance replaced by a later configure (ignored)WARNING<call> ignored — this instance was replaced by a later configure; use LinkTrail.shared
Debug referrer override in use (debuggable app only; lt_click value redacted)WARNINGUsing debug referrer override "<referrer>" (debug build only; from <source>)
requestTimeoutMillis not positive, clampedWARNINGrequestTimeoutMillis (<value>) <= 0; using <default>
retryPolicy.maxAttempts below 1, clampedWARNINGretryPolicy.maxAttempts (<value>) < 1; using 1
retryPolicy.baseDelayMillis not positive, clampedWARNINGretryPolicy.baseDelayMillis (<value>) <= 0; using <default>
retryPolicy.maxDelayMillis not positive, clampedWARNINGretryPolicy.maxDelayMillis (<value>) <= 0; using <default>
retryPolicy.maxDelayMillis below the base delay, raisedWARNINGretryPolicy.maxDelayMillis (<max>) < baseDelayMillis (<base>); using <base>
Old SDK state from a backup discarded (fresh install or restore)DEBUGDiscarded SDK state restored from a backup

On Android, "Install attributed (deterministic click token)" prints when the Play Install Referrer carried an lt_click token. The referrer line prints at DEBUG with the lt_click value replaced by <redacted>, and the token line shows only its first 4 characters and length. The debug referrer override line redacts the value the same way.