Skip to content

Commit

Permalink
Fixed issue where root logger was always set to DEBUG.
Browse files Browse the repository at this point in the history
Fixes #1.
  • Loading branch information
haizaar committed Jan 18, 2019
1 parent fbf9f36 commit c9def59
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 46 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ name = "pypi"

[dev-packages]
bumpversion = "*"
flake8 = "*"

[requires]
python_version = "3.6"
31 changes: 30 additions & 1 deletion Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
def main():
logger = uberlogging.get_logger()

# NOTE: using cache_structlog_logger=False JUST FOR DEMO to
# showcase style changes.
# NOTE: using cache_structlog_logger=False JUST FOR DEMO to showcase style changes.

uberlogging.configure(cache_structlog_loggers=False)
logger.info("Plain text, autoconfigured with %s", "defaults", text="foo", i=1)
logging.getLogger("STDLIB").warn("Stdlib logger comming %s", "through")
logging.getLogger().debug("You should not see this line since root log level is INFO by default")

uberlogging.configure(style=uberlogging.Style.text_color, cache_structlog_loggers=False)
logger.info("Plain text, colors (forced)", text="foo", i=1)
Expand Down
102 changes: 59 additions & 43 deletions uberlogging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys
from copy import deepcopy
from enum import Enum
import logging
from logging.config import dictConfig

import coloredlogs
Expand All @@ -21,19 +22,12 @@
"style",
)

field_styles = deepcopy(coloredlogs.DEFAULT_FIELD_STYLES)
field_styles.update({
"module": {"color": "white", "faint": True},
"funcName": {"color": "white", "faint": True},
"lineno": {"color": "white", "faint": True},
})


default_fmt = "%(asctime)s.%(msecs)03d: " + \
"%(name)-15s %(levelname)-5s ## " + \
"%(message)s %(module)s.%(funcName)s:%(lineno)d"
default_datefmt = "%Y-%m-%dT%H:%M:%S"
simple_fmt_name = "simple"
simple_colors_fmt_name = "simple_colors"
simple_json_fmt_name = "simple_json"


Expand All @@ -44,6 +38,13 @@ class Style(Enum):
text_no_color = 3


style_to_fmt_name = {
Style.json: simple_json_fmt_name,
Style.text_color: simple_colors_fmt_name,
Style.text_no_color: simple_fmt_name,
}


def default_conf(fmt=default_fmt, datefmt=default_datefmt):
return {
"version": 1,
Expand All @@ -52,12 +53,16 @@ def default_conf(fmt=default_fmt, datefmt=default_datefmt):
simple_fmt_name: {
"format": fmt,
"datefmt": datefmt,
"class": "logging.Formatter"
"class": "logging.Formatter",
},
simple_colors_fmt_name: {
"format": fmt,
"datefmt": datefmt,
"class": "uberlogging.ColoredFormatter",
},
simple_json_fmt_name: {
"format": fmt,
"datefmt": datefmt,
# "class": "pythonjsonlogger.jsonlogger.JsonFormatter",
"class": "uberlogging.SeverityJsonFormatter",
}
},
Expand All @@ -69,18 +74,19 @@ def default_conf(fmt=default_fmt, datefmt=default_datefmt):
}
},
"root": {
"level": "INFO",
"level": None,
"handlers": ["console"],
},
}


def configure(style=Style.auto,
fmt=default_fmt, datefmt=default_datefmt,
logger_confs: dict=None,
logger_confs_list: list=None,
logger_confs: dict = None,
logger_confs_list: list = None,
cache_structlog_loggers=True,
full_conf=None):
full_conf: dict = None,
root_level=logging.INFO):
"""
Configure both structlog and stdlib logger libraries
with sane defaults.
Expand All @@ -99,14 +105,14 @@ def configure(style=Style.auto,
This is a shortcut to change configuration quickly without
fiddling with logging configuration in full
:logger_confs:
:param logger_confs:
Configuration for additional loggers, e.g.::
logger_confs = {
"requests": {"level": "DEBUG"}
}
:logger_confs_list:
:param logger_confs_list:
Configuration for additional loggers in list format.
The list will be converted to logger_confs dict (overriding existing key)
The rationale is overcome limitation of configuration libraries that don't
Expand All @@ -121,19 +127,26 @@ def configure(style=Style.auto,
}
:cache_structlog_loggers:
:param cache_structlog_loggers:
Enable/disabled caching of structlog loggers as described
in `documentation <http://www.structlog.org/en/stable/performance.html>`_.
You should generally leave it to True, unless, e.g. writing tests
:param full_conf:
Provide your own dictConfig dictionary - hard override for everything except
of structlog key-val formatting
:param root_level:
Set log level of the root logger. Defaults to logging.INFO.
"""

