| # Copyright 2018 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Global singleton setup for ts-mon. |
| |
| Ts-mon setup must happen post-fork, or else the gunicorn workers will all use |
| the same hostname and task_num arguments, causing conflicts when they flush |
| metrics. |
| |
| This can be accomplished with flask's "app.before_first_request" decorator |
| function. See flask's documentation: |
| http://flask.pocoo.org/docs/0.12/api/#flask.Flask.before_first_request |
| """ |
| |
| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| |
| import os |
| import socket |
| |
| from chromite.lib import cros_logging as logging |
| |
| from infra_libs.ts_mon.common import interface |
| from infra_libs.ts_mon.common import monitors |
| from infra_libs.ts_mon.common import standard_metrics |
| from infra_libs.ts_mon.common import targets |
| import googleapiclient.discovery |
| |
| from flex_ts_mon import gae_app_identity_shim as app_identity |
| from flex_ts_mon import gae_modules_shim as modules |
| |
| |
| _WasSetup = False |
| |
| _PRODX_MON_ENDPOINT = 'https://prodxmon-pa.googleapis.com/v1:insert' |
| _PRODXMON_SERVICE_ACCOUNT_EMAIL = ( |
| 'app-engine-metric-publishers@' |
| 'prodx-mon-chrome-infra.google.com.iam.gserviceaccount.com') |
| _REGION = 'appengine' |
| _FLUSH_INTERVAL_SECS = 60 |
| |
| |
| def SetupTsMonGlobalState(debug_file=None): |
| """Sets up the ts-mon flushing thread. |
| |
| Args: |
| debug_file: If non-none, send metrics to this path instead of to PubSub. |
| """ |
| global _WasSetup # pylint: disable=global-statement |
| if not _WasSetup: |
| _SetupTsMon(debug_file) |
| _WasSetup = True |
| |
| |
| def _SetupTsMon(debug_file): |
| """Sets up ts-mon global state given parsed argparse options. |
| |
| Args: |
| debug_file: If non-none, send metrics to this path instead of to PubSub. |
| """ |
| googleapiclient.discovery.logger.setLevel(logging.WARNING) |
| try: |
| if debug_file: |
| endpoint = 'file://' + debug_file |
| else: |
| endpoint = _PRODX_MON_ENDPOINT |
| |
| _SetupAppEngineTarget() |
| _SetupMonitorForEndpoint(endpoint) |
| |
| # TODO(phobbs): set up the same global app engine metrics as |
| # gae_ts_mon.shared |
| standard_metrics.init() |
| |
| interface.state.flush_mode = 'auto' |
| # pylint: disable=protected-access |
| interface.state.flush_thread = interface._FlushThread(_FLUSH_INTERVAL_SECS) |
| interface.state.flush_thread.start() |
| |
| logging.notice('ts_mon was set up.') |
| except Exception as e: |
| logging.warning('Failed to configure ts_mon, monitoring is disabled: %s', e, |
| exc_info=True) |
| |
| |
| def _SetupAppEngineTarget(): |
| """Creates the ts-mon target for an App Engine app.""" |
| fqdn = socket.getfqdn().lower() |
| host = fqdn.split('.')[0] |
| |
| # Note - any graphs using metrics from these apps need a taskless |
| # precomputation to remove task_num. |
| task_hostname = 'autogen:' + host |
| task_num = os.getpid() |
| |
| service_name = app_identity.get_application_id() |
| job_name = modules.get_current_module_name() |
| |
| interface.state.target = targets.TaskTarget( |
| service_name, job_name, _REGION, task_hostname, task_num=task_num) |
| |
| logging.info('Initializing ts_mon with service_name=%s, job_name=%s, ' |
| 'hostname=%s', service_name, job_name, task_hostname) |
| |
| |
| def _SetupMonitorForEndpoint(endpoint): |
| """Sets up a ts-mon monitor. |
| |
| Args: |
| endpoint: The endpoint to send metrics to. Starts with file:// or https://, |
| or is "none". |
| """ |
| if endpoint.startswith('file://'): |
| interface.state.global_monitor = monitors.DebugMonitor( |
| endpoint[len('file://'):]) |
| elif endpoint.startswith('https://'): |
| logging.info('Using https monitor %s with %s', endpoint, |
| _PRODXMON_SERVICE_ACCOUNT_EMAIL) |
| credentials = monitors.DelegateServiceAccountCredentials( |
| _PRODXMON_SERVICE_ACCOUNT_EMAIL, |
| monitors.GCECredentials()) |
| interface.state.global_monitor = monitors.HttpsMonitor( |
| endpoint, credentials) |
| interface.state.use_new_proto = True |
| elif endpoint.lower() == 'none': |
| logging.info('ts_mon monitoring has been explicitly disabled') |
| else: |
| logging.error('ts_mon monitoring is disabled because the endpoint provided' |
| ' is invalid or not supported: %s', endpoint) |