From 140726c2cb35dd5f770c4bde86fee3f5d61348b6 Mon Sep 17 00:00:00 2001
From: Art Lukyanchyk <artiom.lukyanchyk@hs-hannover.de>
Date: Tue, 12 Sep 2017 15:08:58 +0200
Subject: [PATCH] Fix and adjust the colored logger code.

---
 colored_logger.py | 91 ++++++++++++++++++++++++-----------------------
 1 file changed, 46 insertions(+), 45 deletions(-)

diff --git a/colored_logger.py b/colored_logger.py
index 42a5f6f..300fab3 100644
--- a/colored_logger.py
+++ b/colored_logger.py
@@ -1,12 +1,7 @@
 
 """
-
-This module decorates methods of Logger class.
-It's quite harmless, unless you use some weird non-tty stdout/stderr while DEBUG-ing.
-
-This hack should be auto-loaded by it's app.
-If you don't use the app, just import this file to apply the hack.
-
+This module decorates/patches methods of logging.Logger class, plus patches already existing logger instances.
+It's quite harmless, unless you use some weird non-tty stdout/stderr.
 """
 
 import sys
@@ -16,9 +11,10 @@ import random
 
 
 # Control sequences (just google VT52 to see the compatibility burden straight from 1975)
-color_set = "\x1b[{}m{}"
+color_set = "\x1b[{}m"
 color_reset = "\x1b[0m"
 
+
 # useless stuff displayed before applying patches... yes, I felt very bored
 intro_words = ["magic", "MaGiC", "voodoo", "sorcery", "wizardry", "witchery", "foobar", "rainbow"]
 intro_adjectives = ["colourful", "evil", "fairy", "random"]
@@ -34,45 +30,30 @@ decorate_info = {
     "exception": "1;95",
 }
 
-if settings.DEBUG:  # probably PyCharm or some other IDE with own stream handling
-    # useless foo
-    intro_seq = zip(itertools.cycle(intro_colors), random.choice(intro_words))
-    print(
-        "{adj} {word}{reset}:".format(
-            adj=random.choice(intro_adjectives).capitalize(),
-            word=str().join(color_set.format(*symbol) for symbol in intro_seq),
-            reset=color_reset,
-        ),
-        end=" ")
-    # Patching handlers: Python logging.StreamHandler targets stderr by default,
-    # therefore Django example config does, therefore our logging config also does,
-    # so PyCharm believes every line of log is error. Well... Fixing it now.
-    # Existing loggers first.
-    patched = list()
-    for logger in logging.Logger.manager.loggerDict.values():
-        for handler in getattr(logger, "handlers", list()):
-            if isinstance(handler, logging.StreamHandler):
-                if handler.stream == sys.stderr:
-                    handler.stream = sys.stdout
-                    patched.append(handler)
-    # Future loggers (they should be initially created with the proper stream).
-    original_constructor = logging.StreamHandler.__init__  # must grab a reference outside of the lambda
-    logging.StreamHandler.__init__ = lambda zelf, stream=None: original_constructor(zelf, stream or sys.stdout)
-    # the isatty() stuff below solved some problems in the past, I'm not sure anymore which exactly
-    try:
-        stdout_tty = sys.stdout.isatty()
-    except Exception:
-        stdout_tty = False
-    if not stdout_tty:
-        patched.append(sys.stdout.isatty)
-        sys.stdout.isatty = lambda: True  # stdout is now a TTY no matter what
-    # reporting
-    print("applied {n} logging patches.".format(n=len(patched)))
-    logging.getLogger().debug("Logging patches: {}.".format(", ".join(str(e) for e in patched) or "none"))
-
+# Adjusting handlers: Python logging.StreamHandler targets stderr by default.
+# Django default logging configuration doesn't change it, too.
+# Existing loggers first.
+patched_objects = set()
+for logger in logging.Logger.manager.loggerDict.values():
+    for handler in getattr(logger, "handlers", list()):
+        if isinstance(handler, logging.StreamHandler):
+            if handler.stream == sys.stderr:
+                handler.stream = sys.stdout
+                patched_objects.add(handler)
+# Future loggers (they should be initially created with the proper stream).
+original_constructor = logging.StreamHandler.__init__  # must grab a reference outside of the lambda
+logging.StreamHandler.__init__ = lambda zelf, stream=None: original_constructor(zelf, stream or sys.stdout)
+# the isatty() stuff below solved some problems in the past, I'm not sure anymore which exactly
+try:
+    stdout_tty = sys.stdout.isatty()
+except Exception:
+    stdout_tty = False
+if not stdout_tty:
+    patched_objects.add(sys.stdout.isatty)
+    sys.stdout.isatty = lambda: True  # stdout is now a TTY no matter what
 
 def _apply_color_to_msg(msg, color, extras):
-    return "".join([color_set.format(color, extras), str(msg), color_reset])
+    return "".join([color_set.format(color), extras, str(msg), color_reset])
 
 
 def decorate_for_tty(method, color="1;39", extras=""):
@@ -92,7 +73,27 @@ for entity in [logging.Logger]:
         if isinstance(entity, logging.Logger) or entity is logging.Logger:
             new_method = decorate_for_tty(getattr(entity, method_name), color_str)
             setattr(entity, method_name, new_method)
+            patched_objects.add(entity)
         else:
             pass  # that should be a placeholder object (non-public API), ignoring it
 
 
+# useless foo
+intro_seq = zip(itertools.cycle(intro_colors), random.choice(intro_words))
+intro_msg = "{adj} {word}{reset}".format(
+    adj=random.choice(intro_adjectives).capitalize(),
+    word=str().join((color_set + "{}").format(*symbol) for symbol in intro_seq),
+    reset=color_reset,
+)
+
+# reporting
+sys.stdout.write("{intro_msg}{c}: patched {n} object{s}{cr}\n".format(
+    intro_msg=intro_msg,
+    c=color_set.format("1;90"),
+    n=len(patched_objects),
+    s="" if len(patched_objects) is 1 else "s",
+    cr=color_reset,
+))
+logging.getLogger("pydevutils").debug("Logging patches: {}.".format(", ".join(str(e) for e in patched_objects) or "none"))
+
+
-- 
GitLab