Nginx Logging: A Comprehensive Guide | Better Stack Community (2024)

Nginx, like most applications, records a wealth of datarelated to client interactions, system events, and potential errors. However,this data's potential can only be fully realized through proper configuration,management, and analysis.

This article will teach you to effectively customize Nginx logs for enhancedvisibility and control over your web servers and proxies. Doing this will help youproactively identify and address issues before they disrupt user experience.

Let's begin!

The fastest logsearch on the planetBetter Stack lets you see inside any stack, debug any issue, and resolve anyincident.

Prerequisites

To follow through with this tutorial, you need the following:

  • Basic command-line skills.
  • A Linux system that includes a non-root user with sudo privileges.
  • A recent version of Dockerinstalled on your system.

Step 1 — Running Nginx with Docker

Using the official Docker image is the easiestway to begin working with the Nginx. It simplifies the setup process and ensuresconsistent reproducibility across different systems.

To get started, create a new nginx-logging-tutorial directory, navigate intoit, and execute the following docker run command: :

Copied!

mkdir nginx-logging-tutorial && cd nginx-logging-tutorial

Copied!

docker run --name nginx-server --rm -p 80:80 nginx

In this command:

  • --name nginx-server: Assigns the nginx-server name to the container foreasier reference.
  • --rm: Automatically removes the container when it's stopped, ideal fortesting or temporary setups.
  • -p 80:80: Maps port 80 of your host machine to port 80 inside the container,allowing you to access the Nginx server at http://localhost.

If the nginx image isn't already present on your system, Docker will downloadit before launching the container.

If you encounter an error like the following:

Output

docker: Error response from daemon: driver failed programming external connectivity on endpoint nginx (363e1b33c95786ca6293208b529051a4cf0509208444707b65aef2ddd329ef7e): Bind for 0.0.0.0:80failed: port is already allocated.

It indicates that another application on your system is already using port 80.Ensure that no other services are running on this port before retrying thecommand.

If Nginx starts successfully, you'll see messages like these in your terminal:

Output

. . ./docker-entrypoint.sh: Configuration complete; ready for start up2024/08/06 14:47:59 [notice] 1#1: using the "epoll" event method2024/08/06 14:47:59 [notice] 1#1: nginx/1.27.02024/08/06 14:47:59 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)2024/08/06 14:47:59 [notice] 1#1: OS: Linux 6.9.12-200.fc40.x86_642024/08/06 14:47:59 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1073741816:10737418162024/08/06 14:47:59 [notice] 1#1: start worker processes2024/08/06 14:47:59 [notice] 1#1: start worker process 29

Now, open your web browser and navigate to http://localhost. You should begreeted by the default Nginx welcome page, confirming that your web server is upand running.

Nginx Logging: A Comprehensive Guide | Better Stack Community (1)
Nginx Logging: A Comprehensive Guide | Better Stack Community (2)

Upon returning to your terminal, you might notice log entries like these:

Output

