Skip to content
This repository was archived by the owner on Nov 28, 2022. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 38 additions & 31 deletions logzero/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
colorama_init()


def setup_logger(name=__name__, logfile=None, level=DEBUG, formatter=None, maxBytes=0, backupCount=0, fileLoglevel=None, disableStderrLogger=False, isRootLogger=False, json=False, json_ensure_ascii=False):
def setup_logger(name=__name__, logfile=None, level=DEBUG, formatter=None, maxBytes=0, backupCount=0, fileLoglevel=None, log_stream='stderr', isRootLogger=False, json=False, json_ensure_ascii=False):
"""
Configures and returns a fully configured logger instance, no hassles.
If a logger with the specified name already exists, it returns the existing instance,
Expand All @@ -122,7 +122,7 @@ def setup_logger(name=__name__, logfile=None, level=DEBUG, formatter=None, maxBy
:arg int maxBytes: Size of the logfile when rollover should occur. Defaults to 0, rollover never occurs.
:arg int backupCount: Number of backups to keep. Defaults to 0, rollover never occurs.
:arg int fileLoglevel: Minimum `logging-level <https://docs.python.org/2/library/logging.html#logging-levels>`_ for the file logger (is not set, it will use the loglevel from the ``level`` argument)
:arg bool disableStderrLogger: Should the default stderr logger be disabled. Defaults to False.
:arg string log_stream: Which standard stream should the log output go to. Can take values of `stderr`, `stdout` or `None`. Defaults to `stderr`.
:arg bool isRootLogger: If True then returns a root logger. Defaults to False. (see also the `Python docs <https://docs.python.org/3/library/logging.html#logging.getLogger>`_).
:arg bool json: If True then log in JSON format. Defaults to False. (uses `python-json-logger <https://github.com/madzak/python-json-logger>`_).
:arg bool json_ensure_ascii: Passed to json.dumps as `ensure_ascii`, default: False (if False: writes utf-8 characters, if True: ascii only representation of special characters - eg. '\u00d6\u00df')
Expand All @@ -138,8 +138,13 @@ def setup_logger(name=__name__, logfile=None, level=DEBUG, formatter=None, maxBy
# Setup default formatter
_formatter = _get_json_formatter(json_ensure_ascii) if json else formatter or LogFormatter()

# Ensure the log stream passed by user is valid.
if log_stream not in ("stderr", "stdout", None):
_logger.warning("Log stream must be one of `'stderr'`, `'stdout'` or `None`. Using `'stderr'`")
log_stream = "stderr"

# Reconfigure existing handlers
stderr_stream_handler = None
std_stream_handler = None
for handler in list(_logger.handlers):
if hasattr(handler, LOGZERO_INTERNAL_LOGGER_ATTR):
if isinstance(handler, logging.FileHandler):
Expand All @@ -148,22 +153,25 @@ def setup_logger(name=__name__, logfile=None, level=DEBUG, formatter=None, maxBy
_logger.removeHandler(handler)
continue
elif isinstance(handler, logging.StreamHandler):
stderr_stream_handler = handler
if log_stream is None or (std_stream_handler and log_stream not in std_stream_handler.stream.name):
# remove the std handler (stream_handler) if disabled, or if there is a stream(err/out) mismatch
_logger.removeHandler(handler)
continue
else:
# Else, this is the stderr/stdout stream that we need to reconfigure
std_stream_handler = handler

# reconfigure handler
handler.setLevel(level)
handler.setFormatter(_formatter)

# remove the stderr handler (stream_handler) if disabled
if disableStderrLogger:
if stderr_stream_handler is not None:
_logger.removeHandler(stderr_stream_handler)
elif stderr_stream_handler is None:
stderr_stream_handler = logging.StreamHandler()
setattr(stderr_stream_handler, LOGZERO_INTERNAL_LOGGER_ATTR, True)
stderr_stream_handler.setLevel(level)
stderr_stream_handler.setFormatter(_formatter)
_logger.addHandler(stderr_stream_handler)
if log_stream is not None and std_stream_handler is None:
# This is the block that will be entered for the default logger (ex: `from logzero import logger`)
std_stream_handler = logging.StreamHandler(stream=getattr(sys, log_stream))
setattr(std_stream_handler, LOGZERO_INTERNAL_LOGGER_ATTR, True)
std_stream_handler.setLevel(level)
std_stream_handler.setFormatter(_formatter)
_logger.addHandler(std_stream_handler)

if logfile:
rotating_filehandler = RotatingFileHandler(filename=logfile, maxBytes=maxBytes, backupCount=backupCount)
Expand Down Expand Up @@ -305,7 +313,7 @@ def _safe_unicode(s):
return repr(s)


def setup_default_logger(logfile=None, level=DEBUG, formatter=None, maxBytes=0, backupCount=0, disableStderrLogger=False):
def setup_default_logger(logfile=None, level=DEBUG, formatter=None, maxBytes=0, backupCount=0, log_stream='stderr'):
"""
Deprecated. Use `logzero.loglevel(..)`, `logzero.logfile(..)`, etc.

