> ## Documentation Index
> Fetch the complete documentation index at: https://docs.westyx.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin SDK - Logging

> SLF4J integration and the secret-hygiene guarantee in the Kotlin SDK.

The SDK logs through **SLF4J**, the JVM ecosystem's standard logging abstraction. There is no
bespoke logger interface to implement.

## Enabling it

Nothing to enable. `NexusConfig.logger` defaults to the SLF4J logger named `dev.westyx.nexus`,
and SLF4J is a no-op unless the application binds a backend - so an application that has not
asked for logging stays silent.

To route SDK output somewhere specific, pass your own logger:

```kotlin theme={null}
import org.slf4j.LoggerFactory

NexusClient.create(NexusConfig(
    baseUrl = "https://my-service.westyx.dev",
    apiKey  = System.getenv("NEXUS_API_KEY"),
    logger  = LoggerFactory.getLogger("my-app.nexus"),
))
```

With the [Ktor plugin](/sdks/kotlin/ktor-plugin) the default is the Ktor application's own logger, so SDK
output lands in the application log with no configuration at all.

## What is logged

| Level   | Events                                                                                                                                                                                                                                                                             |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `info`  | client ready (endpoint, key type, TTL, whether WIF is active); SSE stream connected; SSE stream closed by the server                                                                                                                                                               |
| `warn`  | sync failed and the cache is being served; billing blocked the refresh; quarantine in force with its deadline; SSE transport error; fallback to TTL polling with the next attempt time; a config value that does not fit the requested type; an absorbed observer-callback failure |
| `debug` | sync applied, with entry counts; sync not modified (304); WIF provider resolution and session refresh, with the session lifetime; reconnect attempt and its backoff; `connectStream()` ignored because a stream is already running; client closed                                  |

Values are passed as **structured parameters**, never interpolated into the message:

```kotlin theme={null}
log.warn("nexus: SSE fell back to TTL polling; next attempt in {}ms", wait)
```

A message that embeds its values produces a unique string per occurrence, which cannot be grouped
or queried in a log aggregator - and it also defeats SLF4J's level check, since the string is
built whether or not the level is enabled.

## Secret hygiene

**No log record carries a secret value, a secret key name, an API key, a session JWT or a
workload OIDC token.** Where a key must be identified - a file secret that could not be
materialised, for instance - only a truncated hash of the key appears.

This is enforced, not just intended. `LoggingSecretLeakTest` drives a sync carrying both a text
and a file-type secret, a secret read, a file-path read and a WIF token exchange through a
capturing logger with **every level enabled**, then fails if any seeded value, key name, API key
or token appears anywhere in the output - message, parameters or stack trace.

`NexusConfig.toString()` also redacts `apiKey`, since a config object is often printed while
debugging.

## Observer failures

A `NexusObserver` callback that throws is absorbed by the SDK's guarded dispatcher and reported
here at `warn`, naming the callback:

```
nexus: observer callback onConnected threw and was absorbed
```

Absorbing without reporting would be the worse failure - the stream would carry on while the
application's own instrumentation silently stopped working. See [Stream observer](/sdks/kotlin/stream-observer).
