utils: add gsmtap_logread.py a gsmtap log reader

Receive gsmtap logs and feeds it into the python logging
framework. Allows to use generic logging features and
further utilities.

Change-Id: I24478d8e16066c6118e867bdba54c6418c15e170
diff --git a/utils/gsmtap_logread.py b/utils/gsmtap_logread.py
new file mode 100644
index 0000000..a29f149
--- /dev/null
+++ b/utils/gsmtap_logread.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+#
+# License: MIT
+# Copyright 2019 by Sysmocom s.f.m.c. GmbH
+# Author: Alexander Couzens <lynxis@fe80.eu>
+
+import logging
+import socket
+
+from gsmtap import GSMTAP_TYPE_OSMOCORE_LOG, gsmtap_hdr, gsmtap_log, TooSmall
+
+LOG = logging.getLogger("gsmlogreader")
+
+def parse_gsm(packet):
+    hdr = None
+
+    try:
+        hdr = gsmtap_hdr(packet)
+    except TooSmall:
+        return None
+
+    if hdr.type != GSMTAP_TYPE_OSMOCORE_LOG:
+        return None
+
+    if len(packet) <= hdr.hdr_len:
+        return None
+
+    try:
+        return gsmtap_log(packet[hdr.hdr_len:])
+    except TooSmall:
+        return None
+
+def gsmtaplevel_to_loglevel(level):
+    """ convert a gsmtap log level into a python log level """
+    if level <= 1:
+        return logging.DEBUG
+    if level <= 3:
+        return logging.INFO
+    if level <= 5:
+        return logging.WARNING
+
+    return logging.ERROR
+
+def convert_gsmtap_log(gsmtap):
+    level = gsmtaplevel_to_loglevel(gsmtap.level)
+
+    attr = {
+        "name": "gsmtap",
+        "levelno": level,
+        "levelname": gsmtap_get_logname(gsmtap.level),
+        "pathname": gsmtap.filename,
+        "lineno": gsmtap.fileline_nr,
+        "processName": gsmtap.proc_name,
+        "process": gsmtap.pid,
+        "module": gsmtap.subsys,
+        "created": float(gsmtap.sec + gsmtap.usec / 1000000.0),
+        "msec": int(gsmtap.usec / 1000),
+        "msg": gsmtap.message.replace('\n', ' '),
+        }
+    return attr
+
+def gsmtap_get_logname(level):
+    names = {
+        1: "DEBUG",
+        3: "INFO",
+        5: "NOTICE",
+        7: "ERROR",
+        8: "FATAL",
+        }
+    if level in names:
+        return names[level]
+    return "UNKNOWN"
+
+if __name__ == "__main__":
+    # Create a UDP socket
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    server_address = ('0.0.0.0', 4729)
+    sock.bind(server_address)
+
+    logger = logging.getLogger("gsmtap")
+    logformat = "%(asctime)s %(message)s"
+    logging.basicConfig(format=logformat, level=logging.DEBUG)
+
+
+    while True:
+        data, address = sock.recvfrom(4096)
+        log = parse_gsm(data)
+        if not log:
+            continue
+
+        record = logging.makeLogRecord(convert_gsmtap_log(log))
+        logger.handle(record)