def _get_chrome_infra_event(timestamp_kind, service_name=None): """Compute a basic event. Validates the inputs and returns a pre-filled ChromeInfraEvent or None if any check failed. The proto is filled using values provided in setup_monitoring() at initialization time, and args. Args: timestamp_kind (string): any of ('POINT', 'BEGIN', 'END'). Returns: event (chrome_infra_log_pb2.ChromeInfraEvent): """ # Testing for None because we want an error message when timestamp_kind == ''. if timestamp_kind is not None and timestamp_kind not in TIMESTAMP_KINDS: logging.error('Invalid value for timestamp_kind: %s', timestamp_kind) return None # We must accept unicode here. if service_name is not None and not isinstance(service_name, basestring): logging.error('Invalid type for service_name: %s', type(service_name)) return None event = ChromeInfraEvent() event.CopyFrom(config._cache['default_event']) if timestamp_kind: event.timestamp_kind = ChromeInfraEvent.TimestampKind.Value(timestamp_kind) if service_name: event.event_source.service_name = service_name return event
def get_default_event(): """Returns a copy of the default event.""" # We return a copy here to tell people not to modify the event directly. ret = ChromeInfraEvent() ret.CopyFrom(_cache['default_event']) return ret
def _send_to_endpoint(self, events): try: for ev in events.log_event: ev_str = str(ChromeInfraEvent.FromString(ev.source_extension)) logging.log(self.severity, 'Sending event_mon event:\n%s' % ev_str) except Exception: logging.exception('Unable to log the events') return False return True
def _send_to_endpoint(self, events): # Prints individual events because it's what we're usually interested in # in that case. infra_events = [ str(ChromeInfraEvent.FromString(ev.source_extension)) for ev in events.log_event ] try: self.stream.write('%s\n' % '\n'.join(infra_events)) except Exception: logging.exception('Unable to write to provided stream') return False return True
def test_stringio_stream(self): config._cache['default_event'] = ChromeInfraEvent() log_event = infra_libs.event_mon.monitoring._get_service_event( 'START', timestamp_kind='POINT', event_timestamp=1234).log_event() stream = StringIO.StringIO() r = router._TextStreamRouter(stream=stream) self.assertTrue(r.push_event(log_event)) stream.seek(0) output = stream.read() self.assertGreater(stream.len, 10) # Minimal checking because if this is printed the rest is printed as well # unless google.protobuf is completely broken. self.assertIn('timestamp_kind: POINT', output)
def test_events_are_logged_correctly(self, log_mock): logger = router._LoggingStreamRouter() events = [] for i in range(3): event = LogRequestLite.LogEventLite() event.event_time_ms = router.time_ms() event.event_code = 1 event.event_flow_id = 2 infra_event = ChromeInfraEvent() infra_event.cq_event.issue = str(i + 1) event.source_extension = infra_event.SerializeToString() events.append(event) self.assertTrue(logger.push_event(events)) expected_calls = [ ((logging.INFO, 'Sending event_mon event:\ncq_event {\n issue: "1"\n}\n'), ), ((logging.INFO, 'Sending event_mon event:\ncq_event {\n issue: "2"\n}\n'), ), ((logging.INFO, 'Sending event_mon event:\ncq_event {\n issue: "3"\n}\n'), ) ] self.assertEqual(log_mock.call_args_list, expected_calls)
def process_argparse_options(args): """Sets the default event based on --event-logrequest-path. This function raises exceptions because if the base event is wrong, then it's not worth sending anything anyway. """ event_mon.process_argparse_options(args) _extra_argument_checking(args) if args.event_logrequest_path: try: with open(args.event_logrequest_path, 'rb') as f: request = LogRequestLite.FromString(f.read()) if len(request.log_event) == 1: default_event = ChromeInfraEvent.FromString( request.log_event[0].source_extension) # Assume that the content is sane because we don't want to duplicate # any business logic here. # TODO(pgervais): find a better solution. event_mon.set_default_event(default_event) else: raise ValueError( 'Expected only one log_event in the LogRequestLite proto ' 'pointed by --event-logrequest-path. Found %d in %s', len(request.log_event), args.event_logrequest_path) except Exception: LOGGER.exception('Failure when reading/parsing file %s', args.event_logrequest_path) raise default_event = event_mon.get_default_event() # When the default event is set using --event-logrequest-path, passing # --build-event-type or --service-event-type is optional. These options # still takes precedence but they must keep the event type the same. if (default_event.build_event.HasField('type') and default_event.service_event.HasField('type')): msg = ( 'Default event contains both service_event_type and ' 'build_event_type which is incorrect. Make sure you passed ' 'a correct proto to --event-logrequest-path. Otherwise it\'s an ' 'internal error. Aborting.') LOGGER.error(msg) raise ValueError(msg) if default_event.build_event.HasField('type'): if args.service_event_type: msg = ( 'The default event contains a type for build_event, but a ' 'service_event type was provided on the command-line. At most ' 'one of them can be specified. Aborting.') LOGGER.error(msg) raise ValueError(msg) if not args.build_event_type: args.build_event_type = event_mon.BuildEvent.BuildEventType.Name( default_event.build_event.type) if default_event.service_event.HasField('type'): if args.build_event_type: msg = ( 'The default event contains a type for service_event, but a ' 'build_event type was provided on the command-line. At most ' 'one of them can be specified. Aborting.') LOGGER.error(msg) raise ValueError(msg) if not args.service_event_type: args.service_event_type = event_mon.ServiceEvent.ServiceEventType.Name( default_event.service_event.type)
def setup_monitoring(run_type='dry', hostname=None, service_name=None, appengine_name=None, output_file=None, dry_run=False, http_timeout=10, http_retry_backoff=2.): """Initializes event monitoring. This function is mainly used to provide default global values which are required for the module to work. If you're implementing a command-line tool, use process_argparse_options instead. Args: run_type (str): One of 'dry', 'test', or 'prod'. Do respectively nothing, hit the testing endpoint and the production endpoint. hostname (str): hostname as it should appear in the event. If not provided a default value is computed. service_name (str): logical name of the service that emits events. e.g. "commit_queue". appengine_name (str): name of the appengine app, if running on appengine. output_file (str): file where to write the output in run_type == 'file' mode. dry_run (bool): if True, the code has no side-effect, what would have been done is printed instead. http_timeout (int): timeout in seconds for HTTP requests to send events. http_retry_backoff (float): time in seconds before retrying POSTing to the HTTP endpoint. Randomized exponential backoff is applied on subsequent retries. """ global _router logging.debug('event_mon: setting up monitoring.') if _router: return default_event = ChromeInfraEvent() hostname = hostname or socket.getfqdn() # hostname might be empty string or None on some systems, who knows. if hostname: # pragma: no branch default_event.event_source.host_name = hostname else: # pragma: no cover logging.warning('event_mon: unable to determine hostname.') if service_name: default_event.event_source.service_name = service_name if appengine_name: default_event.event_source.appengine_name = appengine_name _cache['default_event'] = default_event if run_type not in RUNTYPES: logging.error('Unknown run_type (%s). Setting to "dry"', run_type) run_type = 'dry' if run_type == 'dry': # If we are running on AppEngine or devserver, use logging module. server_software = os.environ.get('SERVER_SOFTWARE', '') if (server_software.startswith('Google App Engine') or server_software.startswith('Development')): _router = ev_router._LoggingStreamRouter() else: _router = ev_router._TextStreamRouter() elif run_type == 'file': _router = ev_router._LocalFileRouter(output_file, dry_run=dry_run) else: _router = ev_router._HttpRouter(_cache, ENDPOINTS.get(run_type), dry_run=dry_run, timeout=http_timeout, retry_backoff=http_retry_backoff)