This guide shows you how to send NGINX access and error logs to SigNoz with the OpenTelemetry Collector. The Collector tails the log files and parses each line into fields such as status, request_path, and severity, so you can filter and aggregate them in SigNoz.
Prerequisites
- NGINX writing access and error logs to files, such as
/var/log/nginx/access.logand/var/log/nginx/error.log - The OpenTelemetry Collector on the same host, sending data to SigNoz (VM install guide)
- An instance of SigNoz (either Cloud or Self-Hosted)
Send logs to SigNoz
Step 1. Let the Collector read the log files
The nginx.org packages create the log files with mode 640, owned by the nginx user and the adm group. The Collector service runs as the otelcol-contrib user, so add that user to the adm group:
sudo usermod -aG adm otelcol-contribIf you run the Collector binary manually, add the user that runs it to the adm group instead. Without this step, the Collector logs permission denied for each file.
Step 2. Add the log receivers
Add these two receivers to /etc/otelcol-contrib/config.yaml, under the existing receivers section. They read the access and error logs and parse each line:
receivers:
# On Collector v0.149.0 and newer, use "file_log" to avoid a deprecation warning.
filelog/nginx-access-logs:
include: [/var/log/nginx/access.log]
operators:
# Parses the "combined" log format, and the nginx.org "main" format that appends "$http_x_forwarded_for":
# $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
- type: regex_parser
if: body matches '^(?P<remote_addr>[0-9a-fA-F\\.:]+) - (?P<remote_user>[^\\s]+) \\[(?P<ts>.+)\\] "(?P<request_method>\\w+?) (?P<request_path>.+?) (?P<protocol>HTTP/[0-9.]+)" (?P<status>[0-9]+) (?P<body_bytes_sent>[0-9]+) "(?P<http_referrer>[^"]*)" "(?P<http_user_agent>[^"]*)"( "(?P<http_x_forwarded_for>[^"]*)")?$'
parse_from: body
parse_to: attributes
regex: '^(?P<remote_addr>[0-9a-fA-F\.:]+) - (?P<remote_user>[^\s]+) \[(?P<ts>.+)\] "(?P<request_method>\w+?) (?P<request_path>.+?) (?P<protocol>HTTP/[0-9.]+)" (?P<status>[0-9]+) (?P<body_bytes_sent>[0-9]+) "(?P<http_referrer>[^"]*)" "(?P<http_user_agent>[^"]*)"( "(?P<http_x_forwarded_for>[^"]*)")?$'
timestamp:
parse_from: attributes.ts
layout: "02/Jan/2006:15:04:05 -0700"
layout_type: gotime
severity:
parse_from: attributes.status
overwrite_text: true
mapping:
debug: "1xx"
info: ["2xx", "3xx"]
warn: "4xx"
error: "5xx"
- type: remove
if: attributes.ts != nil
field: attributes.ts
- type: add
field: attributes.source
value: nginx
# On Collector v0.149.0 and newer, use "file_log" to avoid a deprecation warning.
filelog/nginx-error-logs:
include: [/var/log/nginx/error.log]
operators:
# Parses the default error log format:
# YYYY/MM/DD HH:MM:SS [LEVEL] PID#TID: *CID MESSAGE
- type: regex_parser
if: body matches '^(?P<ts>.+?) \\[(?P<log_level>\\w+)\\] (?P<pid>\\d+)#(?P<tid>\\d+). \\*(?P<cid>\\d+) (?P<message>.+)$'
parse_from: body
parse_to: attributes
regex: '^(?P<ts>.+?) \[(?P<log_level>\w+)\] (?P<pid>\d+)#(?P<tid>\d+). \*(?P<cid>\d+) (?P<message>.+)$'
timestamp:
parse_from: attributes.ts
layout: "2006/01/02 15:04:05"
layout_type: gotime
severity:
parse_from: attributes.log_level
overwrite_text: true
mapping:
debug: "debug"
info: ["info", "notice"]
warn: "warn"
error: ["error", "crit", "alert"]
fatal: "emerg"
- type: remove
if: attributes.ts != nil
field: attributes.ts
- type: move
if: attributes.message != nil
from: attributes.message
to: body
- type: add
field: attributes.source
value: nginxIf your log files live elsewhere, update the include paths.
Step 3. Add a logs pipeline
Add a logs/nginx pipeline under the existing service.pipelines section. It reuses the processors and exporter from the VM install guide config:
service:
pipelines:
logs/nginx:
receivers: [filelog/nginx-access-logs, filelog/nginx-error-logs]
processors: [batch, resourcedetection]
exporters: [otlphttp]Step 4. Restart the Collector
Restart the Collector so it picks up the new group membership and configuration:
sudo systemctl restart otelcol-contribThe Collector reads only lines written after it starts, so send a few requests to NGINX to produce new log lines.
Validate
- Send a few requests to NGINX, for example
curl http://localhost/. - Open SigNoz and navigate to Logs > Explorer.
- Filter by
source = nginx. - Click a log line to see its parsed attributes, such as
status,request_method, andrequest_path.

Severity follows the response status for access logs: 2xx and 3xx are INFO, 4xx is WARN, and 5xx is ERROR. Error log lines keep the NGINX level.
Connect the NGINX integration (Optional)
The Integrations page in SigNoz tracks whether NGINX logs are arriving.
- Open Integrations and search for
nginx.

- Open Nginx, click Connect Nginx, and then click I have already configured.

- Send a few requests to NGINX. Once logs arrive, the page shows This integration is working properly.

To stop tracking, click Remove from SigNoz. This does not change your Collector configuration.
Data collected
The Data Collected tab of the integration lists the parsed log attributes.

The configuration in this guide also adds protocol and http_x_forwarded_for, which the tab does not list yet.
| Name | Path | Type |
|---|---|---|
| Timestamp | timestamp | timestamp |
| Severity Text | severity_text | string |
| Severity Number | severity_number | number |
| Body Bytes Sent | attributes.body_bytes_sent | string |
| Referrer | attributes.http_referrer | string |
| User Agent | attributes.http_user_agent | string |
| Request Method | attributes.request_method | string |
| Request Path | attributes.request_path | string |
| Protocol | attributes.protocol | string |
| Response Status Code | attributes.status | string |
| Remote Address | attributes.remote_addr | string |
| Forwarded For | attributes.http_x_forwarded_for | string |
Troubleshooting
Check the Collector logs first:
sudo journalctl -u otelcol-contrib -fWhy do the logs show open /var/log/nginx/access.log: permission denied?
The otelcol-contrib user cannot read the NGINX log files. Add it to the adm group as shown in Step 1, then restart the Collector. Group changes apply only after the restart.
Why do NGINX logs arrive without parsed attributes?
Your log_format does not match the access log regex. Compare a line from access.log with the format in the receiver comment, then adjust the regex and if lines.
Why are older log lines missing?
The filelog receiver starts at the end of each file by default. To also send existing lines once, add start_at: beginning to both receivers.
For more help with missing data, see Debug missing traces, logs, and metrics in SigNoz.
Next steps
- Create log-based alerts on
5xxresponses - Trace NGINX requests with the native OpenTelemetry module
- Collect NGINX metrics with the same Collector
Get Help
If you need help with the steps in this topic, please reach out to us on SigNoz Community Slack. If you are a SigNoz Cloud user, please use in product chat support located at the bottom right corner of your SigNoz instance or contact us at cloud-support@signoz.io.