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