# Configuring Logging


> Capture detailed information about errors and request processing in log files, either locally or via syslog.


Logging is essential for troubleshooting and understanding how traffic flows through your infrastructure. This article explains how to configure error and access logs in NGINX and NGINX Plus, customize access log formats, reduce noise with conditional logging, and forward logs to syslog for centralized collection.

## Set up the error log {#error_log}

NGINX writes an error log that records issues of different severity levels. The [`error_log`](https://nginx.org/en/docs/ngx_core_module.html#error_log) directive sets up the log location and severity level. The log location can be a particular file, `stderr`, or `syslog`.

### Default path

By default, the error log is located at **logs/error.log**, but the absolute path depends on the operating system and installation method. You can find the path to error log by running `nginx -V` command and extracting the value of `--error-log-path=`:

```shell
nginx -V 2>&1|sed -n 's/.*--error-log-path=\([^ ]*\).*/\1/p'
```
The output depends on the operating system.

- For RHEL-based, Debian, Ubuntu:

```shell
/var/log/nginx/error.log
```
- For FreeBSD:
```shell
/usr/local/var/log/nginx/error.log
```

- For docker images, it is sent to container logs (stderr) via symlinks `/var/log/nginx/error.log -> /dev/stderr`.

### Severity level

The error severity level follows the `syslog` classification system. The log includes messages from all severity levels above the specified level.

The following example changes the minimal severity level of error messages to log from `error` to `warn`:

```nginx
error_log logs/error.log warn;
```

With this setting, messages above the `warn` level are logged. That includes `warn`, `error`, `crit`, `alert`, and `emerg` levels.

For debug logging, see [Debugging NGINX](debugging.md).

### Error log formats

The `error_log` directive writes error messages in the standard NGINX text error-log format. In NGINX Plus, error log also supports structured JSON output via a [`json`](https://nginx.org/en/docs/ngx_core_module.html#error_log_json) parameter:

```nginx
error_log /var/log/nginx/error.json error json;
```

Example of JSON-formatted error entry:

```json
{
  "level": "error",
  "timestamp": "2026-05-04T10:30:15.042+00:00",
  "pid": 12345,  "tid": 12345,  "cnum": 3,
  "msg": "connect() failed",
  "client": "192.168.1.10",  "server": "example.com",
  "request": "GET /api HTTP/1.1",
  "upstream": "http://127.0.0.1:8080/api",
  "errno": 111,
  "errtext": "Connection refused"
}
```

### Error log inheritance

The default setting of the error log works globally. To override it, place the [`error_log`](https://nginx.org/en/docs/ngx_core_module.html#error_log) directive in the `main` (top-level) configuration context. Settings in the `main` context are always inherited by other configuration levels (`http`, `server`, `location`). The `error_log` directive can also be specified on the [`http`](https://nginx.org/en/docs/http/ngx_http_core_module.html#http), [`stream`](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream), `server` and [`location`](https://nginx.org/en/docs/http/ngx_http_core_module.html#location) levels. Settings at lower levels override the settings inherited from the higher levels. Each error message is written only once to the error log closest to the level where the error has occurred. However, if several `error_log` directives are specified on the same level, the message is written to all specified logs.

## Set up the access log {#access_log}

NGINX records client requests in the access log right after the request is processed. The [`access_log`](https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log) directive specifies the location of the log and its format. By default, the access log is located at **logs/access.log**. The format of logged messages is the predefined **combined** format. To change the format of logged messages, use the [log_format](https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format) directive. The log format is defined using variables.

The following example extends the predefined **combined** format by specifying the `gzip ratio` keyword. This enables gzip compression of the response in a virtual server.

```nginx
http {
    log_format compression '$remote_addr - $remote_user [$time_local] '
                           '"$request" $status $body_bytes_sent '
                           '"$http_referer" "$http_user_agent" "$gzip_ratio"';

    server {
        gzip on;
        access_log /spool/logs/nginx-access.log compression;
        ...
    }
}
```

Another example of the log format enables tracking different time values between NGINX and an upstream server. This may help with diagnosing a slowdown of your website. You can use the following variables to log the indicated time values:

- [`$upstream_connect_time`](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#var_upstream_connect_time) – The time spent on establishing a connection with an upstream server
- [`$upstream_header_time`](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#var_upstream_header_time) – The time between establishing a connection and receiving the first byte of the response header from the upstream server
- [`$upstream_response_time`](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#var_upstream_response_time) – The time between establishing a connection and receiving the last byte of the response body from the upstream server
- [`$request_time`](https://nginx.org/en/docs/http/ngx_http_log_module.html#var_request_time) – The total time spent processing a request

All time values are measured in seconds with millisecond resolution.

```nginx
http {
    log_format upstream_time '$remote_addr - $remote_user [$time_local] '
                             '"$request" $status $body_bytes_sent '
                             '"$http_referer" "$http_user_agent"'
                             'rt="$request_time" uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';

    server {
        access_log /spool/logs/nginx-access.log upstream_time;
        ...
    }
}
```

When reading the resulting time values, keep the following in mind:

- When a request is processed through several servers, the variable contains several values separated by commas
- When there is an internal redirect from one upstream group to another, the values are separated by semicolons
- When a request is unable to reach an upstream server or a full header cannot be received, the variable contains `0` (zero)
- In case of internal error while connecting to an upstream or when a reply is taken from the cache, the variable contains `-` (hyphen)

The log can be optimized by enabling the `buffer` and the `cache`. With the [buffer](https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log) parameter enabled, messages will be stored in the buffer first. When the buffer is full (or in some other [cases](https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log)), the messages will be written to the log.
To enable caching of log file descriptors, use the [open_log_file_cache](https://nginx.org/en/docs/http/ngx_http_log_module.html#open_log_file_cache) directive.

Similar to the `error_log` directive, the [access_log](https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log) directive defined on a particular configuration level overrides the settings from the previous levels. When processing of a request is completed, the message is written to the log that is configured on the current level, or inherited from the previous levels. If one level has multiple access log definitions, the message is written to all of them.

## Enabling conditional logging {#conditional}

Conditional logging allows excluding trivial or unimportant log entries from the access log. In NGINX, conditional logging is enabled by the `if` parameter to the [access_log](https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log) directive.

This example excludes requests with HTTP status codes `2xx` (Success) and `3xx` (Redirection):

```nginx
map $status $loggable {
    ~^[23]  0;
    default 1;
}

access_log /path/to/access.log combined if=$loggable;
```

## Usecase: sampling TLS parameters {#tls_sample}

Many clients use TLS versions older than TLS 1.3. Though many ciphers are declared insecure, older implementations still use them; ECC certificates offer greater performance than RSA, but not all clients can accept ECC. Many TLS attacks rely on a “man in the middle” who intercepts the cipher negotiation handshake and forces the client and server to select a less secure cipher. Therefore, it’s important to configure F5 NGINX Plus to not support weak or legacy ciphers, but doing so may exclude legacy clients.

You can evaluate the SSL data obtained from the client and determine what proportion of clients get excluded if support for older SSL protocols and ciphers is removed.

The following configuration example logs the SSL protocol, cipher, and `User-Agent` header of any connected TLS client, assuming that each client selects the most recent protocol and most secure ciphers it supports.

In this example, each client is identified by its unique combination of IP address and User-Agent.

1. Define the custom log format `sslparams` that includes the version of the SSL protocol ([`$ssl_protocol`](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_protocol)), ciphers used in the connection ([`$ssl_cipher`](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_cipher)), the client IP address ([`$remote_addr`](http://nginx.org/ru/docs/http/ngx_http_core_module.html#var_remote_addr)), and the value of standard `User Agent` HTTP request field (`$http_user_agent`):

   ```nginx
   log_format sslparams '$ssl_protocol $ssl_cipher '
                     '$remote_addr "$http_user_agent"';
   ```

2. Define a key-value storage that will keep the IP address of the client and its User Agent, for example, `clients`:

   ```nginx
   keyval_zone zone=clients:80m timeout=3600s;
   ```

3. Create a variable, for example, `$seen` for each unique combination of `$remote_addr` and `User-Agent` header:

   ```nginx
   keyval $remote_addr:$http_user_agent $seen zone=clients;

   server {
       listen 443 ssl;

       ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
       ssl_ciphers   HIGH:!aNULL:!MD5;

       if ($seen = "") {
           set $seen  1;
           set $logme 1;
       }
       access_log  /tmp/sslparams.log sslparams if=$logme;

       # ...
   }
   ```

4. View the log file generated with this configuration:

   ```none
   TLSv1.2 AES128-SHA 1.1.1.1 "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0"
   TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 2.2.2.2 "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
   TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 3.3.3.3 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:58.0) Gecko/20100101 Firefox/58.0"
   TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 4.4.4.4 "Mozilla/5.0 (Android 4.4.2; Tablet; rv:65.0) Gecko/65.0 Firefox/65.0"
   TLSv1 AES128-SHA 5.5.5.5 "Mozilla/5.0 (Android 4.4.2; Tablet; rv:65.0) Gecko/65.0 Firefox/65.0"
   TLSv1.2 ECDHE-RSA-CHACHA20-POLY1305 6.6.6.6 "Mozilla/5.0 (Linux; U; Android 5.0.2; en-US; XT1068 Build/LXB22.46-28) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.2.1164 Mobile Safari/537.36"
   ```

5. Process the log file to determine the spread of data:

    ```shell
   cat /tmp/sslparams.log | cut -d ' ' -f 2,2 | sort | uniq -c | sort -rn | perl -ane 'printf "%30s %s\n", $F[1], "="x$F[0];'
   ```

   In this output, low‑volume, less secure ciphers are identified:

   ```shell
   ECDHE-RSA-AES128-GCM-SHA256 =========================
   ECDHE-RSA-AES256-GCM-SHA384 ========
                    AES128-SHA ====
   ECDHE-RSA-CHACHA20-POLY1305 ==
       ECDHE-RSA-AES256-SHA384 ==
   ```

   Then you can check the logs to determine which clients are using these ciphers and then make a decision about removing these ciphers from the NGINX Plus configuration.

   For more information about sampling requests with NGINX conditional logging see the [blog post](https://www.nginx.com/blog/sampling-requests-with-nginx-conditional-logging/#var_request_id).

## Logging to syslog {#syslog}

The `syslog` utility is a standard for computer message logging and allows collecting log messages from different devices on a single syslog server. In NGINX, logging to syslog is configured with the `syslog:` prefix in [error_log](https://nginx.org/en/docs/ngx_core_module.html#error_log) and [access_log](https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log) directives.

Syslog messages can be sent to a `server=` which can be a domain name, an IP address, or a UNIX-domain socket path. A domain name or IP address can be specified with a port to override the default port, `514`. A UNIX-domain socket path can be specified after the `unix:` prefix:

```nginx
error_log  syslog:server=unix:/var/log/nginx.sock debug;
access_log syslog:server=[2001:db8::1]:1234,facility=local7,tag=nginx,severity=info;
```

In the example, NGINX error log messages are written to a UNIX domain socket at the `debug` logging level, and the access log is written to a syslog server with an IPv6 address and port `1234`.

The `facility=` parameter specifies the type of program that is logging the message. The default value is `local7`. Other possible values are: `auth`, `authpriv`, `daemon`, `cron`, `ftp`, `lpr`, `kern`, `mail`, `news`, `syslog`, `user`, `uucp`, `local0 ... local7`.

The `tag=` parameter applies a custom tag to syslog messages (`nginx` in our example).

The `severity=` parameter sets the severity level of syslog messages for access log. Possible values in order of increasing severity are: `debug`, `info`, `notice`, `warn`, `error` (default), `crit`, `alert`, and `emerg`. Messages are logged at the specified level and all more severe levels. In our example, the severity level `error` also enables `crit`, `alert`, and `emerg` levels to be logged.

## Live activity monitoring {#monitoring}

NGINX Plus provides a real-time live activity monitoring interface that shows key load and performance metrics of your [HTTP](nginx/admin-guide/load-balancer/http-load-balancer.md) and [TCP](nginx/admin-guide/load-balancer/tcp-udp-load-balancer.md) upstream servers. See the [Live Activity Monitoring](live-activity-monitoring.md) article for more information.

## Compatibility note {#compat}

- Support for multiple `error_log` directives at the same configuration level was introduced in NGINX Open Source [1.5.2](https://nginx.org/en/CHANGES).

- Support for JSON-formatted error logs was introduced in NGINX Plus [PLS.37.0.0.1 LTS](/nginx/releases.md#r37.0).


