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.