Setting Up Nginx Log Collection and Visualization with Promtail, Loki, and Grafana
This guide explains how to collect Nginx access logs, convert them to JSON, ship them with Promtail to Loki, and visualize the data in Grafana, including installation steps, configuration snippets, GeoIP setup, and dashboard customization for a complete observability solution.
A client needed a way to view website traffic without using Google or Baidu analytics, so the author chose Loki as a lightweight log‑collection solution for a small site.
The stack used is Nginx + Promtail + Loki + Grafana . Nginx is installed normally, while Promtail and Loki are installed by downloading their binary releases and starting them with a configuration file.
Promtail configuration (saved as promtail.yaml ) looks like:
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 collecting logs, Nginx’s access log format is changed to JSON using the following log_format directive:
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 geoip_country_code variable is used, the GeoIP module must be compiled into Nginx. Install the required libraries with:
yum -y install GeoIP GeoIP-data GeoIP-develRe‑compile Nginx adding --with-http_geoip_module , replace the old binary using the kill -USR2 signal, and reload.
Grafana is started via Docker:
docker run -d -p 3000:3000 grafana/grafanaLog in with the default admin/admin credentials, add Loki as a data source, and verify log ingestion in the Explore view.
Import a pre‑made dashboard by ID, then install the missing grafana-worldmap-panel plugin:
grafana-cli plugins install grafana-worldmap-panelAfter restarting Grafana, the world map panel displays correctly (the map tiles may need a proxy for overseas access). The article concludes that Loki + Grafana provides a fast, lightweight solution for exposing detailed website access statistics.
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.