view lemuriformes/log.py @ 18:56596902e9ae default tip

add some setup + tests
author Jeff Hammel <k0scist@gmail.com>
date Sun, 10 Dec 2017 17:57:03 -0800
parents 0d1b8bb1d97b
children
line wrap: on
line source

"""
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()