# -*- encoding: utf-8 -*-

import os
import logging, logging.handlers
import traceback

DEBUG	= logging.DEBUG
INFO	= logging.INFO
WARNING = logging.WARNING
ERROR	= logging.ERROR
CRITICAL= logging.CRITICAL

class Logger(object):
	"""This class is adapter for standard logging library - it has function for both service-wise logging
		and tas-wise logging. The interface is ver similliar to standard logging package."""
	def __init__(self, lvl, main_log_path, task_logs_path, log_to_console):
		"""Constructor
			lvl - logging level (string), possible values: "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
			main_log_path - path to service-wise logfile
			task_logs_path - path to directory containing task specific logfiles
		"""
		self.task_logs_path = task_logs_path
		self.default_logging_lvl = lvl
		logger_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
		self.default_formatter = logging.Formatter(logger_format)

		self.main_logger = self._create_main_logger(main_log_path, log_to_console)

	def log(self, lvl, exception):
		"""Log message in main logfile
			lvl - message level, as in constructor
			exception - string message or exception to log
		"""
		self._log(self.main_logger, lvl, exception)

	def debug(exception):
		self.log(DEBUG, exception)
	def info(exception):
		self.log(INFO, exception)
	def warning(exception):
		self.log(WARNING, exception)
	def error(exception):
		self.log(ERROR, exception)

	def task_log(self, task_token, lvl, exception):
		"""Log message in task specific logfile
			lvl - message level, as in constructor
			exception - string message or exception to log
		"""
		task_logger, task_logger_file_descriptor = self._create_task_logger(task_token)
		self._log(task_logger, lvl, exception)

	def task_debug(task_token, exception):
		self.log(task_token, DEBUG, exception)
	def task_info(task_token, exception):
		self.log(task_token, INFO, exception)
	def task_warning(task_token, exception):
		self.log(task_token, WARNING, exception)
	def task_error(task_token, exception):
		self.log(task_token, ERROR, exception)

	@staticmethod
	def str2logging_lvl(str_level):
		"""Translates logging level in string into logging module const"""
		str_level = str_level.lower()
		return {"debug"		: DEBUG,
				"info"		: INFO,
				"warning"	: WARNING,
				"error"		: ERROR,
				"critical"	: CRITICAL}[str_level]

	def _create_main_logger(self, main_log_path, log_to_console):
		"""Creates main logger - global for whole service and a parent to all task loggers,
			which writes to console and main logfile."""
		logger, descriptor = self._create_logger(self.default_logging_lvl, 'service', main_log_path)
		self.main_logfile_descr = descriptor

		if log_to_console:
			console_handler = logging.StreamHandler()
			console_handler.setLevel(self.default_logging_lvl)
			console_handler.setFormatter(self.default_formatter)
			logger.addHandler(console_handler)
		return logger

	def _create_task_logger(self, task_token):
		"""Creates logging.Logger instance with certain level for task with supplied token"""
		task_logger_name = 'service.task-' + task_token
		task_log_file = os.path.join(self.task_logs_path, task_token)
		return self._create_logger(self.default_logging_lvl, task_logger_name, task_log_file)

	def _create_logger(self, logging_lvl, name, filepath):
		"""Creates logging.Logger instance with certain level and name that writes into files
			given by filepath (rotating).Returns logger and it's file descriptor."""
		handler = logging.handlers.RotatingFileHandler(filepath, maxBytes=1024000, backupCount=10)
		handler.setLevel(logging_lvl)
		handler.setFormatter(self.default_formatter)

		logger = logging.getLogger(name)
		logger.setLevel(logging_lvl)
		logger.addHandler(handler)

		return logger, handler.stream.fileno()

	def _log(self, logger, lvl, exception):
		"""Logs given exception to given logger with given level. If the exception is ERROR or
			CRITICAL, than prints traceback as well."""
		logger.log(lvl, str(exception))
		if (lvl == ERROR or lvl == CRITICAL) and isinstance(exception, Exception):
			if hasattr(exception, 'traceback'):
				traceback_str = exception.traceback
			else:
				traceback_str = traceback.format_exc()

			if traceback_str is not None:
				logger.log(lvl, traceback_str)
