Skip to content

Instantly share code, notes, and snippets.

@kamermans
Last active September 18, 2021 02:56
Show Gist options
  • Save kamermans/4fd55912fd6333451f8a6cae7826db6e to your computer and use it in GitHub Desktop.
Save kamermans/4fd55912fd6333451f8a6cae7826db6e to your computer and use it in GitHub Desktop.
Simple nginx stats aggregation without NGINX Plus, Lua or NginScript - all you need is Python3!
#!/usr/bin/env python3
# Start this script before you start NGINX, so it is ready and waiting for log data.
# You should start it in the background with & or run it from supervisord or something.
# If the port is above 1024, you can run this script as a non-root user (recommended).
#
# It listens for raw Syslog data over UDP, parses is, aggregates it, and writes
# it to a file every flush_interval seconds. The output file format looks like this:
# key<TAB>value
# Here is an example (note that the delimiter is actually TAB, not a space):
'''
count 6
200 1
500 5
2xx 1
5xx 5
'''
# Add this to your nginx config in the section that you want to collect log data on (http, server, location):
# log_format status '$status\n';
# access_log syslog:server=127.0.0.1:10514,nohostname,tag=nginx status;
#
import sys
import time
import socket
if len(sys.argv) < 2:
report_file ="/tmp/nginx_status"
else:
report_file = sys.argv[1]
listen = ("127.0.0.1", 10514)
flush_interval = 5
total_msgs = 0
last_flush = 0
rx_buffer = 1024
delimiter = "\t"
statuses = {}
started_at = time.gmtime()
# Clear report file when we first start
with open(report_file, "w") as fh:
print(delimiter.join( ("started", time.strftime("%a, %d %b %Y %H:%M:%S +0000", started_at)) ), file=fh)
print(delimiter.join( ("updated", time.strftime("%a, %d %b %Y %H:%M:%S +0000")) ), file=fh)
print(delimiter.join( ("count", "0") ), file=fh)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(listen)
# For each message, record stats
while True:
message, addr = sock.recvfrom(rx_buffer)
total_msgs += 1
msg_len = len(message)
if msg_len < 5:
continue
try:
status = int(message[msg_len-4:msg_len-1])
except ValueError:
continue;
if status in statuses:
statuses[status] += 1
else:
statuses[status] = 1
now = time.time()
if now - last_flush > flush_interval:
groupstatuses = {
"2xx": 0,
"3xx": 0,
"4xx": 0,
"5xx": 0,
};
with open(report_file, "w") as fh:
print(delimiter.join( ("started", time.strftime("%a, %d %b %Y %H:%M:%S +0000", started_at)) ), file=fh)
print(delimiter.join( ("updated", time.strftime("%a, %d %b %Y %H:%M:%S +0000")) ), file=fh)
print(delimiter.join( ("count", str(total_msgs)) ), file=fh)
for status in statuses:
# Write exact status stats
print(delimiter.join( (str(status), str(statuses[status])) ), file=fh)
if status < 200 or status >= 600:
continue
# Record group status (2xx, 3xx, etc)
groupstatus = "%sxx" % str(status)[0]
groupstatuses[groupstatus] += statuses[status]
for groupstatus in groupstatuses:
# Write group status stats
print(delimiter.join( (groupstatus, str(groupstatuses[groupstatus])) ), file=fh)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment