# Logging Mechanisms in Apple Container: Swift-Log Architecture and Handler Implementation

> Explore Apple Container's logging mechanisms powered by Swift-Log. Discover OSLogHandler, FileLogHandler, and StderrLogHandler for versatile log routing. Learn how ServiceLogger.bootstrap integrates these handlers.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: internals
- Published: 2026-06-27

---

**Apple Container utilizes the Swift-Log ecosystem with three concrete `LogHandler` implementations—`OSLogHandler`, `FileLogHandler`, and `StderrLogHandler`—that are selected at runtime via the `ServiceLogger.bootstrap` helper to route logs to the system log, files, or standard error.**

The Apple Container project implements a flexible, pluggable logging architecture built on the Swift-Log framework. This repository centralizes all diagnostic output through a custom bootstrap mechanism that dynamically selects between system logging, file persistence, and console output based on runtime configuration. Understanding these logging mechanisms reveals how the container runtime handles observability across different execution contexts.

## Log Handler Architecture

The logging infrastructure rests on three concrete implementations of the `LogHandler` protocol defined in the `ContainerLog` module. Each handler targets a specific output destination and is optimized for different operational scenarios.

### OSLogHandler (System Logging)

Defined in [`Sources/ContainerLog/OSLogHandler.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/OSLogHandler.swift), this handler wraps Apple's Unified Logging system (`os.Logger`). It translates Swift-Log's `Logging.Logger.Level` into appropriate `OSLogType` values—mapping debug levels to `debug`, `info`, `default`, `error`, and `fault` types. This handler serves as the default mechanism when no explicit log file path is supplied to the bootstrap process.

### FileLogHandler (Persistent Storage)

Located in [`Sources/ContainerLog/FileLogHandler.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/FileLogHandler.swift), this handler writes formatted log entries to the filesystem. It manages log-level filtering, metadata attachment, and file hierarchy creation under the specified directory path. The handler is instantiated exclusively when a `logPath` parameter is successfully passed to `ServiceLogger.bootstrap`.

### StderrLogHandler (Console Output)

Implemented in [`Sources/ContainerLog/StderrLogHandler.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/StderrLogHandler.swift), this handler emits output to the process's standard error stream. It is primarily utilized for interactive CLI debugging and by test utilities where immediate console feedback is required, rather than for long-running service logging.

## ServiceLogger Bootstrap Configuration

The `ServiceLogger.bootstrap` method in [`Sources/ContainerLog/ServiceLogger.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/ServiceLogger.swift) serves as the central entry point for configuring the logging subsystem. This static method initializes the `LoggingSystem` with a factory closure that selects the appropriate handler based on runtime parameters.

```swift
public struct ServiceLogger {
    public static func bootstrap(
        label: String = "com.apple.container",
        category: String,
        metadata: [String: String] = [:],
        debug: Bool,
        logPath: FilePath?
    ) -> Logger {
        LoggingSystem.bootstrap { label in
            if let logPath, let handler = try? FileLogHandler(label: label,
                                                             category: category,
                                                             path: logPath) {
                return handler               // → FileLogHandler
            }
            return OSLogHandler(label: label, category: category) // → OSLogHandler
        }

        var log = Logger(label: label)
        if debug { log.logLevel = .debug }
        metadata.forEach { log[metadataKey: $0] = $1 }
        return log
    }
}

```

The selection logic follows this priority:

1. If `logPath` is provided and the `FileLogHandler` instantiates successfully, all output routes to the specified directory.
2. If the file handler cannot be created (e.g., due to permission issues), the system logs an error and falls back to `OSLogHandler`.
3. When no path is specified, the system defaults immediately to `OSLogHandler`.

## Usage Patterns Across the Codebase

Components throughout the repository receive `Logger` instances via dependency injection or direct instantiation, utilizing the standard Swift-Log API (`log.info`, `log.debug`, `log.error`, etc.). Subsystems like `SocketForwarder`, API servers, and CLI command implementations inject the logger to maintain consistent, testable behavior.

