logger-middleware
The logger middleware logs information about each HTTP request and response, including the method, URL, status code, and response time. It is useful during development and can be configured for production logging.
Installation
The logger middleware is included with Remix. No additional installation is needed.
Import
import { logger } from 'remix/logger-middleware'API
logger(options?)
Returns a middleware function that logs request and response information.
let router = createRouter({
middleware: [
logger(),
],
})Options
| Option | Type | Default | Description |
|---|---|---|---|
format | 'dev' | 'short' | 'combined' | (info: LogInfo) => string | 'dev' | The log format. See Log Formats below. |
skip | (context: RequestContext, response: Response) => boolean | undefined | A function that returns true to skip logging for a request. |
output | (message: string) => void | console.log | Custom output function for log messages. |
Log Formats
'dev'
A concise, colorized format for development. Includes the method, URL, status code, and response time:
GET /posts 200 12ms
POST /posts 302 45ms
GET /favicon.ico 304 2msStatus codes are colorized: green for 2xx, cyan for 3xx, yellow for 4xx, and red for 5xx.
'short'
Similar to 'dev' but without colors. Suitable for production log files:
GET /posts 200 12ms'combined'
An Apache-style combined log format. Includes the remote address, timestamp, method, URL, HTTP version, status code, content length, referrer, and user agent:
127.0.0.1 - [08/Apr/2026:12:00:00 +0000] "GET /posts HTTP/1.1" 200 1234 "-" "Mozilla/5.0..."Custom Format
Pass a function to create your own format:
logger({
format(info) {
return `${info.method} ${info.url} ${info.status} ${info.responseTime}ms`
},
})The info object contains:
| Property | Type | Description |
|---|---|---|
method | string | HTTP method (GET, POST, etc.) |
url | string | Request URL path |
status | number | Response status code |
responseTime | number | Time in milliseconds from request to response |
contentLength | number | undefined | Response body size in bytes |
userAgent | string | undefined | The User-Agent header value |
referrer | string | undefined | The Referer header value |
remoteAddress | string | undefined | The client's IP address |
Examples
Development Logging
import { createRouter } from 'remix/fetch-router'
import { logger } from 'remix/logger-middleware'
let router = createRouter({
middleware: [
logger(),
],
})Skip Static File Logging
logger({
skip(context) {
return context.url.pathname.startsWith('/assets/')
},
})Production Logging
Use the 'combined' format and write to a custom output:
import { writeFileSync, appendFileSync } from 'node:fs'
logger({
format: 'combined',
output(message) {
appendFileSync('/var/log/app/access.log', message + '\n')
},
})Conditional Format by Environment
logger({
format: process.env.NODE_ENV === 'production' ? 'combined' : 'dev',
})Recommended Middleware Order
Place logger() first in the middleware chain so it captures the total response time including all downstream middleware:
let router = createRouter({
middleware: [
logger(),
compression(),
staticFiles('./public'),
csrf(),
formData(),
],
})Related
- Middleware --- How middleware works in Remix.
- async-context-middleware --- Access request context from deep utility functions for structured logging.