#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
  xenon-log
  ~~~~~~~~~
  
  `xenon` is a built-in HHVM extension that periodically captures
  stacktraces of running PHP code. This tool reads xenon-generated
  traces via redis and logs them to disk.

"""
from __future__ import print_function

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import argparse
import datetime
import errno
import logging
import os
import os.path

import redis
import yaml


parser = argparse.ArgumentParser()
parser.add_argument('config', nargs='?', default='/etc/xenon-log.yaml')
args = parser.parse_args()

with open(args.config) as f:
    config = yaml.load(f)


class TimeLog(object):

    base_path = config.get('base_path', '/srv/xenon/logs')

    def __init__(self, period, format, retain, write_every=1):
        self.period = period
        self.format = format
        self.retain = retain
        self.write_every = write_every
        self.skipped = 0
        self.path = os.path.join(self.base_path, period)
        try:
            os.makedirs(self.path, 0755)
        except OSError as exc:
            if exc.errno != errno.EEXIST: raise

    def get_time_from_file(self, file_name):
        return datetime.datetime.strptime(file_name, self.format + '.log')

    def get_file_from_time(self, time=None):
        time = datetime.datetime.utcnow() if time is None else time
        return time.strftime(self.format) + '.log'

    def write(self, message, time=None):
        self.skipped += 1
        if self.skipped >= self.write_every:
            self.skipped = 0
            file = os.path.join(self.path, self.get_file_from_time(time))
            if not os.path.isfile(file):
                self.prune_files()
            with open(file, 'a') as f:
                print(message, file=f)

    def prune_files(self):
        files = {}
        for file in os.listdir(self.path):
            try:
                files[file] = self.get_time_from_file(file)
            except ValueError:
                continue
        files = list(sorted(files, key=files.get, reverse=True))
        for file in files[self.retain:]:
            try:
                os.remove(os.path.join(self.path, file))
            except OSError:
                continue



logs = [TimeLog(**log) for log in config['logs']]
conn = redis.Redis(**config['redis'])
pubsub = conn.pubsub()
pubsub.subscribe('xenon')

for message in pubsub.listen():
    data = message['data']
    time = datetime.datetime.utcnow()
    for log in logs:
        log.write(data, time)
