كيف يمكنني لون بيثون تسجيل الإخراج؟ [python]


Answers

منذ سنوات كتبت معالج تيار ملون للاستخدام الخاص بي. ثم جئت عبر هذه الصفحة ووجدت مجموعة من مقتطفات الشفرة التي يقوم الناس بنسخها / لصقها :-( يعمل معالج البث حاليا فقط على أونيكس (لينوكس و ماك أوس X) ولكن الميزة هي أنه متوفر على بيبيجيثوب ) وانها ميتة بسيطة للاستخدام، كما أن لديها وضع التركيب فيم :-). في المستقبل أنا قد تمتد إلى العمل على ويندوز.

لتثبيت الحزمة:

$ pip install coloredlogs

للتأكد من أنه يعمل:

$ coloredlogs --demo

لبدء استخدام الشفرة الخاصة بك:

$ python
> import coloredlogs, logging
> coloredlogs.install()
> logging.info("It works!")
2014-07-30 21:21:26 peter-macbook root[7471] INFO It works!

يحتوي تنسيق السجل الافتراضي الموضح في المثال أعلاه على التاريخ والوقت واسم المضيف واسم المسجل و بيد ومستوى السجل ورسالة السجل. هذا هو ما يبدو في الممارسة العملية:

Question

منذ بعض الوقت، رأيت تطبيق مونو مع الإخراج الملونة، ويفترض بسبب نظام السجل الخاص به (لأن جميع الرسائل كانت موحدة).

الآن، بيثون لديه وحدة logging ، والذي يتيح لك تحديد الكثير من الخيارات لتخصيص الإخراج. لذلك، أنا تخيل شيء مماثل سيكون من الممكن مع بيثون، ولكن لا أستطيع أن أعرف كيفية القيام بذلك في أي مكان.

هل هناك أي طريقة لجعل بيثون الإخراج وحدة الانتاج في اللون؟

ما أريد (على سبيل المثال) أخطاء باللون الأحمر، رسائل التصحيح باللون الأزرق أو الأصفر، وهلم جرا.

وبطبيعة الحال هذا قد يتطلب محطة متوافقة (معظم المحطات الحديثة هي). ولكن يمكن أن تراجع إلى الإخراج الأصلي logging إذا لم يتم اعتماد اللون.

أي أفكار كيف يمكنني الحصول على الانتاج الملونة مع وحدة تسجيل؟




حل سريع وقذر لمستويات سجل محددة مسبقا وبدون تعريف فئة جديدة.

logging.addLevelName( logging.WARNING, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.WARNING))
logging.addLevelName( logging.ERROR, "\033[1;41m%s\033[1;0m" % logging.getLevelName(logging.ERROR))



لقد قمت بتحديث المثال من إيرميند دعم العلامات للمقدمة والخلفية. مجرد استخدام المتغيرات اللون $ أسود - $ وايت في سلسلة المنسق السجل الخاص بك. لتعيين الخلفية مجرد استخدام $ بغ-بلاك - $ بغ-وايت.

import logging

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

COLORS = {
    'WARNING'  : YELLOW,
    'INFO'     : WHITE,
    'DEBUG'    : BLUE,
    'CRITICAL' : YELLOW,
    'ERROR'    : RED,
    'RED'      : RED,
    'GREEN'    : GREEN,
    'YELLOW'   : YELLOW,
    'BLUE'     : BLUE,
    'MAGENTA'  : MAGENTA,
    'CYAN'     : CYAN,
    'WHITE'    : WHITE,
}

RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ  = "\033[1m"

class ColorFormatter(logging.Formatter):

    def __init__(self, *args, **kwargs):
        # can't do super(...) here because Formatter is an old school class
        logging.Formatter.__init__(self, *args, **kwargs)

    def format(self, record):
        levelname = record.levelname
        color     = COLOR_SEQ % (30 + COLORS[levelname])
        message   = logging.Formatter.format(self, record)
        message   = message.replace("$RESET", RESET_SEQ)\
                           .replace("$BOLD",  BOLD_SEQ)\
                           .replace("$COLOR", color)
        for k,v in COLORS.items():
            message = message.replace("$" + k,    COLOR_SEQ % (v+30))\
                             .replace("$BG" + k,  COLOR_SEQ % (v+40))\
                             .replace("$BG-" + k, COLOR_SEQ % (v+40))
        return message + RESET_SEQ

