Mercurial > hg > Lemuriformes
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 14:756dbd3e391e | 15:0d1b8bb1d97b |
|---|---|
| 1 """ | |
| 2 ElasticSearch-style logging | |
| 3 """ | |
| 4 | |
| 5 import json | |
| 6 import sys | |
| 7 import time | |
| 8 from .cli import ConfigurationParser | |
| 9 | |
| 10 stdout = sys.stdout | |
| 11 | |
| 12 try: | |
| 13 # python 2 | |
| 14 string = (str, unicode) | |
| 15 except NameError: | |
| 16 # python 3 | |
| 17 string = (str, ) | |
| 18 | |
| 19 | |
| 20 def read_logfile(f): | |
| 21 """ | |
| 22 read a JSON-per-line log file's contents and return the value | |
| 23 | |
| 24 f -- log file pointer or name | |
| 25 """ | |
| 26 | |
| 27 if isinstance(f, string): | |
| 28 with open(f) as _f: | |
| 29 return read_logfile(_f) | |
| 30 lines = f.read().strip().splitlines() | |
| 31 return [json.loads(line) for line in lines] | |
| 32 | |
| 33 | |
| 34 class ElasticLogger(object): | |
| 35 """Elasticsearch-compatible log dispatcher""" | |
| 36 | |
| 37 def __init__(self, logfilepath=None, **data): | |
| 38 """ | |
| 39 logfilepath -- path to logfile | |
| 40 data -- data to be included with each logging event | |
| 41 """ | |
| 42 | |
| 43 self.logfilepath = logfilepath | |
| 44 self.data = data | |
| 45 | |
| 46 def write(self, f, **data): | |
| 47 """ | |
| 48 write JSON `data` to file-like object `f` | |
| 49 """ | |
| 50 | |
| 51 f.write(json.dumps(data, sort_keys=True) + '\n') | |
| 52 f.flush() | |
| 53 | |
| 54 def __call__(self, message, **kw): | |
| 55 | |
| 56 # create log data JSON blob | |
| 57 now = time.time() | |
| 58 data = self.data.copy() # shallow copy | |
| 59 data.update({'time': now, | |
| 60 'message': message}) | |
| 61 data.update(kw) | |
| 62 | |
| 63 # log to stdout | |
| 64 self.write(stdout, **data) | |
| 65 | |
| 66 if self.logfilepath: | |
| 67 # log to a file | |
| 68 with open(self.logfilepath, 'a') as logfile: | |
| 69 self.write(logfile, **data) | |
| 70 | |
| 71 | |
| 72 class ElasticLoggerParser(ConfigurationParser): | |
| 73 | |
| 74 def add_arguments(self): | |
| 75 self.add_argument('-l', '--log', '--logfile', dest='logfile', | |
| 76 help="where to log events to in addition to stdout") | |
| 77 self.add_argument('--tag', dest='tags', nargs='+', default=(), | |
| 78 type=self.keyvalue, metavar="KEY=VALUE", | |
| 79 help="set of key, values to tag all log lines with") | |
| 80 | |
| 81 def logger(self): | |
| 82 """return elastic logger instance""" | |
| 83 | |
| 84 assert self.options is not None | |
| 85 return ElasticLogger(self.options.logfile, | |
| 86 **dict(self.options.tags)) | |
| 87 | |
| 88 | |
| 89 def main(args=sys.argv[1:]): | |
| 90 """example CLI program""" | |
| 91 | |
| 92 # parse command line | |
| 93 parser = ElasticLoggerParser(description="my elastic diary") | |
| 94 options = parser.parse_args() | |
| 95 | |
| 96 # example: timestamped diary | |
| 97 logger = parser.logger() | |
| 98 | |
| 99 # main loop | |
| 100 try: | |
| 101 while True: | |
| 102 logger(raw_input()) | |
| 103 except KeyboardInterrupt: | |
| 104 pass | |
| 105 | |
| 106 | |
| 107 if __name__ == '__main__': | |
| 108 main() |