172.17.0.1 - - [06/Aug/2024:14:55:37 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" "-"2024/08/06 14:55:37 [error] 29#29: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP /1.1", host: "localhost", referrer: "http://localhost/"172.17.0.1 - - [06/Aug/2024:14:55:37 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://localhost/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" "-"

These logs reveal that your browser successfully fetched the main webpage butencountered a 404 error when attempting to retrieve the favicon.ico filebecause it's not in the default Nginx directory.

Feel free to stop the Nginx container using Ctrl-C before moving on to thenext section, where we'll dive deeper into the meaning of these log messages.

Step 2 — Locating the Nginx log files

Nginx Logging: A Comprehensive Guide | Better Stack Community (3)
Nginx Logging: A Comprehensive Guide | Better Stack Community (4)

Like most web servers, Nginx meticulously records its activities in two distinctlog files:

  1. Access log: This file chronicles each incoming request, capturing crucialdetails such as the client's IP address, the timestamp of the request, therequested resource (URI), the HTTP status code of the response, and theclient's user agent (browser and operating system).

  2. Error log: This file serves as a diagnostic tool, recording errors andissues encountered during request processing and other Nginx operations. Itlogs information such as the timestamp, error level, error message, and anyrelevant context to aid troubleshooting.

Locating Nginx logs in different environments

The location of these log files varies depends on your operating system andNginx installation method.

Linux distributions

In most Linux distributions, Nginx log files are typically located in the/var/log/nginx/ directory. You'll find them named access.log and error.logrespectively.

Nginx Logging: A Comprehensive Guide | Better Stack Community (5)
Nginx Logging: A Comprehensive Guide | Better Stack Community (6)

If you can't find the log files in the default location, you'll need to checkyour specific Nginx configuration. Start by determining the location of yourNginx configuration file (usually /etc/nginx/nginx.conf):

Copied!

sudo nginx -t

This command should output the location of your configuration file if it'svalid:

Output

nginx: the configuration file /etc/nginx/nginx.conf syntax is oknginx: configuration file /etc/nginx/nginx.conf test is successful

Open the configuration file and look for the error_log and access_logdirectives to pinpoint their respective locations.

/etc/nginx/nginx.conf

Copied!

error_log /var/log/nginx/error.loghttp { . . . access_log /var/log/nginx/access.log; . . .}

By default, Nginx applies the error_log directive globally, while theaccess_log is usually placed within the http block.

You can use the tail command to view the contents of these files in real-time:

Copied!

sudo tail -f /var/log/nginx/access.log

Copied!

sudo tail -f /var/log/nginx/error.log

Docker container

Since Docker containers are ephemeral, it's not practical to store logs directlywithin the container. The official Nginx Docker image addresses this by creatingsymbolic links from /var/log/nginx/access.log and /var/log/nginx/error.logto the container's standard output (/dev/stdout) and standard error(/dev/stderr) streams respectively. This enables Docker's loggingmechanisms to collect and manage the logs.

You can find therelevant lines in the Dockerfile:

mainline/debian/Dockerfile

Copied!

. . .ln -sf /dev/stdout /var/log/nginx/access.log \&& ln -sf /dev/stderr /var/log/nginx/error.log \

Start the nginx-server container once again, but detach it from your currentterminal session:

Copied!

docker run --name nginx-server -d -p 80:80 nginx

Output

18394cb59b3e1d334143300e9a86744c2babb6994b5a92782fb92e10098f25b4

With the container running, visit http://localhost once again to generate somelogs, then use the docker logs command to view them accordingly:

Copied!

docker logs -f nginx-server

You will see the familiar Nginx log output which combines both the access anderror logs:

Output

. . .172.17.0.1 - - [06/Aug/2024:16:37:59 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0" "-"2024/08/06 16:37:59 [error] 31#31: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost", referrer: "http://localhost/"172.17.0.1 - - [06/Aug/2024:16:37:59 +0000] "GET /favicon.ico HTTP/1.1" 404 153 "http://localhost/" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0" "-"

To view only the access logs, redirect the standard error to /dev/null:

Copied!

docker logs -f nginx-server 2>/dev/null

Output

. . .172.17.0.1 - - [06/Aug/2024:16:37:59 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0" "-"172.17.0.1 - - [06/Aug/2024:16:37:59 +0000] "GET /favicon.ico HTTP/1.1" 404 153 "http://localhost/" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0" "-"

Similarly, to view only the error logs, redirect the standard output to/dev/null:

Copied!

docker logs -f nginx-server 1>/dev/null

Output

. . .2024/08/06 16:37:59 [error] 31#31: *3 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost", referrer: "http://localhost/"

Now that you know how to locate and access Nginx log files in variousenvironments, let's explore how you can customize the access log format to suityour needs.

Step 3 — Configuring Nginx access logs

By default, Nginx access logs are generated in the combined format unlessotherwise specified. This format is defined as:

Copied!

'$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" "$http_x_forwarded_for"';

This configuration produces access log entries similar to this:

Output

172.17.0.1 - - [06/Aug/2024:16:37:59 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0" "-"
Nginx Logging: A Comprehensive Guide | Better Stack Community (7)
Nginx Logging: A Comprehensive Guide | Better Stack Community (8)

Let's break down what each token in the log represents:

  • 172.17.0.1: The IP address of the client that made the request.
  • -: If authentication is used, this is the authenticated username; otherwise,it's a hyphen (-).
  • [06/Aug/2024:16:37:59 +0000]: The local time when the request was processed.
  • "GET / HTTP/1.1" : The request method, path, and HTTP protocol version.
  • 200: The HTTP status code returned to the client.
  • 615: The size of the response body in bytes.
  • "-": The URL of the referring page (if any).
  • "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0" -The browser and operating system information provided by the client.
  • "-": If the request passed through a proxy, this variable contains theoriginal client IP address.

While this format captures a wide range of information that is useful foranalyzing traffic patterns, troubleshooting errors, and understanding userbehavior, you may want to customize it to capture only the data that mattersmost to you. Let's look at how to do that next.

You can tailor the access log format using the log_format directive in eitherthe main Nginx configuration file (/etc/nginx/nginx.conf) or withinhost-specific configurations in /etc/nginx/sites-enabled.

If Nginx is running directly on your host machine, you can edit the relevantfile accordingly. For Docker instances, execute the command below to extract theNginx configuration file from the nginx image and save it to an nginx.conffile on your host machine:

Copied!

docker run --rm --entrypoint=cat nginx /etc/nginx/nginx.conf > nginx.conf

Once you're ready to launch the container once again, ensure to mount themodified file from your host machine to /etc/nginx/nginx.conf within thecontainer:

Copied!

docker run --name nginx-server -v ./nginx.conf:/etc/nginx/nginx.conf:ro -d nginx

Customizing the access log format

Customizing the format of the entries in the access log can be done using thelog_format directive:

Copied!

log_format <name> '<formatting_variables>';

All you need to do is give the custom format a name and define the structure ofthe log using the providedcore variablesandlog variables.Here's an example of what it could look like:

nginx.conf

Copied!

. . .http { . . . log_format custom '$remote_addr - $remote_user [$time_local] $status ' '"$host" "$request" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; . . .}

This example adds the $host variable to the combined log format so that it thedomain (or subdomain) being requested is also presented in the logs.

To apply this custom format, modify the access_log directive:

Copied!

access_log /var/log/nginx/access.log custom;

Save your changes, then stop and remove your existing nginx-server containerwith:

Copied!

docker container stop nginx-server

Copied!

docker container rm nginx-server

Start it again with:

Copied!

docker run --name nginx-server --rm -p 80:80 -v ./nginx.conf:/etc/nginx/nginx.conf:ro nginx

When you visit http://localhost now, you will observe that the domain isrecorded in the corresponding log entry:

Output

172.17.0.1 - - [07/Aug/2024:09:32:40 +0000] 404 "localhost" "GET /favicon.ico HTTP/1.1" 555 "http://localhost/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36" "-"

Setting up conditional logging

Nginx's access logs can become quite large, especially under heavy traffic.Conditional logging allows you to selectively filter log entries based onspecific criteria to reduce log volume and improve performance.

The syntax looks like this:

Copied!

access_log /path/to/access.log <log_format> if=<condition>;

The <condition> is a boolean expression that Nginx evaluates for each request.If it evaluates to true, the log entry is written; otherwise, it's skipped.

The following example demonstrates how to exclude successful (2xx) andredirection (3xx) status codes from the access log:

Copied!

http { map $status $loggable { ~^[23] 0; # Match 2xx and 3xx status codes default 1; # Log everything else } access_log /var/log/nginx/access.log combined if=$loggable;}

Some practical applications of conditional logging include:

  • Logging only error responses (4xx and 5xx) for troubleshooting.
  • Excluding specific user agents or IP addresses known to be bots.
  • Logging only requests to specific parts of your application.
  • Logging a percentage of requests to reduce logging costs whilestill capturing a representative sample(see here for some sampling techniques).

Disabling the access log

If you're already collecting request logs through your web application, you maywant to disable the Nginx access log by using a special off value, or byredirecting to /dev/null

Copied!

access_log off;access_log /dev/null;

Step 4 - Structuring Nginx access logs

In the world of cloud-native distributed systems and microservices, structuredlogging has gained significant traction due to its numerousbenefits over traditional plain-text logs.

For example, Caddy, an Nginx alternative, produces access logs thatlook like this:

Copied!

{ "level": "info", "ts": 1646861401.5241024, "logger": "http.log.access", "message": "handled request", "request": { "remote_ip": "127.0.0.1", "remote_port": "41342", "client_ip": "127.0.0.1", "proto": "HTTP/2.0", "method": "GET", "host": "localhost", "uri": "/", "headers": { "User-Agent": ["curl/7.82.0"], "Accept": ["*/*"], "Accept-Encoding": ["gzip, deflate, br"], }, "tls": { "resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "example.com" } }, "bytes_read": 0, "user_id": "", "duration": 0.000929675, "size": 10900, "status": 200, "resp_headers": { "Server": ["Caddy"], "Content-Encoding": ["gzip"], "Content-Type": ["text/html; charset=utf-8"], "Vary": ["Accept-Encoding"] }}

Let's explore how to bring Nginx access logs into this modern era.

While Nginx doesn't natively produce JSON logs, you can achieve this using thelog_format directive in conjunction with the escape=json parameter whichensures that characters that aren't valid in JSON are properly escaped.

Let's see how this works:

nginx.conf

Copied!

. . .http { . . . log_format custom_json escape=json '{' '"level":"info",' '"ts": "$time_iso8601",' '"message": "handled request $request_method $request_uri",' '"request": {' '"id": "$http_x_request_id",' '"remote_ip": "$remote_addr",' '"remote_port": "$remote_port",' '"protocol": "$server_protocol",' '"method": "$request_method",' '"host": "$host",' '"uri": "$request_uri",' '"headers": {' '"user-agent": "$http_user_agent",' '"accept": "$http_accept",' '"accept-encoding": "$http_accept_encoding",' '"traceparent": "$http_traceparent",' '"tracestate": "$http_tracestate"' '}' '},' '"bytes_read": $request_length,' '"duration_msecs": $request_time,' '"size": $bytes_sent,' '"status": $status,' '"resp_headers": {' '"content_length": "$sent_http_content_length",' '"content_type": "$sent_http_content_type"' '}' '}'; access_log /var/log/nginx/access.log custom_json; . . .}

In this configuration:

  • We defined a new log format named custom_json and enabled JSON escaping withescape=json.
  • Within the JSON structure, we capture a variety of information:
    • Basic details like log level, timestamp (ts), and a message.
    • Detailed request information nested under the request object, includingheaders using $http_<header_name>.
    • Metrics like bytes read, response time, and response size.
    • Response details like status and specific response headers using$sent_http_<header_name>.
  • Finally, the custom_json format is applied to the access log.

After saving the configuration and restarting Nginx, make a request with somefictional distributed tracing headers:

Copied!

curl -v -H "traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01" \ -H "tracestate: rojo=00f067aa0ba902b7" \ -H "X-Request-Id: f45a82a7-7066-40d4-981d-145952c290f8" \ http://localhost

You'll observe new access log entries in a clean JSON format:

Output

{ "level": "info", "ts": "2024-08-07T11:57:31+00:00", "message": "handled request GET /", "request": { "id": "f45a82a7-7066-40d4-981d-145952c290f8", "remote_ip": "172.17.0.1", "remote_port": "39638", "protocol": "HTTP/1.1", "method": "GET", "host": "localhost", "uri": "/", "headers": { "user-agent": "curl/8.6.0", "accept": "*/*", "accept-encoding": "", "traceparent": "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", "tracestate": "rojo=00f067aa0ba902b7" } }, "bytes_read": 229, "duration_msecs": 0.000, "size": 853, "status": 200, "resp_headers": { "content_length": "615", "content_type": "text/html" }}

This structured format makes your logs much easier to parse and analyze, openingthe doors to powerful log management and visualization capabilities.

Step 5 — Configuring Nginx error logs

The Nginx error log is a crucial tool for diagnosing and resolving issues withyour web server. It captures errors, warnings, and other important events thatoccur during various Nginx operations. Let's explore how to configure and managethis valuable resource.

The error_log directive controls Nginx's error logging behavior. It acceptstwo parameters: the path of the log file, and the minimum severitylevel of the log.

Copied!

error_log /var/log/nginx/error.log <severity_level>;

Nginx categorizes error log messages into the following levels, ranging fromleast to most severe:

  • debug: Highly detailed messages primarily used for troubleshooting anddevelopment.
  • info: General informational messages about the server's operation.
  • notice: Noteworthy events that aren't necessarily errors.
  • warn: Unexpected occurrences that could indicate potential problems.
  • error: Actual errors encountered during processing.
  • crit: Critical conditions that require attention.
  • alert: Errors that demand immediate action.
  • emerg: Severe errors that render the system unusable.

If you haven't explicitly configured the error severity level in your Nginxconfiguration, you will see messages at the error level and all levels aboveit (crit, alert, and emerg). However, the default level for the officialNginx docker image is set to notice.

To change the default error log level, provide the desired level as the secondparameter to the error_log directive:

Copied!

error_log /var/log/nginx/error.log warn;

This configuration will log messages at the warn level and all higher levels.

The Nginx error log format

Nginx Logging: A Comprehensive Guide | Better Stack Community (9)
Nginx Logging: A Comprehensive Guide | Better Stack Community (10)

Nginx error logs adhere to a format designed for human readability and easyparsing by tools. The general format is:

Copied!

YYYY/MM/DD HH:MM:SS [<severity_level>] <pid>#<tid>: *<cid> <message>

Where:

  • <pid>: Process ID
  • <tid>: Thread ID
  • <cid>: Connection ID

Here's an example of an actual error log entry:

Copied!

2024/08/07 17:41:58 [error] 29#29: *1 open() "/usr/share/nginx/html/make" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /make HTTP/1.1", host: "localhost"

Logging errors to multiple files

Similar to access logs, you can configure Nginx to log errors to multiple files,even with different severity levels:

Copied!

error_log /var/log/nginx/error.log info;error_log /var/log/nginx/emerg_error.log emerg;

In this setup, all events except debug level messages will be logged toerror.log, while emergency events will be logged to a separate file namedemerg_error.log.

Disabling the error log

If you need to completely disable the Nginx error log (though not generallyrecommended), you can redirect it to /dev/null. There doesn't appear to be aspecial off value at the time of writing.

Copied!

error_log /dev/null;

In the next section, we'll look at how you can structure your Nginx error logsin JSON format.

Step 7 — Structuring Nginx error logs

While Nginx doesn't offer built-in JSON formatting for its error logs, we canleverage external log processing tools like Logstash,Fluentd, or Vector to parse, reformat,and enrich these logs for better analysis and integration with modern loggingsystems.

In this section, I'll demonstrate how to use Vector to transform Nginx errorlogs into structured JSON format, providing similar benefits to what we achievedwith the access logs in the previous step.

If you haven't already,install Vector on your machine orpull the official Docker image:

Copied!

docker pull timberio/vector:0.40.0-alpine

Output

0.40.0-alpine: Pulling from timberio/vectorc6a83fedfae6: Already existsb9fc015ecb16: Pull completef7f83e464043: Pull completefe91b3a632fb: Pull complete5312bc41fca9: Pull complete4f4fb700ef54: Pull completeDigest: sha256:7a81fdd62e056321055a9e4bdec4073d752ecf68f4c192e676b85001721523c2Status: Downloaded newer image for timberio/vector:0.40.0-alpinedocker.io/timberio/vector:0.40.0-alpine

Next, create a vector.yaml file in the current directory with the followingcontents:

vector.yaml

Copied!

sources: nginx: type: docker_logs include_images: - nginxtransforms: nginx_json: type: remap inputs: - nginx source: | .context = parse_json(.message) ?? parse_nginx_log(.message, "error") ?? set!(value: {}, path: ["message"], data: .message) .message = .context.message del(.context.message) .sinks: print: type: console inputs: - nginx_json encoding: codec: json
  • sources: Defines the log source. In this case, we're collecting logs fromDocker containers based on the nginx image.

  • transforms: Defines a transformation named nginx_json.

    • type: remap: Uses the Vector Remap Language (VRL) for transformation.
    • inputs: Takes input from the nginx source.
    • source: Contains the VRL script:
    • It first attempts to parse the message field as JSON using parse_json().
    • If that fails, it tries to parse it as an Nginx error log usingparse_nginx_log().
    • If both parsing attempts fail, it creates an empty object and assigns itto .context.message.
    • It then sets the top-level message field to .context.message and deletes.context.message.
    • Finally, the . at the end returns the modified event.
  • sinks: Defines the destination for the transformed logs which is the consolein JSON format.

Note for file-based logs: If you're reading the Nginx logs from files, thesources portion should be changed to:

vector.yml

Copied!

sources: nginx: type: file include: - /var/log/nginx/error.log - /var/log/nginx/access.log. . .

To see this in action, let's bring up both the Nginx and Vector containers withDocker Compose. Before you proceed, create adocker-compose.yml file in your nginx-logging-tutorial directory andpopulate it with the contents below:

docker-compose.yml

Copied!

name: nginx-logging-tutorialservices: nginx: container_name: nginx-server image: nginx:alpine restart: always ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/nginx.conf vector: container_name: vector image: timberio/vector:0.40.0-alpine restart: always volumes: - ./vector.yaml:/etc/vector/vector.yaml - /var/run/docker.sock:/var/run/docker.sock:ro

When configuring the vector service in your docker-compose.yml, it's necessaryto mount the vector.yaml file you created into the container to replace thedefault configuration

You also need to mount the Docker socket (/var/run/docker.sock) inside thecontainer. This gives Vector the necessary access to communicate with the Dockerdaemon, enabling it to identify and monitor the logs of other containers youspecify.

However, in a production environment, exposing the Docker socket directly to acontainer can be a security risk. Consideralternative approaches, such as:

  • Configuring Vector to interact with the Docker daemon over a SSH or HTTPS.
  • Instead of running Vector in a container, install it directly on the hostmachine to avoid the need for socket mounting.

These precautions help safeguard your Docker environment while still allowingVector to effectively collect and process your Nginx logs.

To bring up your services, ensure that you've removed the running nginx-servercontainer by pressing CTRL-C before running the command below:

Copied!

docker compose up -d

Output

[+] Running 3/3 ✔ Network nginx-logging-tutorial_default Created 0.3s ✔ Container nginx-server Started 0.6s ✔ Container vector Started 0.6s

Navigate back to your browser, access http://localhost and refresh the page acouple of times.

You can then view the processed logs with:

Copied!

docker logs -f vector

If you have jq installed, you can pipe theoutput to jq while ignoring non-JSON objects with:

Copied!

docker logs -f vector | jq -R 'fromjson? | select(type == "object")'

You will see the following output representing an access log:

Output

. . .{ "container_created_at": "2024-08-08T08:59:06.052457248Z", "container_id": "0f17a23cef07616df8cf4f698664e8b9f2c62daaff75bbfc77d750f797eb06c5", "container_name": "nginx-server",

"context": {

"bytes_read": 72,

"duration_msecs": 0.0,

"level": "info",

"request": {

"headers": {

"accept": "*/*",

"accept-encoding": "",

"traceparent": "",

"tracestate": "",

"user-agent": "curl/8.6.0"

},

"host": "localhost",

"id": "",

"method": "GET",

"protocol": "HTTP/1.1",

"remote_ip": "172.24.0.1",

"remote_port": "34480",

"uri": "/"

},

"resp_headers": {

"content_length": "615",

"content_type": "text/html"

},

"size": 853,

"status": 200,

"ts": "2024-08-08T11:13:57+00:00"

},

"host": "3d0ebf54b0eb", "image": "nginx:alpine", "label": { "com.docker.compose.config-hash": "b27d7c9cd09c4f82e419ac408790cbea8ea31a8102d37160b93b318cb4f18cc6", "com.docker.compose.container-number": "1", "com.docker.compose.depends_on": "", "com.docker.compose.image": "sha256:1ae23480369fa4139f6dec668d7a5a941b56ea174e9cf75e09771988fe621c95", "com.docker.compose.oneoff": "False", "com.docker.compose.project": "nginx-logging-tutorial", "com.docker.compose.project.config_files": "/home/ayo/dev/betterstack/demo/nginx-docker/docker-compose.yml", "com.docker.compose.project.working_dir": "/home/ayo/dev/betterstack/demo/nginx-docker", "com.docker.compose.service": "nginx", "com.docker.compose.version": "2.29.1", "maintainer": "NGINX Docker Maintainers <[email protected]>" }, "message": "handled request GET /", "source_type": "docker_logs", "stream": "stdout", "timestamp": "2024-08-08T11:13:57.834369885Z"}

The context property contains the original JSON object from Nginx as parsed bythe parse_json() function we used earlier. The other properties were added byVector when collecting the logs from the Docker container.

To what see a parsed error log, you can make a request for a non-existent filewith:

Copied!

curl http://localhost/favicon.ico

You will subsequently observe the following entry in the Docker logs:

Output

{ "container_created_at": "2024-08-08T08:59:06.052457248Z", "container_id": "0f17a23cef07616df8cf4f698664e8b9f2c62daaff75bbfc77d750f797eb06c5", "container_name": "nginx-server", "context": { "cid": 1, "client": "172.24.0.1", "host": "localhost", "pid": 23, "request": "GET /favicon.ico HTTP/1.1", "server": "localhost", "severity": "error", "tid": 23, "timestamp": "2024-08-08T16:37:59Z" }, "host": "3d0ebf54b0eb", "image": "nginx:alpine", "label": { "com.docker.compose.config-hash": "b27d7c9cd09c4f82e419ac408790cbea8ea31a8102d37160b93b318cb4f18cc6", "com.docker.compose.container-number": "1", "com.docker.compose.depends_on": "", "com.docker.compose.image": "sha256:1ae23480369fa4139f6dec668d7a5a941b56ea174e9cf75e09771988fe621c95", "com.docker.compose.oneoff": "False", "com.docker.compose.project": "nginx-logging-tutorial", "com.docker.compose.project.config_files": "/home/ayo/dev/betterstack/demo/nginx-docker/docker-compose.yml", "com.docker.compose.project.working_dir": "/home/ayo/dev/betterstack/demo/nginx-docker", "com.docker.compose.service": "nginx", "com.docker.compose.version": "2.29.1", "maintainer": "NGINX Docker Maintainers <[email protected]>" }, "message": "open() \"/usr/share/nginx/html/favicon.ico\" failed (2: No such file or directory)", "source_type": "docker_logs", "stream": "stderr", "timestamp": "2024-08-08T16:37:59.012279362Z"}

With structured JSON logs for both access and error events, you're nowwell-equipped to gain valuable insights into your Nginx server's behavior andperformance.

Vector is much more capable than what we've covered here, so ensure to check outit* documentation and read our comprehensiveguide to get a quick overview.

Step 8 — Monitoring Nginx logs with Better Stack

Now that you've configured and structured your Nginx logs, let's elevate yourlog management by centralizing them with a dedicated service.This will empower you to view, search, and analyze your logs effortlessly,uncovering valuable insights and identifying potential issues.

Better Stack is an excellent solution forcentralized log management. It offers a user-friendly interface, powerful searchcapabilities, and the ability to set up alerts for critical events. Let's seehow to integrate your Nginx logs with Better Stack using Vector as a logforwarder.

Sign up for a free Better Stackaccount, then navigate to the Logs & Metrics dashboard. From the left-handmenu, choose Sources and click on Connect source:

Nginx Logging: A Comprehensive Guide | Better Stack Community (11)
Nginx Logging: A Comprehensive Guide | Better Stack Community (12)

Name your source Nginx Logging Tutorial and choose Vector as the platform,then click Create source:

Nginx Logging: A Comprehensive Guide | Better Stack Community (13)
Nginx Logging: A Comprehensive Guide | Better Stack Community (14)

Copy the provided Source Token from the source details page:

Nginx Logging: A Comprehensive Guide | Better Stack Community (15)
Nginx Logging: A Comprehensive Guide | Better Stack Community (16)

Return to your editor, and update your vector.yaml file with the followingsinks configuration, replacing <your_betterstack_source_token> with thetoken you copied:

vector.yaml

Copied!

. . .sinks: betterstack: type: http method: post inputs: [nginx_json] uri: https://in.logs.betterstack.com/ encoding: codec: json auth: strategy: bearer token: <your_betterstack_source_token>

Instead of printing the processed logs to the console, this updatedconfiguration instructs Vector to transmit them into Better Stack over HTTP.

You may now restart your running Docker Compose services with:

Copied!

docker compose restart

Output

[+] Restarting 2/2 ✔ Container nginx-server Started 0.8s ✔ Container vector Started 0.8s

Once the services are relaunched, return back to your browser, accesshttp://localhost and refresh the homepage a couple of times.

After a few seconds, you'll start seeing the Nginx logs arriving in Livetail:

Nginx Logging: A Comprehensive Guide | Better Stack Community (17)
Nginx Logging: A Comprehensive Guide | Better Stack Community (18)

Congratulations! You've successfully centralized your Nginx logs with BetterStack. You now have a powerful platform to monitor, search, and analyze your logdata, helping you keep your web server and applications running smoothly andefficiently.

Feel free to explore the variousfeatures and capabilities of BetterStack to gain deeper insights into your Nginx logs and application behavior.

Final thoughts

In this guide, you learned how to locate, access, and interpret Nginx access anderror logs, both on traditional servers and within Docker containers. You alsolearned to customize access logs, implement conditional logging, and format logsas structured JSON for easier analysis.

By following these techniques, you can now effectively monitor your Nginxserver, troubleshoot issues, and gain valuable insights into your web trafficand application performance.

Thanks for reading, and happy logging!

Nginx Logging: A Comprehensive Guide | Better Stack Community (19)

Article by

Ayooluwa Isaiah

Ayo is the Head of Content at Better Stack. His passion is simplifying and communicating complex technical ideas effectively. His work was featured on several esteemed publications including LWN.net, Digital Ocean, and CSS-Tricks. When he’s not writing or coding, he loves to travel, bike, and play tennis.

Got an article suggestion?Let us know

Next article

Monitoring Linux Authentication Logs: A Practical GuideLearn to effectively monitor Linux authentication logs to detect security threats with this practical step-by-step guide→

Nginx Logging: A Comprehensive Guide | Better Stack Community (20)

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Nginx Logging: A Comprehensive Guide | Better Stack Community (2024)
Top Articles
Learn about the Silver Libertad as an Investment
It Could Soon Be Illegal for Airlines to Charge Families to Sit Together
Fiskars X27 Kloofbijl - 92 cm | bol
Jail Inquiry | Polk County Sheriff's Office
Manhattan Prep Lsat Forum
Algebra Calculator Mathway
What Are the Best Cal State Schools? | BestColleges
T Mobile Rival Crossword Clue
Sprague Brook Park Camping Reservations
Tyrunt
Music Archives | Hotel Grand Bach - Hotel GrandBach
biBERK Business Insurance Provides Essential Insights on Liquor Store Risk Management and Insurance Considerations
Items/Tm/Hm cheats for Pokemon FireRed on GBA
Indiana Immediate Care.webpay.md
Jack Daniels Pop Tarts
Classroom 6x: A Game Changer In The Educational Landscape
ocala cars & trucks - by owner - craigslist
Conan Exiles Thrall Master Build: Best Attributes, Armor, Skills, More
Mzinchaleft
All Obituaries | Buie's Funeral Home | Raeford NC funeral home and cremation
Glenda Mitchell Law Firm: Law Firm Profile
Scout Shop Massapequa
Sea To Dallas Google Flights
Anotherdeadfairy
F45 Training O'fallon Il Photos
Sofia the baddie dog
Page 2383 – Christianity Today
Wood Chipper Rental Menards
Radical Red Ability Pill
Hwy 57 Nursery Michie Tn
Rek Funerals
Little Einsteins Transcript
Greyson Alexander Thorn
Nurofen 400mg Tabletten (24 stuks) | De Online Drogist
Vlacs Maestro Login
Mosley Lane Candles
Account Now Login In
Mg Char Grill
Bozjan Platinum Coins
Whas Golf Card
Supermarkt Amsterdam - Openingstijden, Folder met alle Aanbiedingen
How to Draw a Sailboat: 7 Steps (with Pictures) - wikiHow
Keir Starmer looks to Italy on how to stop migrant boats
Clausen's Car Wash
US-amerikanisches Fernsehen 2023 in Deutschland schauen
Is Ameriprise A Pyramid Scheme
Ghareeb Nawaz Texas Menu
Yale College Confidential 2027
Tacos Diego Hugoton Ks
Where and How to Watch Sound of Freedom | Angel Studios
Latest Posts
Article information

Author: Francesca Jacobs Ret

Last Updated:

Views: 5999

Rating: 4.8 / 5 (48 voted)

Reviews: 95% of readers found this page helpful

Author information

Name: Francesca Jacobs Ret

Birthday: 1996-12-09

Address: Apt. 141 1406 Mitch Summit, New Teganshire, UT 82655-0699

Phone: +2296092334654

Job: Technology Architect

Hobby: Snowboarding, Scouting, Foreign language learning, Dowsing, Baton twirling, Sculpting, Cabaret

Introduction: My name is Francesca Jacobs Ret, I am a innocent, super, beautiful, charming, lucky, gentle, clever person who loves writing and wants to share my knowledge and understanding with you.