logging.ColorFormatter = ColorFormatter

حتى الآن يمكنك بسيطة القيام بما يلي في ملف التكوين الخاص بك:

[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s



عدلت المثال الأصلي الذي قدمه سورين وتحت تصنيف سترهاندلر إلى كولوريدزكونسوليهاندلر.

الجانب السلبي من الحل هو أنه يعدل الرسالة، ولأن ذلك هو تعديل لوغمساج الفعلية أي معالجات أخرى سوف تحصل على رسالة معدلة كذلك.

أدى هذا إلى ملفات لوغفيلز مع كولوركوديس في حالتنا لأننا نستخدم عدة قطع الاشجار.

فئة أدناه يعمل فقط على منصات دعم أنسي، ولكن يجب أن يكون تافهة لإضافة كولوركوديس ويندوز لذلك.

import copy
import logging


class ColoredConsoleHandler(logging.StreamHandler):
    def emit(self, record):
        # Need to make a actual copy of the record
        # to prevent altering the message for other loggers
        myrecord = copy.copy(record)
        levelno = myrecord.levelno
        if(levelno >= 50):  # CRITICAL / FATAL
            color = '\x1b[31m'  # red
        elif(levelno >= 40):  # ERROR
            color = '\x1b[31m'  # red
        elif(levelno >= 30):  # WARNING
            color = '\x1b[33m'  # yellow
        elif(levelno >= 20):  # INFO
            color = '\x1b[32m'  # green
        elif(levelno >= 10):  # DEBUG
            color = '\x1b[35m'  # pink
        else:  # NOTSET and anything else
            color = '\x1b[0m'  # normal
        myrecord.msg = color + str(myrecord.msg) + '\x1b[0m'  # normal
        logging.StreamHandler.emit(self, myrecord)






import logging
import sys

colors = {'pink': '\033[95m', 'blue': '\033[94m', 'green': '\033[92m', 'yellow': '\033[93m', 'red': '\033[91m',
      'ENDC': '\033[0m', 'bold': '\033[1m', 'underline': '\033[4m'}

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)


def str_color(color, data):
    return colors[color] + str(data) + colors['ENDC']

params = {'param1': id1, 'param2': id2}

