Massive_Shed
Massive_Shed

Reputation: 47

Exclude ANSI escape sequences from output log file

I have set up a class of colours to make the stdout easier to read in case of warnings. I also want to write all print statements to a log file.

# Colour set up
class colours:
    warning = '\033[93m'
    colour1 = '\033[94m'
    colour2 = '\033[1m'
    terminate = '\033[0m'

# Logger set up
class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = open(“output.log”, "a")

    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)

sys.stdout = Logger()

# Example
print colours.warning + ‘WARNING!’ + colours.terminate

*in colour*:$ WARNING!

*in output.log*: [93mWARNING!

Is there any way to either write characters that will also colour the output.log file or print in colour to the stdout but not include '[94m' in the log file? I’d prefer not to require an install of any non-native python packages for user ease.

Upvotes: 3

Views: 3505

Answers (2)

Maciej Kopeć
Maciej Kopeć

Reputation: 91

I know, that is an old thread, but I want to leave my improved (in my mind) solution of @AKX for other people.

Improved solution

To automate what was done when logging, i.e. re.sub(...) one can create a custom formatter.

class TermEscapeCodeFormatter(logging.Formatter):
    """A class to strip the escape codes from the """
    def __init__(self, fmt=None, datefmt=None, style='%', validate=True):
        super().__init__(fmt, datefmt, style, validate)

    def format(self, record):
        escape_re = re.compile(r'\x1b\[[0-9;]*m')
        record.msg = re.sub(escape_re, "", str(record.msg))
        return super().format(record)

Then I set it as a Formatter for a particular FileHandler.

def main():
    lger = logging.getLogger("test_hw")
    lger.setLevel(logging.INFO)

    stdo = logging.StreamHandler()
    stdo.setLevel(logging.INFO)

    fhdr = logging.FileHandler("fname.txt", "w")
    fhdr.setLevel(logging.INFO)

    fmer = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    stdo.setFormatter(fmer)
    cfmer = TermEscapeCodeFormatter('%(asctime)s - %(levelname)s - %(message)s')
    fhdr.setFormatter(cfmer)

    lger.addHandler(stdo)
    lger.addHandler(fhdr)

Now one can use one logger with coloring (I use Colorama) to output to stdout with the colors enabled and to file with them disabled.

    lger.info(f"value = {Style.BRIGHT}{value :>5.2f} V{Style.RESET_ALL}")

Flaws

In this version works only for ANSI terminals, but one could adapt the regexp to match other patterns.

There is no defaults keyword argument as per newest (3.10 I believe) Python version. I use 3.8.

Upvotes: 1

AKX
AKX

Reputation: 168913

Use a regexp like \x1b\[[0-9;]*m to strip out the ANSI codes when writing to the output.log object?

I.e.

import re

ansi_re = re.compile(r'\x1b\[[0-9;]*m')

# ...

self.log.write(re.sub(ansi_re, '', message))

Upvotes: 3

Related Questions