actual_style = _detect_style(style)
use_json = actual_style == Style.json
colored = actual_style == Style.text_color
formatter_name = style_to_fmt_name[actual_style]
colored = (actual_style == Style.text_color)

conf = full_conf or _build_conf(fmt, datefmt, logger_confs, logger_confs_list, use_json)
conf = full_conf or _build_conf(fmt, datefmt, logger_confs, logger_confs_list, formatter_name, root_level)
_configure_structlog(colored, cache_structlog_loggers)
_configure_stdliblog(conf, colored, is_custom=bool(full_conf))
_configure_stdliblog(conf)


def _detect_style(style):
Expand Down Expand Up @@ -175,42 +188,44 @@ def _configure_structlog(colored, cache_loggers):
)


def _build_conf(fmt, datefmt, logger_confs, logger_confs_list, use_json):
def _configure_stdliblog(conf):
dictConfig(conf)


def _build_conf(fmt, datefmt, logger_confs, logger_confs_list, formatter_name, root_level):
conf = default_conf(fmt, datefmt)
logger_confs = logger_confs or {}
for lconf in (logger_confs_list or []):
name = lconf.pop("name")
logger_confs[name] = lconf
if logger_confs:
conf["loggers"] = logger_confs
if use_json:
for handler in conf["handlers"].values():
handler["formatter"] = simple_json_fmt_name
for handler in conf["handlers"].values():
handler["formatter"] = formatter_name
conf["root"]["level"] = logging.getLevelName(root_level)
return conf


def _configure_stdliblog(conf, colored, is_custom):
dictConfig(conf)
if not colored:
return
if is_custom:
# Custom configuration used. Install coloring manually
return

coloredlogs.install(fmt=conf["formatters"]["simple"]["format"],
datefmt=conf["formatters"]["simple"]["datefmt"],
field_styles=field_styles,
level="DEBUG",
)


class SeverityJsonFormatter(jsonlogger.JsonFormatter):
def add_fields(self, log_record, record, message_dict):
# Fix for Stackdriver that expects loglevel in "severity" field
super().add_fields(log_record, record, message_dict)
log_record["severity"] = record.levelname


class ColoredFormatter(coloredlogs.ColoredFormatter):
custom_field_styles = deepcopy(coloredlogs.DEFAULT_FIELD_STYLES)
custom_field_styles.update({
"module": {"color": "white", "faint": True},
"funcName": {"color": "white", "faint": True},
"lineno": {"color": "white", "faint": True},
})

# Exposing logging.Formatter interface since we don't initialize this class by ourselves
def __init__(self, fmt=None, datefmt=None, style='%'):
super().__init__(fmt, datefmt, field_styles=self.custom_field_styles)


class KeyValueRendererWithFlatEventColors:
style_key = {"color": "cyan"}
style_val = {"color": "magenta"}
Expand All @@ -224,9 +239,10 @@ def __call__(self, _, __, event_dict):
ev = str(ev)

context = " ".join(
(ansi_wrap(key, **self.style_key) if self.color else key) +
"=" +
(ansi_wrap(repr(event_dict[key]), **self.style_val) if self.color else repr(event_dict[key]))
(ansi_wrap(key, **self.style_key) if self.color else key)
+ "="
+ (ansi_wrap(repr(event_dict[key]), **self.style_val) if self.color else repr(event_dict[key]))

for key in event_dict.keys() if key != "exc_info"
)

Expand Down

0 comments on commit c9def59

Please sign in to comment.