logging.info('\nParams:' + str_color("blue", str(params)))`



وإليك الحل:

class ColouredFormatter(logging.Formatter):
    RESET = '\x1B[0m'
    RED = '\x1B[31m'
    YELLOW = '\x1B[33m'
    BRGREEN = '\x1B[01;32m'  # grey in solarized for terminals

    def format(self, record, colour=False):
        message = super().format(record)

        if not colour:
            return message

        level_no = record.levelno
        if level_no >= logging.CRITICAL:
            colour = self.RED
        elif level_no >= logging.ERROR:
            colour = self.RED
        elif level_no >= logging.WARNING:
            colour = self.YELLOW
        elif level_no >= logging.INFO:
            colour = self.RESET
        elif level_no >= logging.DEBUG:
            colour = self.BRGREEN
        else:
            colour = self.RESET

        message = colour + message + self.RESET

        return message


class ColouredHandler(logging.StreamHandler):
    def __init__(self, stream=sys.stdout):
        super().__init__(stream)

    def format(self, record, colour=False):
        if not isinstance(self.formatter, ColouredFormatter):
            self.formatter = ColouredFormatter()

        return self.formatter.format(record, colour)

    def emit(self, record):
        stream = self.stream
        try:
            msg = self.format(record, stream.isatty())
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)


h = ColouredHandler()
h.formatter = ColouredFormatter('{asctime} {levelname:8} {message}', '%Y-%m-%d %H:%M:%S', '{')
logging.basicConfig(level=logging.DEBUG, handlers=[h])



في حين أن الحلول الأخرى تبدو جيدة لديهم بعض القضايا. البعض لا لون الخطوط كلها التي لا يريد بعض الأوقات، وبعض حذف أي تكوين قد يكون لديك معا. الحل أدناه لا يؤثر على أي شيء سوى الرسالة نفسها.

الشفرة

class ColoredFormatter(logging.Formatter):
    def format(self, record):
        if record.levelno == logging.WARNING:
            record.msg = '\033[93m%s\033[0m' % record.msg
        elif record.levelno == logging.ERROR:
            record.msg = '\033[91m%s\033[0m' % record.msg
        return logging.Formatter.format(self, record)

مثال

logger = logging.getLogger('mylogger')
handler = logging.StreamHandler()

log_format = '[%(asctime)s]:%(levelname)-7s:%(message)s'
time_format = '%H:%M:%S'
formatter = ColoredFormatter(log_format, datefmt=time_format)
handler.setFormatter(formatter)
logger.addHandler(handler)

logger.warn('this should be yellow')
logger.error('this should be red')

انتاج |

[17:01:36]:WARNING:this should be yellow
[17:01:37]:ERROR  :this should be red

كما ترى، كل شيء آخر لا يزال يحصل المخرجات وتبقى في لونها الأولي. إذا كنت ترغب في تغيير أي شيء آخر من الرسالة يمكنك ببساطة تمرير رموز الألوان إلى log_format في المثال.




استخدام بيفانسي .

مثال:

print(pyfancy.RED + "Hello Red!" + pyfancy.END)



import logging

logging.basicConfig(filename="f.log" filemode='w', level=logging.INFO,
                    format = "%(logger_name)s %(color)s  %(message)s %(endColor)s")


class Logger(object):
    __GREEN = "\033[92m"
    __RED = '\033[91m'
    __ENDC = '\033[0m'

    def __init__(self, name):
        self.logger = logging.getLogger(name)
        self.extra={'logger_name': name, 'endColor': self.__ENDC, 'color': self.__GREEN}


    def info(self, msg):
        self.extra['color'] = self.__GREEN
        self.logger.info(msg, extra=self.extra)

    def error(self, msg):
        self.extra['color'] = self.__RED
        self.logger.error(msg, extra=self.extra)

استعمال

Logger("File Name").info("This shows green text")




هناك طن من الردود. ولكن لا شيء يتحدث عن الديكور. حتى هنا الألغام.

لأنه هو الكثير أكثر بساطة.

ليس هناك حاجة لاستيراد أي شيء، ولا لكتابة أي فئة فرعية:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import logging


NO_COLOR = "\33[m"
RED, GREEN, ORANGE, BLUE, PURPLE, LBLUE, GREY = \
    map("\33[%dm".__mod__, range(31, 38))

logging.basicConfig(format="%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)

# the decorator to apply on the logger methods info, warn, ...
def add_color(logger_method, color):
  def wrapper(message, *args, **kwargs):
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

for level, color in zip((
  "info", "warn", "error", "debug"), (
  GREEN, ORANGE, RED, BLUE
)):
  setattr(logger, level, add_color(getattr(logger, level), color))

# this is displayed in red.
logger.error("Launching %s." % __file__)

هذه المجموعة الأخطاء في رسائل التصحيح الحمراء باللون الأزرق، وهلم جرا. كما طلب في السؤال.

حتى نتمكن من التكيف مع المجمع لاتخاذ colorحجة لdynamicaly مجموعة الألوان الرسالة باستخدامlogger.debug("message", color=GREY)

تحرير: حتى هنا في الديكور تكييفها لضبط الألوان في وقت التشغيل:

def add_color(logger_method, _color):
  def wrapper(message, *args, **kwargs):
    color = kwargs.pop("color", _color)
    if isinstance(color, int):
      color = "\33[%dm" % color
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

# blah blah, apply the decorator...

# this is displayed in red.
logger.error("Launching %s." % __file__)
# this is displayed in blue
logger.error("Launching %s." % __file__, color=34)
# and this, in grey
logger.error("Launching %s." % __file__, color=GREY)