### Creating a Service Logger

To initialize logging for a service such as the DNS server using system logs:

```swift
import Logging
import ContainerLog

let dnsLogger = ServiceLogger.bootstrap(
    category: "dns",
    debug: true,
    logPath: nil)                 // → OSLogHandler (system log)
dnsLogger.info("DNS server started")

```

### Enabling File-Based Logging

For long-running container operations requiring persistent logs:

```swift
import Logging
import ContainerLog
import SystemPackage

let logDir = try FilePath("/var/log/container")
let fileLogger = ServiceLogger.bootstrap(
    category: "container",
    debug: false,
    logPath: logDir)              // → FileLogHandler (writes to files)
fileLogger.debug("Initializing container")   // filtered out unless debug is true
fileLogger.error("Failed to start VM")

```

### Injecting Loggers into Components

The `UDPForwarder` and similar components accept loggers via initialization parameters:

```swift
public final class UDPForwarder {
    private let log: Logger?

    init(log: Logger? = nil) {
        self.log = log
    }

    func forward(_ packet: Data) {
        log?.trace("Forwarding UDP packet of size \(packet.count)")
        // … actual forwarding logic …
    }
}

```

Key files implementing these patterns include:
- [`Sources/ContainerLog/OSLogHandler.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/OSLogHandler.swift) – Unified Logging integration
- [`Sources/ContainerLog/FileLogHandler.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/FileLogHandler.swift) – Filesystem persistence
- [`Sources/ContainerLog/StderrLogHandler.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/StderrLogHandler.swift) – Console output handling
- [`Sources/ContainerLog/ServiceLogger.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/ServiceLogger.swift) – Bootstrap configuration
- [`Sources/SocketForwarder/UDPForwarder.swift`](https://github.com/apple/container/blob/main/Sources/SocketForwarder/UDPForwarder.swift) – Logger injection examples

## Summary

- **Apple Container** implements three specialized `LogHandler` types: `OSLogHandler` for system logs, `FileLogHandler` for disk persistence, and `StderrLogHandler` for console debugging.
- The `ServiceLogger.bootstrap` method in [`Sources/ContainerLog/ServiceLogger.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/ServiceLogger.swift) serves as the central configuration point, selecting handlers based on the presence of a `logPath` parameter.
- Runtime selection prioritizes file logging when valid paths are provided, falling back to `OSLogHandler` on failure or when no path is specified.
- All components utilize the standard Swift-Log API with dependency injection patterns, ensuring consistent observability across the container runtime.

## Frequently Asked Questions

### What logging library does Apple Container use?

Apple Container uses the **Swift-Log** ecosystem (`apple/swift-log`) as its foundational logging framework. The repository provides custom `LogHandler` implementations that wrap specific output destinations like `os.Logger` (Unified Logging), files, and standard error streams.

### How do I configure file-based logging in Apple Container?

Pass a valid `FilePath` to the `logPath` parameter when calling `ServiceLogger.bootstrap`. If the path is accessible and writable, the system instantiates `FileLogHandler` from [`Sources/ContainerLog/FileLogHandler.swift`](https://github.com/apple/container/blob/main/Sources/ContainerLog/FileLogHandler.swift) and persists all log entries to that directory. If the path is invalid, the system automatically falls back to `OSLogHandler`.

### What happens if the log file path has permission issues?

If `FileLogHandler` initialization fails due to permissions or other filesystem errors, the bootstrap mechanism catches the exception and defaults to `OSLogHandler`. The system logs an error message indicating the file handler creation failure before continuing with system logging.

### How can I enable debug-level logging across the container runtime?

Set the `debug` parameter to `true` when invoking `ServiceLogger.bootstrap`. This sets the logger's `logLevel` property to `.debug`, allowing trace and debug messages to flow through the active handler. Without this flag, debug messages are filtered out at the logger level regardless of the handler's capabilities.