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
- Logger: create a logger (
myApacheLogger) with at least a console sink. - Server: standard
New-KrServer(logger doesn’t need to be default—middleware references by name). - Listener: add an HTTP endpoint (loopback:5000 by default, overridable with
-Port/-IPAddress). - Middleware:
Add-KrCommonAccessLogMiddleware -LoggerName 'myApacheLogger' -UseUtcTimestampinserts the CLF writer into the pipeline (it runs after the response is produced, capturing status & size). - Route: simple
/helloreturning text. - Start:
Enable-KrConfigurationfinalizes configuration thenStart-KrServerbegins processing. - 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:
identdanduserare-(dash) when not available (typical for most modern setups).<bytes>is the response body length in bytes (or0if no body / not measurable).- When
-UseUtcTimestampis supplied, the timestamp is rendered in UTC with+0000offset; 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
Informationlevel; 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
- Add-KrCommonAccessLogMiddleware
- New-KrLogger
- Register-KrLogger
- Add-KrSinkConsole
- Write-KrTextResponse
- New-KrServer
- Add-KrMapRoute
- Start-KrServer
Previous / Next
Go back to Hot Reload or continue to Logging (Concepts).