blob: 79936b73ef781643ead374dcee251aed33a12d64 [file] [log] [blame]
# 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)