155 lines
4.7 KiB
Python
155 lines
4.7 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
A Python Statsd implementation with dimensions added
|
|
"""
|
|
|
|
# set up logging before importing any other components
|
|
from monagent.common.config import initialize_logging
|
|
from monagent.monstatsd.reporter import Reporter
|
|
from monagent.monstatsd.udp import Server
|
|
|
|
initialize_logging('monstatsd')
|
|
|
|
import os
|
|
os.umask(022)
|
|
|
|
# stdlib
|
|
import logging
|
|
import optparse
|
|
import signal
|
|
import sys
|
|
|
|
# project
|
|
from monagent.common.aggregator import MetricsBucketAggregator
|
|
from monagent.common.check_status import MonstatsdStatus
|
|
from monagent.common.config import get_config
|
|
from monagent.common.daemon import Daemon, AgentSupervisor
|
|
from monagent.common.util import PidFile, get_hostname
|
|
|
|
log = logging.getLogger('monstatsd')
|
|
|
|
|
|
class Monstatsd(Daemon):
|
|
""" This class is the monstatsd daemon. """
|
|
|
|
def __init__(self, pid_file, server, reporter, autorestart):
|
|
Daemon.__init__(self, pid_file, autorestart=autorestart)
|
|
self.server = server
|
|
self.reporter = reporter
|
|
|
|
def _handle_sigterm(self, signum, frame):
|
|
log.debug("Caught sigterm. Stopping run loop.")
|
|
self.server.stop()
|
|
|
|
def run(self):
|
|
# Gracefully exit on sigterm.
|
|
signal.signal(signal.SIGTERM, self._handle_sigterm)
|
|
|
|
# Handle Keyboard Interrupt
|
|
signal.signal(signal.SIGINT, self._handle_sigterm)
|
|
|
|
# Start the reporting thread before accepting data
|
|
self.reporter.start()
|
|
|
|
try:
|
|
try:
|
|
self.server.start()
|
|
except Exception, e:
|
|
log.exception('Error starting server')
|
|
raise e
|
|
finally:
|
|
# The server will block until it's done. Once we're here, shutdown
|
|
# the reporting thread.
|
|
self.reporter.stop()
|
|
self.reporter.join()
|
|
log.info("Monstatsd is stopped")
|
|
# Restart if asked to restart
|
|
if self.autorestart:
|
|
sys.exit(AgentSupervisor.RESTART_EXIT_STATUS)
|
|
|
|
def info(self):
|
|
logging.getLogger().setLevel(logging.ERROR)
|
|
return MonstatsdStatus.print_latest_status()
|
|
|
|
|
|
def init_monstatsd(config_path=None, use_watchdog=False):
|
|
"""Configure the server and the reporting thread.
|
|
"""
|
|
c = get_config(parse_args=False, cfg_path=config_path)
|
|
log.debug("Configuration monstatsd")
|
|
|
|
port = c['monstatsd_port']
|
|
interval = int(c['monstatsd_interval'])
|
|
aggregator_interval = int(c['monstatsd_agregator_bucket_size'])
|
|
non_local_traffic = c['non_local_traffic']
|
|
forward_to_host = c.get('statsd_forward_host')
|
|
forward_to_port = c.get('statsd_forward_port')
|
|
event_chunk_size = c.get('event_chunk_size')
|
|
|
|
target = c['forwarder_url']
|
|
|
|
hostname = get_hostname(c)
|
|
|
|
# Create the aggregator (which is the point of communication between the
|
|
# server and reporting threads.
|
|
assert 0 < interval
|
|
|
|
aggregator = MetricsBucketAggregator(hostname, aggregator_interval,
|
|
recent_point_threshold=c.get('recent_point_threshold', None))
|
|
|
|
# Start the reporting thread.
|
|
reporter = Reporter(interval, aggregator, target, use_watchdog, event_chunk_size)
|
|
|
|
# Start the server on an IPv4 stack
|
|
# Default to loopback
|
|
server_host = 'localhost'
|
|
# If specified, bind to all addressses
|
|
if non_local_traffic:
|
|
server_host = ''
|
|
|
|
server = Server(aggregator, server_host, port, forward_to_host=forward_to_host, forward_to_port=forward_to_port)
|
|
|
|
return reporter, server, c
|
|
|
|
|
|
def main(config_path=None):
|
|
""" The main entry point for the unix version of monstatsd. """
|
|
parser = optparse.OptionParser("%prog [start|stop|restart|status]")
|
|
opts, args = parser.parse_args()
|
|
|
|
reporter, server, cnf = init_monstatsd(config_path, use_watchdog=True)
|
|
pid_file = PidFile('monstatsd')
|
|
daemon = Monstatsd(pid_file.get_path(), server, reporter,
|
|
cnf.get('autorestart', False))
|
|
|
|
# If no args were passed in, run the server in the foreground.
|
|
# todo does this need to be a daemon even when it basically always runs in the foreground, if not
|
|
# restructure and get rid of the silly init_function
|
|
if not args:
|
|
daemon.run()
|
|
return 0
|
|
|
|
# Otherwise, we're process the deamon command.
|
|
else:
|
|
command = args[0]
|
|
|
|
if command == 'start':
|
|
daemon.start()
|
|
elif command == 'stop':
|
|
daemon.stop()
|
|
elif command == 'restart':
|
|
daemon.restart()
|
|
elif command == 'status':
|
|
daemon.status()
|
|
elif command == 'info':
|
|
return daemon.info()
|
|
else:
|
|
sys.stderr.write("Unknown command: %s\n\n" % command)
|
|
parser.print_help()
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|