OLD | NEW |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import argparse | 5 import argparse |
6 import glob | 6 import glob |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import json | 9 import json |
10 import sys | 10 import sys |
11 | 11 |
12 from infra_libs import event_mon | 12 from infra_libs import event_mon |
| 13 from infra_libs import ts_mon |
13 import infra_libs.logs | 14 import infra_libs.logs |
14 from infra_libs import ts_mon | |
15 | 15 |
| 16 from infra_libs.event_mon.protos.log_request_lite_pb2 import LogRequestLite |
| 17 from infra_libs.event_mon.protos.chrome_infra_log_pb2 import ChromeInfraEvent |
| 18 from infra_libs.event_mon.protos.goma_stats_pb2 import GomaStats |
16 | 19 |
17 LOGGER = logging.getLogger(__name__) | 20 LOGGER = logging.getLogger(__name__) |
18 | 21 |
19 | 22 |
20 def get_arguments(argv): | 23 def get_arguments(argv): |
21 """Process command-line arguments. | 24 """Process command-line arguments. |
22 | 25 |
23 Args: | 26 Args: |
24 argv (list of strings): sys.argv[1:] | 27 argv (list of strings): sys.argv[1:] |
25 Returns: | 28 Returns: |
(...skipping 13 matching lines...) Expand all Loading... |
39 --service-event-stack-trace "<stack trace>" | 42 --service-event-stack-trace "<stack trace>" |
40 | 43 |
41 run.py infra.tools.send_monitoring_event --build-event-type=SCHEDULER \\ | 44 run.py infra.tools.send_monitoring_event --build-event-type=SCHEDULER \\ |
42 --build-event-build-name=foo | 45 --build-event-build-name=foo |
43 --build-event-hostname='bot.dns.name' | 46 --build-event-hostname='bot.dns.name' |
44 """, formatter_class=argparse.RawTextHelpFormatter) | 47 """, formatter_class=argparse.RawTextHelpFormatter) |
45 | 48 |
46 # Common fields | 49 # Common fields |
47 common_group = parser.add_argument_group('Common event options') | 50 common_group = parser.add_argument_group('Common event options') |
48 common_group.add_argument('--event-mon-timestamp-kind', | 51 common_group.add_argument('--event-mon-timestamp-kind', |
49 choices=[kind | 52 choices=event_mon.TIMESTAMP_KINDS, |
50 for kind in event_mon.TIMESTAMP_KINDS | |
51 if kind], | |
52 default='POINT', | |
53 help='General kind of event. This value is used ' | 53 help='General kind of event. This value is used ' |
54 'e.g. to\nautomatically compute durations between ' | 54 'e.g. to\nautomatically compute durations between ' |
55 'START and STOP\nevents. Default: %(default)s') | 55 'START and STOP\nevents.') |
56 common_group.add_argument('--event-mon-event-timestamp', type=int, | 56 common_group.add_argument('--event-mon-event-timestamp', type=int, |
57 help='Timestamp when the event was generated, as ' | 57 help='Timestamp when the event was generated, as ' |
58 'number of\nmilliseconds since the Unix EPOCH.' | 58 'number of\nmilliseconds since the Unix EPOCH.' |
59 '\nDefaults to current time.') | 59 '\nDefaults to current time.') |
60 | 60 |
61 # Service event | 61 # Service event |
62 service_group = parser.add_argument_group('Service event options') | 62 service_group = parser.add_argument_group('Service event options') |
63 type_group = service_group.add_mutually_exclusive_group() | 63 type_group = service_group.add_mutually_exclusive_group() |
64 type_group.add_argument('--service-event-type', | 64 type_group.add_argument('--service-event-type', |
65 choices=event_mon.EVENT_TYPES, | 65 choices=event_mon.EVENT_TYPES, |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 choices=event_mon.BUILD_RESULTS, | 114 choices=event_mon.BUILD_RESULTS, |
115 help='Result of build or step depending on ' | 115 help='Result of build or step depending on ' |
116 'whether any \n--build-event-step-* options have ' | 116 'whether any \n--build-event-step-* options have ' |
117 'been provided or not.') | 117 'been provided or not.') |
118 | 118 |
119 build_group.add_argument('--build-event-extra-result-code', | 119 build_group.add_argument('--build-event-extra-result-code', |
120 help='Extra result code. String, comma-separated ' | 120 help='Extra result code. String, comma-separated ' |
121 'list of strings or json-encoded list of string. ' | 121 'list of strings or json-encoded list of string. ' |
122 'Each one must be less than 20 characters long.') | 122 'Each one must be less than 20 characters long.') |
123 | 123 |
| 124 build_group.add_argument('--build-event-goma-stats-path', |
| 125 metavar='FILENAME', |
| 126 help='File containing a serialized GomaStats ' |
| 127 'protobuf.') |
| 128 |
124 # Read events from file | 129 # Read events from file |
125 file_group = parser.add_argument_group('Read events from file') | 130 file_group = parser.add_argument_group('Read events from file') |
126 file_group.add_argument('--events-from-file', | 131 file_group.add_argument('--events-from-file', |
127 metavar='FILENAME', nargs='*', | 132 metavar='FILENAME', nargs='*', |
128 help='File containing events as json dict. This ' | 133 help='File containing events as json dict. This ' |
129 'option\nis incompatible with --build-event-type and' | 134 'option\nis incompatible with --build-event-type and' |
130 '\n--service-event-type.\nSee ' | 135 '\n--service-event-type.\nSee ' |
131 'send_event.read_events_from_file for details\n' | 136 'send_event.read_events_from_file for details\n' |
132 'on the format. This options can be passed multiple\n' | 137 'on the format. This option can be passed multiple\n' |
133 'times, and wildcards can be used.') | 138 'times, and wildcards can be used.') |
134 file_group.add_argument('--delete-file-when-sent', | 139 file_group.add_argument('--delete-file-when-sent', |
135 action='store_true', default=False, | 140 action='store_true', default=False, |
136 help='If all events read from a file have been ' | 141 help='If all events read from a file have been ' |
137 'successfully\nsent to the endpoint, delete the ' | 142 'successfully\nsent to the endpoint, delete the ' |
138 'file. By default\nfiles are kept.') | 143 'file. By default\nfiles are kept. This does not ' |
| 144 'affect the file pointed to by ' |
| 145 '--event-logrequest-path') |
| 146 |
| 147 file_group.add_argument('--event-logrequest-path', |
| 148 metavar='FILENAME', |
| 149 help='File containing a serialized LogRequestLite' |
| 150 'proto, containing a single ChromeInfraEvent that ' |
| 151 'will be used as the default event. Such a file can ' |
| 152 'be generated by passing "file" to ' |
| 153 '--event-mon-run-type.') |
139 | 154 |
140 ts_mon.add_argparse_options(parser) | 155 ts_mon.add_argparse_options(parser) |
141 event_mon.add_argparse_options(parser) | 156 event_mon.add_argparse_options(parser) |
142 infra_libs.logs.add_argparse_options(parser) | 157 infra_libs.logs.add_argparse_options(parser) |
143 | 158 |
144 parser.set_defaults( | 159 parser.set_defaults( |
145 ts_mon_flush='manual', | 160 ts_mon_flush='manual', |
146 ts_mon_target_type='task', | 161 ts_mon_target_type='task', |
147 ts_mon_task_service_name='send_monitoring_event', | 162 ts_mon_task_service_name='send_monitoring_event', |
148 ts_mon_task_job_name='manual', | 163 ts_mon_task_job_name='manual', |
(...skipping 17 matching lines...) Expand all Loading... |
166 extra_result_code = args.build_event_extra_result_code.strip() | 181 extra_result_code = args.build_event_extra_result_code.strip() |
167 if extra_result_code.startswith('['): | 182 if extra_result_code.startswith('['): |
168 extra_result_code = json.loads(extra_result_code) | 183 extra_result_code = json.loads(extra_result_code) |
169 elif ',' in extra_result_code: | 184 elif ',' in extra_result_code: |
170 extra_result_code = extra_result_code.split(',') | 185 extra_result_code = extra_result_code.split(',') |
171 args.build_event_extra_result_code = extra_result_code | 186 args.build_event_extra_result_code = extra_result_code |
172 | 187 |
173 return args | 188 return args |
174 | 189 |
175 | 190 |
| 191 def _process_logrequest_path(args): |
| 192 """Sets the default event based on --event-logrequest-path. |
| 193 |
| 194 This function raises exceptions because if the base event is wrong, then it's |
| 195 not worth sending anything anyway. |
| 196 """ |
| 197 if args.event_logrequest_path: |
| 198 try: |
| 199 with open(args.event_logrequest_path, 'rb') as f: |
| 200 request = LogRequestLite.FromString(f.read()) |
| 201 |
| 202 if len(request.log_event) == 1: |
| 203 default_event = ChromeInfraEvent.FromString( |
| 204 request.log_event[0].source_extension) |
| 205 # Assume that the content is sane because we don't want to duplicate |
| 206 # any business logic here. |
| 207 # TODO(pgervais): find a better solution. |
| 208 event_mon.set_default_event(default_event) |
| 209 else: |
| 210 raise ValueError( |
| 211 'Expected only one log_event in the LogRequestLite proto ' |
| 212 'pointed by --event-logrequest-path. Found %d in %s', |
| 213 len(request.log_event), args.event_logrequest_path) |
| 214 except Exception: |
| 215 LOGGER.exception('Failure when reading/parsing file %s', |
| 216 args.event_logrequest_path) |
| 217 raise |
| 218 |
| 219 default_event = event_mon.get_default_event() |
| 220 |
| 221 # When the default event is set using --event-logrequest-path, passing |
| 222 # --build-event-type or --service-event-type is optional. These options |
| 223 # still takes precedence but they must keep the event type the same. |
| 224 if (default_event.build_event.HasField('type') and |
| 225 default_event.service_event.HasField('type')): |
| 226 msg = ('Default event contains both service_event_type and ' |
| 227 'build_event_type which is incorrect. Make sure you passed ' |
| 228 'a correct proto to --event-logrequest-path. Otherwise it\'s an ' |
| 229 'internal error. Aborting.') |
| 230 LOGGER.error(msg) |
| 231 raise ValueError(msg) |
| 232 |
| 233 if default_event.build_event.HasField('type'): |
| 234 if args.service_event_type: |
| 235 msg = ('The default event contains a type for build_event, but a ' |
| 236 'service_event type was provided on the command-line. At most ' |
| 237 'one of them can be specified. Aborting.') |
| 238 LOGGER.error(msg) |
| 239 raise ValueError(msg) |
| 240 |
| 241 if not args.build_event_type: |
| 242 args.build_event_type = event_mon.BuildEvent.BuildEventType.Name( |
| 243 default_event.build_event.type) |
| 244 |
| 245 if default_event.service_event.HasField('type'): |
| 246 if args.build_event_type: |
| 247 msg = ('The default event contains a type for service_event, but a ' |
| 248 'build_event type was provided on the command-line. At most ' |
| 249 'one of them can be specified. Aborting.') |
| 250 LOGGER.error(msg) |
| 251 raise ValueError(msg) |
| 252 if not args.service_event_type: |
| 253 args.service_event_type = event_mon.ServiceEvent.ServiceEventType.Name( |
| 254 default_event.service_event.type) |
| 255 |
| 256 |
176 def process_argparse_options(args): # pragma: no cover | 257 def process_argparse_options(args): # pragma: no cover |
177 infra_libs.logs.process_argparse_options(args) | 258 infra_libs.logs.process_argparse_options(args) |
178 event_mon.process_argparse_options(args) | 259 event_mon.process_argparse_options(args) |
| 260 # Put here because we want to send the presence metric as late as possible. |
179 ts_mon.process_argparse_options(args) | 261 ts_mon.process_argparse_options(args) |
| 262 _process_logrequest_path(args) |
180 | 263 |
181 | 264 |
182 def send_service_event(args): | 265 def send_service_event(args): |
183 """Entry point when --service-event-type is passed.""" | 266 """Entry point when --service-event-type is passed.""" |
184 revinfo = {} | 267 revinfo = {} |
185 if args.service_event_revinfo: | 268 if args.service_event_revinfo: |
186 if args.service_event_revinfo == '-': # pragma: no cover | 269 if args.service_event_revinfo == '-': # pragma: no cover |
187 revinfo = event_mon.parse_revinfo(sys.stdin.read()) | 270 revinfo = event_mon.parse_revinfo(sys.stdin.read()) |
188 else: | 271 else: |
189 with open(args.service_event_revinfo, 'r') as f: | 272 with open(args.service_event_revinfo, 'r') as f: |
190 revinfo = event_mon.parse_revinfo(f.read()) | 273 revinfo = event_mon.parse_revinfo(f.read()) |
191 elif args.service_event_revinfo_from_gclient: # pragma: no cover | 274 elif args.service_event_revinfo_from_gclient: # pragma: no cover |
192 revinfo = event_mon.get_revinfo() | 275 revinfo = event_mon.get_revinfo() |
193 | 276 |
194 if args.service_event_stack_trace: | 277 if args.service_event_stack_trace: |
195 args.service_event_type = 'CRASH' | 278 args.service_event_type = 'CRASH' |
196 | 279 |
197 return bool(event_mon.send_service_event( | 280 return bool(event_mon.send_service_event( |
198 args.service_event_type, | 281 args.service_event_type, |
199 code_version=revinfo.values(), | 282 code_version=revinfo.values(), |
200 stack_trace=args.service_event_stack_trace, | 283 stack_trace=args.service_event_stack_trace, |
201 timestamp_kind=args.event_mon_timestamp_kind, | 284 timestamp_kind=args.event_mon_timestamp_kind, |
202 event_timestamp=args.event_mon_event_timestamp)) | 285 event_timestamp=args.event_mon_event_timestamp)) |
203 | 286 |
204 | 287 |
205 def send_build_event(args): | 288 def send_build_event(args): |
206 """Entry point when --build-event-type is passed.""" | 289 """Entry point when --build-event-type is passed.""" |
| 290 goma_stats = None |
| 291 if args.build_event_goma_stats_path: |
| 292 try: |
| 293 with open(args.build_event_goma_stats_path, 'rb') as f: |
| 294 goma_stats = GomaStats.FromString(f.read()) |
| 295 except Exception: |
| 296 LOGGER.exception('Failure when reading/parsing file %s', |
| 297 args.build_event_goma_stats_path) |
| 298 raise |
| 299 |
207 | 300 |
208 return bool(event_mon.send_build_event( | 301 return bool(event_mon.send_build_event( |
209 args.build_event_type, | 302 args.build_event_type, |
210 args.build_event_hostname, | 303 args.build_event_hostname, |
211 args.build_event_build_name, | 304 args.build_event_build_name, |
212 build_number=args.build_event_build_number, | 305 build_number=args.build_event_build_number, |
213 build_scheduling_time=args.build_event_build_scheduling_time, | 306 build_scheduling_time=args.build_event_build_scheduling_time, |
214 step_name=args.build_event_step_name, | 307 step_name=args.build_event_step_name, |
215 step_number=args.build_event_step_number, | 308 step_number=args.build_event_step_number, |
216 result=args.build_event_result, | 309 result=args.build_event_result, |
217 extra_result_code=args.build_event_extra_result_code, | 310 extra_result_code=args.build_event_extra_result_code, |
218 timestamp_kind=args.event_mon_timestamp_kind, | 311 timestamp_kind=args.event_mon_timestamp_kind, |
219 event_timestamp=args.event_mon_event_timestamp)) | 312 event_timestamp=args.event_mon_event_timestamp, |
| 313 goma_stats=goma_stats)) |
220 | 314 |
221 | 315 |
222 def send_events_from_file(args): | 316 def send_events_from_file(args): |
223 """Entry point when --events-from-file is passed.""" | 317 """Entry point when --events-from-file is passed.""" |
224 file_list = get_event_file_list(args.events_from_file) | 318 file_list = get_event_file_list(args.events_from_file) |
225 status = True | 319 status = True |
226 | 320 |
227 LOGGER.info("Processing %d files", len(file_list)) | 321 LOGGER.info("Processing %d files", len(file_list)) |
228 for filename in file_list: | 322 for filename in file_list: |
229 LOGGER.info('Processing %s', filename) | 323 LOGGER.info('Processing %s', filename) |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
295 step_number=args.get('build-event-step-number'), | 389 step_number=args.get('build-event-step-number'), |
296 result=args.get('build-event-result'), | 390 result=args.get('build-event-result'), |
297 extra_result_code=args.get('build-event-extra-result-code'), | 391 extra_result_code=args.get('build-event-extra-result-code'), |
298 timestamp_kind=args.get('event-mon-timestamp-kind'), | 392 timestamp_kind=args.get('event-mon-timestamp-kind'), |
299 event_timestamp=args.get('event-mon-event-timestamp'), | 393 event_timestamp=args.get('event-mon-event-timestamp'), |
300 service_name=args.get('event-mon-service-name'))) | 394 service_name=args.get('event-mon-service-name'))) |
301 else: | 395 else: |
302 LOGGER.warning('build-event-type field not found, skipping line ' | 396 LOGGER.warning('build-event-type field not found, skipping line ' |
303 '%d in %s', lineno, filename) | 397 '%d in %s', lineno, filename) |
304 return events | 398 return events |
OLD | NEW |