Last active
September 18, 2021 02:56
-
-
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!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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