Setting Up Nginx Log Monitoring with Loki, Promtail, and Grafana
This article walks through a complete, step‑by‑step solution for collecting Nginx access logs, converting them to JSON, shipping them with Promtail to Loki, and visualizing the data in Grafana, including Docker deployment, dashboard import, and world‑map plugin installation.
The author received a client request to view website traffic without using Google or Baidu analytics, so a lightweight log‑monitoring stack based on Loki was chosen.
First, Nginx is installed as usual, while Promtail and Loki are downloaded as binary releases, extracted, and started with a configuration file. The Promtail configuration looks like this:
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://localhost:3100/loki/api/v1/push
scrape_configs:
- job_name: nginx
pipeline_stages:
- replace:
expression: '(?:[0-9]{1,3}\.){3}([0-9]{1,3})'
replace: '***'
static_configs:
- targets:
- localhost
labels:
job: nginx_access_log
host: expatsxxxxs
agent: promtail
__path__: /var/log/nginx/expatshxxxxs.access.logAfter the collector is running, Nginx’s log format is changed to JSON to make it compatible with Loki. The JSON log format definition is:
log_format json_analytics escape=json '{'
"msec": "$msec",
"connection": "$connection",
"connection_requests": "$connection_requests",
"pid": "$pid",
"request_id": "$request_id",
"request_length": "$request_length",
"remote_addr": "$remote_addr",
"remote_user": "$remote_user",
"remote_port": "$remote_port",
"time_local": "$time_local",
"time_iso8601": "$time_iso8601",
"request": "$request",
"request_uri": "$request_uri",
"args": "$args",
"status": "$status",
"body_bytes_sent": "$body_bytes_sent",
"bytes_sent": "$bytes_sent",
"http_referer": "$http_referer",
"http_user_agent": "$http_user_agent",
"http_x_forwarded_for": "$http_x_forwarded_for",
"http_host": "$http_host",
"server_name": "$server_name",
"request_time": "$request_time",
"upstream": "$upstream_addr",
"upstream_connect_time": "$upstream_connect_time",
"upstream_header_time": "$upstream_header_time",
"upstream_response_time": "$upstream_response_time",
"upstream_response_length": "$upstream_response_length",
"upstream_cache_status": "$upstream_cache_status",
"ssl_protocol": "$ssl_protocol",
"ssl_cipher": "$ssl_cipher",
"scheme": "$scheme",
"request_method": "$request_method",
"server_protocol": "$server_protocol",
"pipe": "$pipe",
"gzip_ratio": "$gzip_ratio",
"http_cf_ray": "$http_cf_ray",
"geoip_country_code": "$geoip_country_code"
}';Because the configuration uses the GeoIP module, the required libraries are installed via:
yum -y install GeoIP GeoIP-data GeoIP-develNginx is then recompiled with --with-http_geoip_module , the new binary replaces the old one, and a graceful reload is performed using kill -USR2 . After the reload, the access logs are emitted in JSON and can be verified by querying Loki.
Grafana is launched quickly with Docker:
docker run -d -p 3000:3000 grafana/grafanaAfter logging in with the default admin credentials, a Loki data source is added, and a pre‑built dashboard is imported by ID. The dashboard initially lacks a world‑map, so the required plugin is installed:
grafana-cli plugins install grafana-worldmap-panelGrafana is restarted, the dashboard now shows the map (subject to external map image loading), and the whole stack provides a clear, real‑time view of Nginx traffic.
In summary, the combination of Nginx + Promtail + Loki + Grafana offers a lightweight, scalable solution for website log monitoring without third‑party analytics services.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.