blob: 5b9a2edcf9bac6fd398946c04755bf5d0ea42a0a [file] [log] [blame]
Austin Schuhd1b28992014-10-26 20:55:06 -07001#!/usr/bin/python3
Dave Smith6b2cb012016-03-06 13:10:31 -08002
Dave Smith6b2cb012016-03-06 13:10:31 -08003import re
4import sys
Philipp Schrader7861dce2015-02-23 00:27:59 +00005
Dave Smith6b2cb012016-03-06 13:10:31 -08006"""
7A regular expression to match the envelope part of the log entry.
8Parsing of the JSON msg is handled elsewhere.
9"""
10LOG_RE = re.compile("""
11 (.*?) # 1 name
12 \((\d+)\) # 2 pid
13 \((\d+)\) # 3 message_index
14 :\s
15 (\w+?) # 4 level
16 \s+at\s+
17 (\d+\.\d+)s # 5 time
18 :\s
19 ([A-Za-z0-9_./-]+) # 6 filename
20 :\s
21 (\d+) # 7 linenumber
22 :\s
23 (.*) # 8 msg
24 """, re.VERBOSE)
25
Austin Schuhd1b28992014-10-26 20:55:06 -070026class LogEntry:
Dave Smithacbca192016-03-06 15:27:23 -080027 """
28 This class provides a way to parse log entries.
29 The header portion of the log entry is parsed eagerly.
30 The structured portion of a log entry is parsed on demand.
31 """
Austin Schuhd1b28992014-10-26 20:55:06 -070032
33 def __init__(self, line):
Dave Smith6b2cb012016-03-06 13:10:31 -080034 """Populates a LogEntry from a line."""
35 m = LOG_RE.match(line)
36 if m is None:
37 print("LOG_RE failed on", line)
38 sys.exit(1)
39 self.name = m.group(1)
40 self.pid_index = int(m.group(2))
41 self.msg_index = int(m.group(3))
42 self.level = m.group(4)
43 self.time = float(m.group(5))
44 self.filename = m.group(6)
45 self.linenumber = m.group(7)
46 self.msg = m.group(8)
Austin Schuhd1b28992014-10-26 20:55:06 -070047
48 def __str__(self):
49 """Formats the data cleanly."""
Dave Smithacbca192016-03-06 15:27:23 -080050 return '%s(%d)(%d): %s at %fs: %s: %d: %s' % (
51 self.name, self.pid, self.msg_index, self.level, self.time, self.filename, self.linenumber, self.msg)
Austin Schuhd1b28992014-10-26 20:55:06 -070052
Dave Smithacbca192016-03-06 15:27:23 -080053 def ParseStruct(self):
54 """Parses the message as a structure.
Austin Schuhd1b28992014-10-26 20:55:06 -070055
56 Returns:
Dave Smithacbca192016-03-06 15:27:23 -080057 struct_name, struct_type, json dict.
Austin Schuhd1b28992014-10-26 20:55:06 -070058 """
Dave Smithacbca192016-03-06 15:27:23 -080059 struct_name_index = self.msg.find(':')
60 struct_name = self.msg[0:struct_name_index]
Austin Schuhd1b28992014-10-26 20:55:06 -070061
Dave Smithacbca192016-03-06 15:27:23 -080062 struct_body = self.msg[struct_name_index+2:]
63 tokens = []
64 this_token = ''
65 # For the various deliminators, append what we have found so far to the
66 # list and the token.
67 for char in struct_body:
68 if char == '{':
69 if this_token:
70 tokens.append(this_token)
71 this_token = ''
72 tokens.append('{')
73 elif char == '}':
74 if this_token:
75 tokens.append(this_token)
76 this_token = ''
77 tokens.append('}')
78 elif char == '[':
79 if this_token:
80 tokens.append(this_token)
81 this_token = ''
82 tokens.append('[')
83 elif char == ']':
84 if this_token:
85 tokens.append(this_token)
86 this_token = ''
87 tokens.append(']')
88 elif char == ':':
89 if this_token:
90 tokens.append(this_token)
91 this_token = ''
92 tokens.append(':')
93 elif char == ',':
94 if this_token:
95 tokens.append(this_token)
96 this_token = ''
97 tokens.append(',')
98 elif char == ' ':
99 if this_token:
100 tokens.append(this_token)
101 this_token = ''
Austin Schuhd1b28992014-10-26 20:55:06 -0700102 else:
Dave Smithacbca192016-03-06 15:27:23 -0800103 this_token += char
104 if this_token:
105 tokens.append(this_token)
Austin Schuhd1b28992014-10-26 20:55:06 -0700106
Dave Smithacbca192016-03-06 15:27:23 -0800107 struct_type = tokens[0]
108 json = dict()
109 # Now that we have tokens, parse them.
110 self.JsonizeTokens(json, tokens, 1)
111
112 return (struct_name, struct_type, json)
Austin Schuhd1b28992014-10-26 20:55:06 -0700113
114 def JsonizeTokens(self, json, tokens, token_index):
115 """Creates a json-like dictionary from the provided tokens.
116
117 Args:
118 json: dict, The dict to stick the elements in.
119 tokens: list of strings, The list with all the tokens in it.
120 token_index: int, Where to start in the token list.
121
122 Returns:
123 int, The last token used.
124 """
125 # Check that the message starts with a {
126 if tokens[token_index] != '{':
127 print(tokens)
128 print('Expected { at beginning, found', tokens[token_index])
129 return None
130
131 # Eat the {
132 token_index += 1
133
134 # States and state variable for parsing elements.
135 STATE_INIT = 'init'
136 STATE_HAS_NAME = 'name'
137 STATE_HAS_COLON = 'colon'
138 STATE_EXPECTING_SUBMSG = 'submsg'
139 STATE_EXPECTING_COMMA = 'comma'
140 parser_state = STATE_INIT
141
142 while token_index < len(tokens):
143 if tokens[token_index] == '}':
144 # Finish if there is a }
145 return token_index + 1
146 elif tokens[token_index] == '{':
147 if parser_state != STATE_EXPECTING_SUBMSG:
148 print(tokens)
149 print(parser_state)
150 print('Bad input, was not expecting {')
151 return None
152 # Found a submessage, parse it.
153 sub_json = dict()
154 token_index = self.JsonizeTokens(sub_json, tokens, token_index)
155 json[token_name] = sub_json
156 parser_state = STATE_EXPECTING_COMMA
157 else:
158 if parser_state == STATE_INIT:
159 # This token is the name.
160 token_name = tokens[token_index]
161 parser_state = STATE_HAS_NAME
162 elif parser_state == STATE_HAS_NAME:
163 if tokens[token_index] != ':':
164 print(tokens)
165 print(parser_state)
166 print('Bad input, found', tokens[token_index], 'expected :')
167 return None
168 # After a name, comes a :
169 parser_state = STATE_HAS_COLON
170 elif parser_state == STATE_HAS_COLON:
171 # After the colon, figure out what is next.
172 if tokens[token_index] == '[':
173 # Found a sub-array!
174 sub_array = []
175 token_index = self.__JsonizeTokenArray(sub_array, tokens, token_index)
176 json[token_name] = sub_array
177 parser_state = STATE_EXPECTING_COMMA
178 elif tokens[token_index + 1] == '{':
179 # Found a sub-message, trigger parsing it.
180 parser_state = STATE_EXPECTING_SUBMSG
181 else:
182 # This is just an element, move on.
183 json[token_name] = tokens[token_index]
184 parser_state = STATE_EXPECTING_COMMA
185 elif parser_state == STATE_EXPECTING_COMMA:
186 # Complain if there isn't a comma here.
187 if tokens[token_index] != ',':
188 print(tokens)
189 print(parser_state)
190 print('Bad input, found', tokens[token_index], 'expected ,')
191 return None
192 parser_state = STATE_INIT
193 else:
194 print('Bad parser state')
195 return None
196 token_index += 1
197
198 print('Unexpected end')
199 return None
200
Dave Smithacbca192016-03-06 15:27:23 -0800201 def __JsonizeTokenArray(self, sub_array, tokens, token_index):
202 """Parses an array from the provided tokens.
203
204 Args:
205 sub_array: list, The list to stick the elements in.
206 tokens: list of strings, The list with all the tokens in it.
207 token_index: int, Where to start in the token list.
Austin Schuhd1b28992014-10-26 20:55:06 -0700208
209 Returns:
Dave Smithacbca192016-03-06 15:27:23 -0800210 int, The last token used.
Austin Schuhd1b28992014-10-26 20:55:06 -0700211 """
Dave Smithacbca192016-03-06 15:27:23 -0800212 # Make sure the data starts with a '['
213 if tokens[token_index] != '[':
214 print(tokens)
215 print('Expected [ at beginning, found', tokens[token_index + 1])
216 return None
Austin Schuhd1b28992014-10-26 20:55:06 -0700217
Dave Smithacbca192016-03-06 15:27:23 -0800218 # Eat the '['
219 token_index += 1
220
221 # Loop through the tokens.
222 while token_index < len(tokens):
223 if tokens[token_index + 1] == ',':
224 # Next item is a comma, so we should just add the element.
225 sub_array.append(tokens[token_index])
226 token_index += 2
227 elif tokens[token_index + 1] == ']':
228 # Next item is a ']', so we should just add the element and finish.
229 sub_array.append(tokens[token_index])
230 token_index += 1
231 return token_index
Austin Schuhd1b28992014-10-26 20:55:06 -0700232 else:
Dave Smithacbca192016-03-06 15:27:23 -0800233 # Otherwise, it must be a sub-message.
234 sub_json = dict()
235 token_index = self.JsonizeTokens(sub_json, tokens, token_index + 1)
236 sub_array.append(sub_json)
237 if tokens[token_index] == ',':
238 # Handle there either being another data element.
239 token_index += 1
240 elif tokens[token_index] == ']':
241 # Handle the end of the array.
242 return token_index
243 else:
244 print('Unexpected ', tokens[token_index])
245 return None
Austin Schuhd1b28992014-10-26 20:55:06 -0700246
Dave Smithacbca192016-03-06 15:27:23 -0800247 print('Unexpected end')
248 return None
Austin Schuhd1b28992014-10-26 20:55:06 -0700249
Austin Schuhd1b28992014-10-26 20:55:06 -0700250
251if __name__ == '__main__':
Dave Smithacbca192016-03-06 15:27:23 -0800252 def ParseLine(line):
253 return LogEntry(line)
254
Austin Schuhd1b28992014-10-26 20:55:06 -0700255 print('motor_writer(2240)(07421): DEBUG at 0000000819.99620s: ../../frc971/output/motor_writer.cc: 105: sending: .aos.controls.OutputCheck{pwm_value:221, pulse_length:2.233333}')
256 line = ParseLine('motor_writer(2240)(07421): DEBUG at 0000000819.99620s: ../../frc971/output/motor_writer.cc: 105: sending: .aos.controls.OutputCheck{pwm_value:221, pulse_length:2.233333}')
257 if '.aos.controls.OutputCheck' in line.msg:
258 print(line)
259 print(line.ParseStruct())
260
261 line = ParseLine('claw(2263)(19404): DEBUG at 0000000820.00000s: ../../aos/common/controls/control_loop-tmpl.h: 104: position: .frc971.control_loops.ClawGroup.Position{top:.frc971.control_loops.HalfClawPosition{position:1.672153, front:.frc971.HallEffectStruct{current:f, posedge_count:0, negedge_count:52}, calibration:.frc971.HallEffectStruct{current:f, posedge_count:6, negedge_count:13}, back:.frc971.HallEffectStruct{current:f, posedge_count:0, negedge_count:62}, posedge_value:0.642681, negedge_value:0.922207}, bottom:.frc971.control_loops.HalfClawPosition{position:1.353539, front:.frc971.HallEffectStruct{current:f, posedge_count:2, negedge_count:150}, calibration:.frc971.HallEffectStruct{current:f, posedge_count:8, negedge_count:18}, back:.frc971.HallEffectStruct{current:f, posedge_count:0, negedge_count:6}, posedge_value:0.434514, negedge_value:0.759491}}')
262 print(line.ParseStruct())
263
264 line = ParseLine('joystick_proxy(2255)(39560): DEBUG at 0000000820.00730s: ../../aos/prime/input/joystick_input.cc: 61: sending: .aos.RobotState{joysticks:[.aos.Joystick{buttons:0, axis:[0.000000, 1.000000, 1.000000, 0.000000]}, .aos.Joystick{buttons:0, axis:[-0.401575, 1.000000, -1.007874, 0.000000]}, .aos.Joystick{buttons:0, axis:[0.007874, 0.000000, 1.000000, -1.007874]}, .aos.Joystick{buttons:0, axis:[0.000000, 0.000000, 0.000000, 0.000000]}], test_mode:f, fms_attached:f, enabled:T, autonomous:f, team_id:971, fake:f}')
265 print(line.ParseStruct())