Contents

mihaelamj/openapiloggingmiddleware

A flexible, configurable logging middleware for OpenAPI client and server implementations with support for console, file, and OSLog logging.

Features

  • Dual Parameter System: Separate appName and logPrefix for clean file naming and formatted console output
  • Client & Server Support: Works with both ClientMiddleware and ServerMiddleware
  • Configurable Body Logging: Control how much request/response body data to log
  • Multiple Log Handlers: Simultaneous console (stream), JSON file, and OSLog logging
  • OSLog Integration: Native Apple unified logging system support (macOS 11+, iOS 14+)
  • Automatic File Naming: Generates clean filenames from app name and prefix
  • Pluggable Handlers (v1.2.0): Ship log events to any sink via ObserverLogHandler
  • Per-Event Log Levels (v1.2.0): Configure requestLogLevel, responseLogLevel, errorLogLevel independently

Usage

Basic Usage

import OpenAPILoggingMiddleware

// Client logging
let clientMiddleware = LoggingMiddleware(
    appName: "NSSpainAPI",
    logPrefix: "🚚 APIClient: "
)

// Server logging
let serverMiddleware = await LoggingMiddleware(
    appName: "NSSpainAPI",
    logPrefix: "🖥️ ApiServer: "
)

Advanced Configuration

let middleware = LoggingMiddleware(
    logger: customLogger,                               // Optional custom logger
    bodyLoggingConfiguration: .upTo(maxBytes: 2_000_000), // Log bodies up to 2MB
    appName: "MyApp",                                   // App identifier for files
    logPrefix: "🔧 Service: ",                          // Console output prefix
    requestLogLevel: .debug,                            // v1.2.0+: per-event level
    responseLogLevel: .debug,
    errorLogLevel: .error                               // v1.2.0+: errors at .error by default
)

Extensibility (v1.2.0+)

All built-in handlers are public. Plug a custom sink (admin panel, WebSocket broadcast, analytics pipeline, test harness) alongside the defaults using Apple's Logging.MultiplexLogHandler plus the new ObserverLogHandler:

import Logging
import OpenAPILoggingMiddleware

let logger = Logger(label: "my-app") { label in
    MultiplexLogHandler([
        PrefixedStreamLogHandler(label: label, logPrefix: "🖥️ "),
        ObserverLogHandler(label: label) { event in
            // Forward every log event anywhere you like:
            MyDashboardBuffer.shared.push(event)
        }
    ])
}

let middleware = LoggingMiddleware(
    logger: logger,
    appName: "MyApp",
    logPrefix: "🖥️ "
)

You can also start from the built-in defaults and layer on top:

let base = LoggingMiddleware.defaultLogger(logPrefix: "🖥️ ")
// wrap via MultiplexLogHandler if you need to add your own handler

File Naming

The middleware automatically generates clean, organized log file names:

| appName | logPrefix | File Names | |---------|-----------|------------| | "NSSpainAPI" | "🚚 APIClient: " | _NSSpainAPI_APIClient.json, _NSSpainAPI_APIClient.log | | "NSSpainAPI" | "🖥️ ApiServer: " | _NSSpainAPI_ApiServer.json, _NSSpainAPI_ApiServer.log | | "MyApp" | "Client: " | _MyApp_Client.json, _MyApp_Client.log | | nil | "🚚 APIClient: " | _APIClient.json, _APIClient.log | | nil | "" | _OpenAPILog.json, _OpenAPILog.log |

File Naming Rules:

  1. With both appName and logPrefix: <appName><cleanPrefix>.<ext>
  2. With appName only: _<appName>.<ext>
  3. With logPrefix only: _<cleanPrefix>.<ext>
  4. Neither provided: _OpenAPILog.<ext> (default)

Clean names are generated by:

  • Trimming whitespace
  • Removing spaces from prefix
  • Filtering to ASCII letters and numbers (removes emojis)

Log Output

Console Output (Stream)

Uses the logPrefix as-is for visual distinction:

🚚 APIClient: `method` = `GET`
🚚 APIClient: `path` = `/user/me`
🚚 APIClient: `statusCode` = `200`

JSON File Output

Structured JSON logs saved to Documents directory:

[
  {
    "timestamp": "16.10.2025. 14:30:45",
    "method": "GET",
    "path": "/user/me",
    "statusCode": "200",
    "responseBody": "{...}"
  }
]

OSLog Output (macOS 11+, iOS 14+)

Native Apple unified logging system integration. Logs appear in:

  • Console.app on macOS
  • Xcode Console during development
  • System log database for persistent storage

Subsystem: com.openapi Category: logging-middleware

Viewing OSLog Output
Real-Time Log Streaming

Method 1: Terminal (Recommended for Development)

Open Terminal and run:

# Stream ALL logs from the middleware in real-time
log stream --predicate 'subsystem == "com.openapi"' --level debug

This will show logs as they happen - you'll see each API request/response immediately.

Additional real-time filters:

# Filter by specific prefix (e.g., only APIClient logs)
log stream --predicate 'subsystem == "com.openapi" AND eventMessage CONTAINS "APIClient"' --level debug

# Show only errors in real-time
log stream --predicate 'subsystem == "com.openapi"' --level error

# Filter by specific HTTP method
log stream --predicate 'subsystem == "com.openapi" AND eventMessage CONTAINS "GET"' --level debug

Method 2: Console.app (GUI)

For a visual real-time log viewer:

  1. Open Console.app (press ⌘+Space, type "Console", press Enter)
  2. In the left sidebar, select your Mac under "Devices"
  3. In the search field (top-right), type: subsystem:com.openapi
  4. Click the "Start" button to begin streaming logs in real-time
  5. Logs will appear live as your app makes API calls

Tip: Keep Console.app open while running your app to see all logs in real-time with syntax highlighting!

Historical Logs

View past logs in Terminal:

# Show logs from the last hour
log show --predicate 'subsystem == "com.openapi"' --last 1h

# Show logs with specific text
log show --predicate 'subsystem == "com.openapi" AND eventMessage CONTAINS "GET"' --last 30m

# Export logs to a file
log show --predicate 'subsystem == "com.openapi"' --last 1d --style json > logs.json

Log Level Mapping:

| swift-log Level | OSLog Type | Console.app Display | |----------------|-----------|---------------------| | .trace | .debug | Debug | | .debug | .debug | Debug | | .info | .info | Info | | .notice | .default | Default | | .warning | .error | Error | | .error | .error | Error | | .critical | .fault | Fault |

Privacy & Performance:

  • OSLog is highly optimized for performance
  • Logs are stored in a compressed binary format
  • Automatic log rotation and retention policies
  • System-level privacy controls for sensitive data

Architecture

The middleware uses Apple's Logging.MultiplexLogHandler to direct log output to multiple destinations simultaneously. By default, it's configured with:

  • PrefixedStreamLogHandler: Prints formatted logs to the console (stdout). (Renamed from StreamLogHandler in v1.2.0 to avoid collision with swift-log's own StreamLogHandler.)
  • JSONFileLogHandler: Saves structured JSON logs to a file in the app's Documents directory.
  • OSLogHandler: Sends logs to Apple's unified logging system (macOS 11+, iOS 14+).

As of v1.2.0 all four handlers (plus the new ObserverLogHandler) are public, so you can reuse them when composing your own Logger. See Extensibility above.

This architecture makes it easy to:

  • Monitor logs in real-time during development (console & OSLog)
  • Persist structured logs for analysis (JSON files)
  • Integrate with system-level logging tools (Console.app, log command)
  • Debug production issues using native Apple tools

Body Logging Policy

Control how request/response bodies are logged:

// Never log bodies (for sensitive data)
.never

// Log bodies up to specified size (default: 2MB)
.upTo(maxBytes: 1024 * 1024 * 2)

Log Metadata

Each log entry includes:

  • HTTP method
  • Request path
  • Base URL and full path
  • Request/response headers (JSON format)
  • Request/response body (based on policy)
  • Operation ID
  • Status code and reason
  • Error information (if failed)
  • Unique identifier per request

Log Destinations

File Locations

Log files are saved to the system Documents directory:

  • iOS/macOS: ~/Documents/
  • JSON logs: <appName><prefix>.json

Console Output

  • Stream logs: Output to stdout (Xcode Console, Terminal)

OSLog (macOS 11+, iOS 14+)

  • System database: Managed by logd daemon
  • Location: /var/db/diagnostics/ (system-managed, not user-accessible)
  • Access: Use Console.app or log command (see Viewing OSLog Output)

Example Integration

ApiClient

self.loggingMiddleware = LoggingMiddleware(
    appName: "NSSpainAPI",
    logPrefix: "🚚 APIClient: "
)

self.client = Client(
    serverURL: serverURL,
    transport: transport,
    middlewares: [loggingMiddleware, authMiddleware]
)

ApiServer

let loggingMiddleware = await LoggingMiddleware(
    appName: "NSSpainAPI",
    logPrefix: "🖥️ ApiServer: "
)

try handler.registerHandlers(
    on: transport,
    serverURL: serverURL,
    middlewares: [loggingMiddleware]
)

Thread Safety

The LoggingMiddleware is an actor, ensuring thread-safe operation across concurrent requests.

Dependencies

  • swift-openapi-runtime - OpenAPI runtime support
  • swift-log - Logging backend
  • HTTPTypes - HTTP types

Package Metadata

Repository: mihaelamj/openapiloggingmiddleware

Default branch: main

README: README.md