Skip to content

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 ​

ts
import { logger } from 'remix/logger-middleware'

API ​

logger(options?) ​

Returns a middleware function that logs request and response information.

ts
let router = createRouter({
  middleware: [
    logger(),
  ],
})

Options ​

OptionTypeDefaultDescription
format'dev' | 'short' | 'combined' | (info: LogInfo) => string'dev'The log format. See Log Formats below.
skip(context: RequestContext, response: Response) => booleanundefinedA function that returns true to skip logging for a request.
output(message: string) => voidconsole.logCustom 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 2ms

Status 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:

ts
logger({
  format(info) {
    return `${info.method} ${info.url} ${info.status} ${info.responseTime}ms`
  },
})

The info object contains:

PropertyTypeDescription
methodstringHTTP method (GET, POST, etc.)
urlstringRequest URL path
statusnumberResponse status code
responseTimenumberTime in milliseconds from request to response
contentLengthnumber | undefinedResponse body size in bytes
userAgentstring | undefinedThe User-Agent header value
referrerstring | undefinedThe Referer header value
remoteAddressstring | undefinedThe client's IP address

Examples ​

Development Logging ​

ts
import { createRouter } from 'remix/fetch-router'
import { logger } from 'remix/logger-middleware'

let router = createRouter({
  middleware: [
    logger(),
  ],
})

Skip Static File Logging ​

ts
logger({
  skip(context) {
    return context.url.pathname.startsWith('/assets/')
  },
})

Production Logging ​

Use the 'combined' format and write to a custom output:

ts
import { writeFileSync, appendFileSync } from 'node:fs'

logger({
  format: 'combined',
  output(message) {
    appendFileSync('/var/log/app/access.log', message + '\n')
  },
})

Conditional Format by Environment ​

ts
logger({
  format: process.env.NODE_ENV === 'production' ? 'combined' : 'dev',
})

Place logger() first in the middleware chain so it captures the total response time including all downstream middleware:

ts
let router = createRouter({
  middleware: [
    logger(),
    compression(),
    staticFiles('./public'),
    csrf(),
    formData(),
  ],
})

Released under the MIT License.