#!/usr/bin/python3
-#
-# Reads log files from tinydns and/or dnscache and prints them out in
-# human-readable form. Logs can be supplied on stdin, or listed on the
-# command line:
-#
-# $ cat @*.s | djbdns-logparse
-# $ djbdns-logparse @*.s
-# $ tail -f current | djbdns-logparse
-#
-# Pipes each log file through tai64nlocal, which must be on your path.
-#
-# Acknowledgments:
-#
-# * The log format descriptions by Rob Mayoff were invaluable:
-# ** http://dqd.com/~mayoff/notes/djbdns/tinydns-log.html
-# ** http://dqd.com/~mayoff/notes/djbdns/dnscache-log.html
-#
-# * Faried Nawaz's dnscache log parser was the original inspiration:
-# ** http://www.hungry.com/~fn/dnscache-log.pl.txt
-#
-
-import sys, re
+"""
+Convert tinydns and dnscache logs to human-readable form
+"""
+
+import re
from struct import pack
from time import strftime, gmtime
from subprocess import Popen, PIPE
+
# common components of line-matching regexes
timestamp_pat = r'[\d-]+ [\d:\.]+' # output of tai64nlocal
hex4_pat = r'[0-9a-f]{4}'
}
-def warn(filename, msg):
- sys.stderr.write("warning: %s: %s\n" % (filename, msg))
-
def convert_ip(ip):
"""Convert a hex string representing an IP address to conventional
human-readable form, ie. dotted-quad decimal for IPv4, and
type = int(type, 16) # "001c" -> 28
type = query_type.get(type, type) # 28 -> "aaaa"
- print(timestamp,)
+ print(timestamp, end=' ')
if code == "+":
print ("sent response to %s:%s (id %s): %s %s"
% (code, ip, port, id, type, name))
-def parse_logfile(file, filename):
+def parse_logfile(file):
# Open pipe to tai64nlocal: we will write lines of our input (the
# raw log file) to it, and read log lines with readable timestamps
# from it.
handle_dnscache_log(line, match)
continue
- sys.stdout.write(line)
+ print(line)
def main():
- if len(sys.argv) > 1:
- for filename in sys.argv[1:]:
- if filename == "-":
- parse_logfile(sys.stdin, "(stdin)")
- else:
- with open(filename) as file:
- parse_logfile(file, filename)
- else:
- parse_logfile(sys.stdin, "(stdin)")
+ # Create an argument parser using the file's docsctring as its
+ # description.
+ from argparse import ArgumentParser, FileType
+ parser = ArgumentParser(description = __doc__)
+
+ # Parse zero or more positional arguments into a list of
+ # "logfiles". If none are given, read from stdin instead.
+ from sys import stdin
+ parser.add_argument("logfiles",
+ metavar="LOGFILE",
+ type=FileType("r"),
+ nargs="*",
+ default=[stdin],
+ help="djbdns logfile to process (default: stdin)")
+
+ args = parser.parse_args()
+ for f in args.logfiles:
+ parse_logfile(f)
+