Logging

The HTTP request logger middleware and the pino logger instance used by all module internals, including registration, output format, redaction, and log levels.

The module ships two logging mechanisms: a structured HTTP request logger, and a pino logger instance used by all internal operations. Both are available for use in your own handlers.


HTTP logger

httpLogger() is an H3 plugin that attaches structured request and response logging to your app. It hooks into onRequest, onResponse, and onError to capture the full lifecycle of every request.

Register it during startup. When using the Nuxt module, registration happens automatically.

import { createApp } from 'h3'
import { httpLogger } from 'auth-h3client/v1'

const app = createApp()
httpLogger()(app)

Logs output

On each request, the logger records:

  • Request ID (x-request-id): read from the incoming header if present, otherwise a random UUID. Set on the response and stored on event.context.rid for use by other middleware and handlers.
  • Method, URL path, query string, and full host URL
  • Client IP address and User-Agent
  • Request fingerprint via getRequestFingerprint
  • All request headers and cookies (both redacted to [SECRET] in the log output)
  • Referrer

On each response, it records the status code, response headers, content type, content length, and latency in milliseconds.

On errors, it records the status, error name, message, stack trace, and error body.

Log level selection

The HTTP logger selects the level for each response automatically:

Status rangeLevel
5xx or error contexterror
4xxwarn
2xx / 3xxinfo

Output destination

Log output is written to logs/http.log as newline-delimited JSON using a pino file transport. The logs/ directory is created automatically at the nearest package.json root relative to the process working directory. Set the CLIENT_LOG_DIR environment variable to write logs to a different path. This logger is separate from the general getLogger() instance, but its minimum level is controlled by the same logLevel configuration field.

Log entry shape

Each request produces an entry at onRequest and a second entry at onResponse. The structure looks like:

{
  "level": 30,
  "time": "2026-04-12T10:00:00.000Z",
  "uptime": 120.5,
  "requestId": "a1b2c3d4-...",
  "ip": "1.2.3.4",
  "userAgent": "Mozilla/5.0 ...",
  "FullUrl": "app.example.com/api/profile",
  "fingerPrints": "...",
  "referrer": "https://app.example.com/",
  "httpRequest": {
    "method": "POST",
    "url": "/api/profile",
    "headers": "[SECRET]",
    "remoteAddress": "1.2.3.4"
  },
  "msg": "request start"
}

The response entry adds latencyMS and an httpResponse block with statusCode, statusText, resHeaders, contentType, contentLength, and type. Error entries use an httpError block with status, statusText, body, data, name, message, and stack.

The authorization header and all cookies are redacted to [SECRET] before the entry is written.

Skipped paths

The logger skips static asset paths (.css, .js, .png, .jpg, .svg, .ico, .woff, .ttf, .webp, .json, .map) and /.well-known/ paths to avoid noise in the log output.


getLogger

getLogger() returns the pino logger instance used internally by the module. Use it in custom handlers to write structured log entries in the same format as module logs.

server/api/profile.get.ts
export default defineAuthenticatedEventHandler(async (event) => {
  const log = getLogger().child({ service: 'profile', route: '/api/profile' })

  const { userId } = event.context.authorizedData
  log.info({ userId }, 'Fetching profile')

  return await getProfile(userId)
})

Child loggers inherit the parent log level and transport, and add the fields from the child bindings to every entry they produce.

Output is written to three files under the logs/ directory at the nearest package.json root relative to the process working directory: info.log (info and above), warn.log (warn and above), and errors.log (error and above). Set CLIENT_LOG_DIR to write to a different path.


Log levels

The logLevel field in the configuration controls the minimum severity level emitted by getLogger(). Available levels:

LevelWhen to use
debugDevelopment. Logs every internal step including token checks and cache hits
infoDefault. Logs route executions, token rotations, and key authentication events
warnLogs only potential issues such as rate limits, MFA challenges, and bot flags
errorLogs errors only
fatalLogs only unrecoverable failures

Set logLevel: 'debug' during development to see the full authentication flow. Use info or warn in production.

Logo