"""
An observer doing logging of warnings, infos, errors, etc.

No synchronization takes place; it's probably not worth sweating this.
"""

#c Copyright 2008-2021, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


import logging
import os
import sys
import traceback

from gavo import base
from gavo import utils
from gavo.base import ObserverBase, listensTo
from gavo.protocols.gavolog import RotatingFileHandler


def stringify(s):
	if isinstance(s, bytes):
		return s.decode("utf-8", "ignore")
	# logging gives us a headache when there's non-ascii in here;
	# pending further investigation, hack around it:
	return s.encode("ascii", "replace").decode("ascii")


def addRequestURI(msg):
	"""adds info on the request URI to msg if we seem to handle a request.

	(which, technically, we determine by trying to steal a request variable
	from upstack).
	"""
	try:
		req = utils.stealVar("request")
		if req:
			msg = "While handling %s:\n%s"%(
				req.uri.decode("ascii"),
				msg)
	except:
		# don't fail if we can't add the request URI for some reason;
		# people will notice anyway.
		pass
	return msg


class LoggingUI(ObserverBase):
	logLineFormat = "%(asctime)s [%(levelname)s %(process)s] %(message)s"

	def __init__(self, eh, errorName="dcErrors", infoName="dcInfos"):
		ObserverBase.__init__(self, eh)
		errH = RotatingFileHandler(
			os.path.join(base.getConfig("logDir"), errorName),
			maxBytes=500000, backupCount=3, mode=0o664, encoding="utf-8")
		errH.setFormatter(
			logging.Formatter(self.logLineFormat))
		self.errorLogger = logging.getLogger("dcErrors")
		self.errorLogger.addHandler(errH)
		self.errorLogger.propagate = False

		infoH = RotatingFileHandler(
			os.path.join(base.getConfig("logDir"), infoName),
			maxBytes=500000, backupCount=1, mode=0o664, encoding="utf-8")
		infoH.setFormatter(logging.Formatter(self.logLineFormat))
		self.infoLogger = logging.getLogger("dcInfos")
		self.infoLogger.propagate = False
		self.infoLogger.addHandler(infoH)
		self.infoLogger.setLevel(logging.DEBUG)

	@listensTo("ExceptionMutation")
	def logOldException(self, res):
		if base.DEBUG:
			excInfo, newExc = res
			self.infoLogger.info("Swallowed the exception below, re-raising %s"%
				repr(newExc), exc_info=excInfo)
	
	@listensTo("Info")
	def logInfo(self, message):
		self.infoLogger.info(
			addRequestURI(
				stringify(message)))
	
	@listensTo("Warning")
	def logWarning(self, message):
		self.infoLogger.warning(
			addRequestURI(
				stringify(message)))

	@listensTo("Error")
	def logError(self, message):
		self.errorLogger.error(
			addRequestURI(
				utils.safe_str(message)), exc_info=False)
		if sys.exc_info()!=(None, None, None):
			self.infoLogger.info("Traceback (le) of the exception just logged:\n%s"%
				traceback.format_exc())
