Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4Extract LVAlert message JSON from ``lvalert_listen`` log files. Reads the log 

5file contents from STDIN and writes the JSON packets to STDOUT as a 

6pretty-printed JSON list of message objects. Errors are logged to STDERR. Keeps 

7reading from STDIN, so in principle you can do something like ``tail -f 

8lvalert_listen.log`` to keep track of new messages in real-time (while also 

9logging them). 

10""" 

11 

12import sys 

13import json 

14import argparse 

15from subprocess import Popen, PIPE 

16 

17 

18def get_parser(): 

19 """Get CLI parser.""" 

20 parser = argparse.ArgumentParser(prog='llama dev log lvalert', 

21 description=__doc__) 

22 arg = parser.add_argument 

23 arg("-s", "--script", default=None, help=""" 

24 A script that should be used as an LVAlert handler for all well-defined 

25 LVALerts found in the log read in from STDIN. The LVAlert Node will be 

26 passed as the first command line argument to this script; the FAR 

27 (False Alarm Rate) will be passed as the second argument. The LVAlert 

28 JSON payload will be passed to this script through STDIN. If this 

29 argument is provided, then nothing will be printed to STDOUT, and 

30 instead, the script defined in this argument will be invoked once in 

31 the described manner for each parsed LVAlert. Only one instance of the 

32 script runs at a time. STDOUT and STDERR from the invoked scripts will 

33 be forwarded to STDOUT and STDERR for this calling script.""") 

34 return parser 

35 

36 

37INDENT_SPACES = 2 

38# this is the string that terminates the line preceding the JSON payload 

39JSON_LOAD_ANNOUNCEMENT_SUFFIX = "INFO lvalert_handler - <module>: JSON:" 

40NODE_DEFINITION_SUFFIX = "INFO lvalert_handler - <module>: LVALERT NODE:" 

41FAR_DEFINITION_SUFFIX = "INFO lvalert_handler - <module>: FAR:" 

42 

43 

44def main(): 

45 """Run the CLI.""" 

46 # print the start of the list (unless we are using the --script argument to 

47 # handle each parsed LVAlert) 

48 # default values for node and FAR 

49 node = far = None 

50 

51 

52 parser = get_parser() 

53 args = parser.parse_args() 

54 if args.script is None: 

55 sys.stdout.write('[') 

56 

57 a_message_has_been_parsed = False 

58 parse_next_line = False 

59 for line in sys.stdin: 

60 if parse_next_line: 

61 try: 

62 message_json = line[line.find('{'):] 

63 message = json.loads(message_json) 

64 # if we are running each LVAlert through a handler script, then run 

65 # that script on that message. otherwise, parse it, reformat it, 

66 # and print it to STDOUT. 

67 if args.script is None: 

68 # indent an extra block since this is all being wrapped in a 

69 # list; strip leading spaces from the indent block so that the 

70 # end of the last message and the start of the next can be on 

71 # the same line 

72 message_str = json.dumps( 

73 message, 

74 indent=INDENT_SPACES 

75 ).strip().replace('\n', '\n'+INDENT_SPACES*' ') 

76 if a_message_has_been_parsed: 

77 message_str = ', ' + message_str 

78 else: 

79 message_str = '\n' + INDENT_SPACES*' ' + message_str 

80 sys.stdout.write(message_str) 

81 a_message_has_been_parsed = True 

82 # FIXME. this doesn't make sense. we haven't set it to the 

83 # value for the next message yet... 

84 elif node is not None: 

85 # we only want to run the LVAlert handler --script if node and 

86 # far can be parsed from a given LVAlert. 

87 try: 

88 far = (far if far is not None else 

89 str(message['object']['far'])) 

90 proc = Popen([args.script, node, far], stdin=PIPE) 

91 proc.communicate(input=message_json) 

92 if proc.returncode: 

93 sys.stderr.write(( 

94 "LVAlert handling script {} failed " 

95 "with script {} and payload: {}\n" 

96 ).format(args.script, proc.returncode, message_json)) 

97 except KeyError: 

98 sys.stderr.write(( 

99 "Could not parse a far from this LVAlert's logs nor " 

100 "payload: {}\n" 

101 ).format(message_json)) 

102 else: 

103 sys.stderr.write(( 

104 "Could not parse the LVAlert node from this LVAlert's " 

105 "logs: {}\n" 

106 ).format(message_json)) 

107 except ValueError: 

108 sys.stderr.write("Can't decode JSON: {}\n".format(line)) 

109 parse_next_line = False 

110 # reset the parsed node and far values after any JSON-parsing attempt 

111 node = far = None 

112 else: 

113 parse_next_line = line.strip().endswith(JSON_LOAD_ANNOUNCEMENT_SUFFIX) 

114 # see if we can parse LVAlert node or far from this line 

115 node_ind = line.find(NODE_DEFINITION_SUFFIX) 

116 if node_ind != -1: 

117 node = line[node_ind:].strip() 

118 far_ind = line.find(FAR_DEFINITION_SUFFIX) 

119 if far_ind != -1: 

120 node = line[far_ind:].strip() 

121 

122 # print the end of the list (unless we are using the --script argument to 

123 # handle each parsed LVAlert) 

124 if args.script is None: 

125 sys.stdout.write(('\n' if a_message_has_been_parsed else '') + ']') 

126 

127 

128if __name__ == "__main__": 

129 main()