Expand All @@ -324,10 +332,10 @@ def setup_default_logger(logfile=None, level=DEBUG, formatter=None, maxBytes=0,
:arg Formatter formatter: `Python logging Formatter object <https://docs.python.org/2/library/logging.html#formatter-objects>`_ (by default uses the internal LogFormatter).
:arg int maxBytes: Size of the logfile when rollover should occur. Defaults to 0, rollover never occurs.
:arg int backupCount: Number of backups to keep. Defaults to 0, rollover never occurs.
:arg bool disableStderrLogger: Should the default stderr logger be disabled. Defaults to False.
:arg string log_stream: Which standard stream should the log output go to. Can take values of `stderr`, `stdout` or `None`. Defaults to `stderr`.
"""
global logger
logger = setup_logger(name=LOGZERO_DEFAULT_LOGGER, logfile=logfile, level=level, formatter=formatter, backupCount=backupCount, disableStderrLogger=disableStderrLogger)
logger = setup_logger(name=LOGZERO_DEFAULT_LOGGER, logfile=logfile, level=level, formatter=formatter, backupCount=backupCount, log_stream=log_stream)
return logger


Expand All @@ -352,10 +360,6 @@ def reset_default_logger():
logger = setup_logger(name=LOGZERO_DEFAULT_LOGGER, logfile=_logfile, level=_loglevel, formatter=_formatter)


# Initially setup the default logger
reset_default_logger()


def loglevel(level=DEBUG, update_custom_handlers=False):
"""
Set the minimum loglevel for the default logger (`logzero.logger`) and all handlers.
Expand Down Expand Up @@ -403,7 +407,7 @@ def formatter(formatter, update_custom_handlers=False):
_formatter = formatter


def logfile(filename, formatter=None, mode='a', maxBytes=0, backupCount=0, encoding=None, loglevel=None, disableStderrLogger=False):
def logfile(filename, formatter=None, mode='a', maxBytes=0, backupCount=0, encoding=None, loglevel=None, disable_stream_loggers=False):
"""
Setup logging to file (using a `RotatingFileHandler <https://docs.python.org/2/library/logging.handlers.html#rotatingfilehandler>`_ internally).

Expand All @@ -427,10 +431,10 @@ def logfile(filename, formatter=None, mode='a', maxBytes=0, backupCount=0, encod
:arg int backupCount: Number of backups to keep. Defaults to 0, rollover never occurs.
:arg string encoding: Used to open the file with that encoding.
:arg int loglevel: Set a custom loglevel for the file logger, else uses the current global loglevel.
:arg bool disableStderrLogger: Should the default stderr logger be disabled. Defaults to False.
:arg bool disable_stream_loggers: should the default stream loggers(stderr/stdout) be disabled? defaults to `True`
"""
# First, remove any existing file logger
__remove_internal_loggers(logger, disableStderrLogger)
__remove_internal_loggers(logger, disable_stream_loggers)

# If no filename supplied, all is done
if not filename:
Expand All @@ -455,32 +459,32 @@ def logfile(filename, formatter=None, mode='a', maxBytes=0, backupCount=0, encod
logger.setLevel(loglevel)


def __remove_internal_loggers(logger_to_update, disableStderrLogger=True):
def __remove_internal_loggers(logger_to_update, disable_stream_loggers=True):
"""
Remove the internal loggers (e.g. stderr logger and file logger) from the specific logger
Remove the internal loggers (e.g. stderr/stdout logger and file logger) from the specific logger
:param logger_to_update: the logger to remove internal loggers from
:param disableStderrLogger: should the default stderr logger be disabled? defaults to True
:param disable_stream_loggers: should the default stream loggers(stderr/stdout) be disabled? defaults to `True`
"""
for handler in list(logger_to_update.handlers):
if hasattr(handler, LOGZERO_INTERNAL_LOGGER_ATTR):
if isinstance(handler, RotatingFileHandler):
logger_to_update.removeHandler(handler)
elif isinstance(handler, SysLogHandler):
logger_to_update.removeHandler(handler)
elif isinstance(handler, logging.StreamHandler) and disableStderrLogger:
elif isinstance(handler, logging.StreamHandler) and disable_stream_loggers:
logger_to_update.removeHandler(handler)


def syslog(logger_to_update=logger, facility=SysLogHandler.LOG_USER, disableStderrLogger=True):
def syslog(logger_to_update=logger, facility=SysLogHandler.LOG_USER, disable_stream_loggers=True):
"""
Setup logging to syslog and disable other internal loggers
:param logger_to_update: the logger to enable syslog logging for
:param facility: syslog facility to log to
:param disableStderrLogger: should the default stderr logger be disabled? defaults to True
:param disable_stream_loggers: should the default stream loggers(stderr/stdout) be disabled? defaults to `True`
:return the new SysLogHandler, which can be modified externally (e.g. for custom log level)
"""
# remove internal loggers
__remove_internal_loggers(logger_to_update, disableStderrLogger)
__remove_internal_loggers(logger_to_update, disable_stream_loggers)

# Setup logzero to only use the syslog handler with the specified facility
syslog_handler = SysLogHandler(facility=facility)
Expand Down Expand Up @@ -537,6 +541,9 @@ def wrap(*args, **kwargs):
return wrap


# Initially setup the default logger
reset_default_logger()

if __name__ == "__main__":
_logger = setup_logger()
_logger.info("hello")