diff lemuriformes/log.py @ 15:0d1b8bb1d97b

SQL + data related functionality
author Jeff Hammel <k0scist@gmail.com>
date Sun, 10 Dec 2017 17:16:52 -0800
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lemuriformes/log.py	Sun Dec 10 17:16:52 2017 -0800
@@ -0,0 +1,108 @@
+"""
+ElasticSearch-style logging
+"""
+
+import json
+import sys
+import time
+from .cli import ConfigurationParser
+
+stdout = sys.stdout
+
+try:
+    # python 2
+    string = (str, unicode)
+except NameError:
+    # python 3
+    string = (str, )
+
+
+def read_logfile(f):
+    """
+    read a JSON-per-line log file's contents and return the value
+
+    f -- log file pointer or name
+    """
+
+    if isinstance(f, string):
+        with open(f) as _f:
+            return read_logfile(_f)
+    lines = f.read().strip().splitlines()
+    return [json.loads(line) for line in lines]
+
+
+class ElasticLogger(object):
+    """Elasticsearch-compatible log dispatcher"""
+
+    def __init__(self, logfilepath=None, **data):
+        """
+        logfilepath -- path to logfile
+        data -- data to be included with each logging event
+        """
+
+        self.logfilepath = logfilepath
+        self.data = data
+
+    def write(self, f, **data):
+        """
+        write JSON `data` to file-like object `f`
+        """
+
+        f.write(json.dumps(data, sort_keys=True) + '\n')
+        f.flush()
+
+    def __call__(self, message, **kw):
+
+        # create log data JSON blob
+        now = time.time()
+        data = self.data.copy()   # shallow copy
+        data.update({'time': now,
+                     'message': message})
+        data.update(kw)
+
+        # log to stdout
+        self.write(stdout, **data)
+
+        if self.logfilepath:
+            # log to a file
+            with open(self.logfilepath, 'a') as logfile:
+                self.write(logfile, **data)
+
+
+class ElasticLoggerParser(ConfigurationParser):
+
+    def add_arguments(self):
+        self.add_argument('-l', '--log', '--logfile', dest='logfile',
+                          help="where to log events to in addition to stdout")
+        self.add_argument('--tag', dest='tags', nargs='+', default=(),
+                          type=self.keyvalue, metavar="KEY=VALUE",
+                          help="set of key, values to tag all log lines with")
+
+    def logger(self):
+        """return elastic logger instance"""
+
+        assert self.options is not None
+        return ElasticLogger(self.options.logfile,
+                             **dict(self.options.tags))
+
+
+def main(args=sys.argv[1:]):
+    """example CLI program"""
+
+    # parse command line
+    parser = ElasticLoggerParser(description="my elastic diary")
+    options = parser.parse_args()
+
+    # example: timestamped diary
+    logger = parser.logger()
+
+    # main loop
+    try:
+        while True:
+            logger(raw_input())
+    except KeyboardInterrupt:
+        pass
+
+
+if __name__ == '__main__':
+    main()