Apache Common Access Log

Add structured request logging in the classic Apache Common Log Format (CLF) (also called Common Access Log). This middleware writes a single line per completed HTTP request using the logger you specify.

Prerequisites: complete earlier logging chapters (Simple Logging → Hot Reload) so you know how to build and register loggers. Ensure you have a console or file sink attached to the logger used below so you can actually see the emitted lines.

Full source

File: pwsh/tutorial/examples/5.7-ApacheLog.ps1

<#
    Sample PowerShell script to create a simple Kestrun server with Apache Common Log Format logging
    This script sets up a Kestrun server that responds with "Hello, World!" and logs requests in Apache common log format.
    FileName: 5.7-ApacheLog.ps1
#>
param(
    [int]$Port = 5000,
    [IPAddress]$IPAddress = [IPAddress]::Loopback
)

New-KrLogger |
    Set-KrLoggerLevel -Value Debug |
    Add-KrSinkConsole |
    Add-KrSinkFile -Path '.\logs\apache_access.log' -RollingInterval Day |
    Register-KrLogger -Name 'myApacheLogger'

# Create a new Kestrun server
New-KrServer -Name "Simple Server"

# HTTP listener (optional)
Add-KrEndpoint -Port $Port -IPAddress $IPAddress

# Add the PowerShell runtime


# Add Apache Common Log Format middleware
Add-KrCommonAccessLogMiddleware -LoggerName 'myApacheLogger' -UseUtcTimestamp

# Enable Kestrun configuration
Enable-KrConfiguration

# Map the route
Add-KrMapRoute -Verbs Get -Pattern "/hello" -ScriptBlock {
    Write-KrTextResponse -InputObject "Hello, World!" -StatusCode 200
    # Or the shorter version
    # Write-KrTextResponse "Hello, World!"
}

# Start the server asynchronously
Start-KrServer -CloseLogsOnExit

Step-by-step

  1. Logger: create a logger (myApacheLogger) with at least a console sink.
  2. Server: standard New-KrServer (logger doesn’t need to be default—middleware references by name).
  3. Listener: add an HTTP endpoint (loopback:5000 by default, overridable with -Port / -IPAddress).
  4. Middleware: Add-KrCommonAccessLogMiddleware -LoggerName 'myApacheLogger' -UseUtcTimestamp inserts the CLF writer into the pipeline (it runs after the response is produced, capturing status & size).
  5. Route: simple /hello returning text.
  6. Start: Enable-KrConfiguration finalizes configuration then Start-KrServer begins processing.
  7. Observe: each request produces one CLF line on the chosen logger.

Output format

The middleware emits the canonical Common Log Format:

<remote-host> <identd> <user> [<timestamp>] "<method> <path> <protocol>" <status> <bytes>

Example:

127.0.0.1 - - [29/Sep/2025:18:42:10 +0000] "GET /hello HTTP/1.1" 200 13

Notes:

  • identd and user are - (dash) when not available (typical for most modern setups).
  • <bytes> is the response body length in bytes (or 0 if no body / not measurable).
  • When -UseUtcTimestamp is supplied, the timestamp is rendered in UTC with +0000 offset; otherwise local time.
  • The logger’s sinks control where the line goes (console, file, etc.).

Comparison to structured logs

CLF is line-oriented and designed for legacy log analyzers. For richer analytics (properties, correlation IDs, JSON sinks), use structured logging covered in earlier chapters. You can run both simultaneously: keep structured logs for internal analysis and enable CLF solely for interoperability with existing tooling.

Try it

Run the sample, then issue a few requests:

pwsh ./5.7-ApacheLog.ps1
curl http://127.0.0.1:5000/hello
curl http://127.0.0.1:5000/hello

Expected console output (lines similar to):

127.0.0.1 - - [29/Sep/2025:18:42:10 +0000] "GET /hello HTTP/1.1" 200 13
127.0.0.1 - - [29/Sep/2025:18:42:12 +0000] "GET /hello HTTP/1.1" 200 13

If you direct the logger to a rolling file sink you can tail it:

Get-Content .\logs\*apache*.log -Tail 20 -Wait

(Adjust to match your configured path/pattern.)

Customization

Parameter Effect Notes
-LoggerName Which registered logger to use Must exist; use Register-KrLogger first
-Logger Pass the logger instance directly Alternative to -LoggerName
-UseUtcTimestamp Forces UTC timestamps with +0000 Omit for local timezone
-Filter (If available in future) limit which requests log Not yet implemented (roadmap)

Combine with standard logger features:

  • Enrichment: add properties (e.g. hostname) – though CLF output itself stays unchanged.
  • Multiple sinks: console + file for archival.
  • Level override: CLF lines typically use Information level; adjust minimum if you previously set higher (e.g. Warning).

Troubleshooting

Symptom Cause Fix
No CLF lines Wrong logger name Verify name passed to Register-KrLogger -Name matches -LoggerName
No output anywhere Logger lacks console/file sink Add Add-KrSinkConsole or a file sink before registering
Timestamp not UTC -UseUtcTimestamp missing Add -UseUtcTimestamp switch
Bytes always 0 Response written manually/streamed Ensure Write-Kr*Response helpers or set Content-Length
Duplicate lines Middleware added twice Remove duplicate Add-KrCommonAccessLogMiddleware calls

References


Previous / Next

Go back to Hot Reload or continue to Logging (Concepts).