NGINX Logs

NGINX metrics with Prometheus for custom log parser. Vector example. Should also work with https://github.com/martin-helmich/prometheus-nginxlog-exporter Dashboard is based on 15947.

NGINX Logs screenshot 1

Nginx in docker:

log_format main escape=json '{'
    '"time_local":"$time_local",'
    '"remote_addr":"$remote_addr",'
     '"request":"$request",'
     '"status":"$status",'
     '"body_bytes_sent":"$body_bytes_sent",'
     '"http_referer":"$http_referer",'
     '"http_user_agent":"$http_user_agent",'
     '"http_x_forwarded_for":"$http_x_forwarded_for",'
     '"request_time":"$request_time"'
  '}';
  access_log /dev/stdout main;

Docker compose:

  myweb:
    image: 'my_nginx'
    labels:
      - "servicename=myweb"
  vector:
    image: 'timberio/vector:0.34.0-alpine'
    ports:
      - 9598:9598
    volumes:
      - ./vector034.yaml:/etc/vector/vector.yaml:ro
      - /var/run/docker.sock:/var/run/docker.sock
    command: ["-c", "/etc/vector/vector.yaml"]

vector034.yaml:

sources:
  nginx_logs:
    type: "docker_logs"
    include_labels:
      - "servicename=myweb"

transforms: parse_logs: type: "remap" inputs: ["nginx_logs"] source: | if !exists(.message) { abort } cleaned, err = strip_whitespace(.message) if err != null { # log("Strip whitespace error: " + err, level: "error") cleaned = .message }

  parsed, err = parse_json(cleaned)
  if err != null {
    # log("Parsing error: " + err, level: "error")
    abort
  }
  
  .time_local = parsed.time_local
  .remote_addr = parsed.remote_addr
  .request = parsed.request
  .status = parsed.status
  .body_bytes_sent = parsed.body_bytes_sent
  .http_referer = parsed.http_referer
  .http_user_agent = parsed.http_user_agent
  .http_x_forwarded_for = parsed.http_x_forwarded_for
  .request_time = parsed.request_time
  
  request_parts, err = parse_regex(.request, r'^(?P<method>\S+) (?P<uri>[^\s]+) (?P<protocol>[^"]+)$')
  if err != null {
    log("Request parsing error: " + err, level: "error")
    .request_method = "UNKNOWN"
    .request_uri = "/"
    .request_protocol = "HTTP/1.1"
  } else {
    .request_method = request_parts.method
    .request_uri = request_parts.uri
    .request_protocol = request_parts.protocol
  }
  
  .body_bytes_sent, err = if .body_bytes_sent == "" || .body_bytes_sent == null { 0 } else { to_int(.body_bytes_sent) }
  .request_method = if .request_method == "" || .request_method == null { "UNKNOWN" } else { .request_method }
  .status = if .status == "" || .status == null { "000" } else { .status }
  .request_uri = if .request_uri == "" || .request_uri == null { "/" } else { .request_uri }
  .request_time, err = if .request_time == "" || .request_time == null { 0.0 } else { to_float(.request_time) }
  
  # log("Parsed log: " + encode_json(.), level: "debug")

extract_metrics: type: "log_to_metric" inputs: ["parse_logs"] metrics: - type: "counter" name: "nginx_http_response_count_total" description: "Total HTTP requests" field: "status" tags: method: "{{ request_method }}" status: "{{ status }}" path: "{{ request_uri }}" - type: "counter" name: "nginx_http_response_size_bytes" description: "Total bytes sent" field: "body_bytes_sent" tags: method: "{{ request_method }}" status: "{{ status }}" - type: "histogram" name: "nginx_http_response_time_seconds" description: "Request processing time in seconds" field: "request_time" tags: method: "{{ request_method }}" status: "{{ status }}"

sinks: prometheus_exporter: type: "prometheus_exporter" inputs: ["extract_metrics"] address: "0.0.0.0:9598"

Revisions
RevisionDescriptionCreated
Grafana Loki (self-hosted)

Grafana Loki (self-hosted)

by Grafana Labs
Grafana Labs solution

Easily monitor Grafana Loki (self-hosted), a horizontally scalable, highly available, multi-tenant log aggregation system inspired by Prometheus, with Grafana Cloud's out-of-the-box monitoring solution.

Learn more

Get this dashboard

Import the dashboard template

or

Download JSON

Datasource
Dependencies