bootstrap.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. # encoding=utf-8
  2. import argparse
  3. import configparser
  4. import logging
  5. import signal
  6. import redis
  7. import tornado.httpserver
  8. import tornado.ioloop
  9. from terroroftinytown.tracker.app import Application
  10. from terroroftinytown.tracker.database import Database
  11. from terroroftinytown.tracker.logs import GzipTimedRotatingFileHandler, \
  12. LogFilter
  13. from terroroftinytown.tracker.stats import Stats
  14. logger = logging.getLogger(__name__)
  15. class Bootstrap:
  16. def __init__(self):
  17. self.arg_parser = argparse.ArgumentParser()
  18. self.config = configparser.ConfigParser()
  19. def start(self, args=None):
  20. self.setup_args()
  21. self.parse_args(args=args)
  22. self.load_config()
  23. self.setup_database()
  24. def setup_args(self):
  25. self.arg_parser.add_argument('config')
  26. self.arg_parser.add_argument('--debug', action='store_true')
  27. def parse_args(self, args=None):
  28. self.args = self.arg_parser.parse_args(args=args)
  29. def load_config(self):
  30. self.config.read([self.args.config])
  31. def setup_database(self):
  32. self.database = Database(
  33. path=self.config['database']['path'],
  34. )
  35. def setup_redis(self):
  36. kwargs = {
  37. 'db': self.config.getint('redis', 'db', fallback=0),
  38. 'password': self.config.get('redis', 'password', fallback=None),
  39. }
  40. if self.config['redis']['unix']:
  41. kwargs['unix_socket_path'] = self.config['redis']['unix']
  42. else:
  43. kwargs['host'] = self.config.get('redis', 'host', fallback='localhost')
  44. kwargs['port'] = self.config.getint('redis', 'port', fallback=6379)
  45. self.redis = redis.Redis(**kwargs)
  46. def setup_stats(self):
  47. self.stats = Stats(
  48. self.redis,
  49. self.config.get('redis', 'prefix', fallback=''),
  50. self.config.getint('redis', 'max_stats', fallback=30)
  51. )
  52. def setup_logging(self):
  53. log_path = self.config.get('logging', 'path', fallback=None)
  54. if not log_path:
  55. return
  56. if self.args.debug:
  57. logging.basicConfig(level=logging.DEBUG)
  58. else:
  59. logging.basicConfig(level=logging.INFO)
  60. handler = GzipTimedRotatingFileHandler(
  61. filename=log_path,
  62. backupCount=self.config.get('logging', 'backup_count', fallback=52),
  63. encoding='utf-8')
  64. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  65. handler.setFormatter(formatter)
  66. logging.getLogger().addHandler(handler)
  67. log_filter = LogFilter()
  68. handler.addFilter(log_filter)
  69. class ApplicationBootstrap(Bootstrap):
  70. def start(self):
  71. super().start()
  72. self.setup_redis()
  73. self.setup_stats()
  74. self.setup_application()
  75. self.setup_logging()
  76. self.setup_signal_handlers()
  77. self.boot()
  78. def setup_application(self):
  79. self.application = Application(
  80. self.database,
  81. self.redis,
  82. debug=self.args.debug,
  83. cookie_secret=self.config['web']['cookie_secret'],
  84. maintenance_sentinel=self.config['web'].get('maintenance_sentinel_file'),
  85. )
  86. def boot(self):
  87. host = self.config['web'].get('host', 'localhost')
  88. port = int(self.config['web']['port'])
  89. xheaders = self.config.getboolean('web', 'xheaders', fallback=False)
  90. logger.info('Application booting. Listen on %s:%s', host, port)
  91. if xheaders:
  92. logger.info('Using xheaders.')
  93. self.server = tornado.httpserver.HTTPServer(
  94. self.application, xheaders=xheaders
  95. )
  96. self.server.listen(port, address=host)
  97. tornado.ioloop.IOLoop.instance().start()
  98. def setup_signal_handlers(self):
  99. signal.signal(signal.SIGINT, self._signal_handler)
  100. signal.signal(signal.SIGTERM, self._signal_handler)
  101. def _signal_handler(self, signal_number, stack_frame):
  102. logger.info('Shutting down.')
  103. io_loop = tornado.ioloop.IOLoop.instance()
  104. io_loop.add_callback_from_signal(self.stop)
  105. def stop(self):
  106. io_loop = tornado.ioloop.IOLoop.instance()
  107. self.server.stop()
  108. io_loop.call_later(1, io_loop.stop)