Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write header to a python log file, but only if a record gets written

Tags:

python

logging

fh = logging.FileHandler('example.log',delay = True)
fh.setLevel(logging.INFO)

Since delay is True, the file will never be written unless something is logged. At that point, the first line in the file is the first record, and it will contain the asctime, levelname etc elements

Using python 2.7.10, is there a sane way to add a line (or two) the first time a record is written that don't include those elements?

I can just write to the file before using it for logging, but if I do that, I end up with logs empty but for the header.

The desired output might look like:

Using test.fil with option 7
2015-11-01 13:57:58,045 :log_example: INFO     fn:main result:process 4 complete --7 knights said ni
2015-11-01 13:57:58,045 :log_example: INFO     fn:main result:process 3 complete --3 bunnies attacked

Thanks,

like image 954
Vorpal Swordsman Avatar asked Nov 01 '15 22:11

Vorpal Swordsman


People also ask

How do you log an exception in Python?

To log an exception in Python we can use logging module and through that we can log the error. Logging an exception in python with an error can be done in the logging. exception() method. This function logs a message with level ERROR on this logger.

What is the difference between print and logging in Python?

print will just print the text to console. console. log() actually records it and we can use it for many purposes like email it for bug report.

What is StreamHandler in Python logging?

The StreamHandler class, located in the core logging package, sends logging output to streams such as sys. stdout, sys. stderr or any file-like object (or, more precisely, any object which supports write() and flush() methods).


2 Answers

Sub class the FileHandler to create your own custom FileHandleWithHeader as shown below:

import os
import logging

# Create a class that extends the FileHandler class from logging.FileHandler
class FileHandlerWithHeader(logging.FileHandler):

    # Pass the file name and header string to the constructor.
    def __init__(self, filename, header,  mode='a', encoding=None, delay=0):
        # Store the header information.
        self.header = header

        # Determine if the file pre-exists
        self.file_pre_exists = os.path.exists(filename)

        # Call the parent __init__
        logging.FileHandler.__init__(self, filename, mode, encoding, delay)

        # Write the header if delay is False and a file stream was created.
        if not delay and self.stream is not None:
            self.stream.write('%s\n' % header)

    def emit(self, record):
        # Create the file stream if not already created.
        if self.stream is None:
            self.stream = self._open()

            # If the file pre_exists, it should already have a header.
            # Else write the header to the file so that it is the first line.
            if not self.file_pre_exists:
                self.stream.write('%s\n' % self.header)

        # Call the parent class emit function.
        logging.FileHandler.emit(self, record)

# Create a logger and set the logging level.
logger = logging.getLogger("example")
logger.setLevel(logging.INFO)

# Create a file handler from our new FileHandlerWith Header class and set the
# logging level.
fh = FileHandlerWithHeader('example.log', 'This is my header',  delay=True)
fh.setLevel(logging.INFO)

# Add formatter to the file handler.
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
fh.setFormatter(formatter)

# Add the handler to the logger.
logger.addHandler(fh)

# Since the constructor of the FileHandlerWithHeader was passed delay=True
# the file should not exist until the first log as long as the log file did
# not pre-exist.
print "Ready to write to the the example.log file."
raw_input("Press Enter to continue...")

# Send 3 logs to the logger.
logger.info("First line in the file")
logger.info("Second line in the file")
logger.info("Third line in the file")

# The log file should now be created and only have a header at the begining of
# the file.
print "The example.log file should exist and have a header."

This script should run as is in Python 2.7. If the "example.log" file already exists, it will not recreate the header.

This solution required knowledge of the logging source code found here and general use of the python logging package found here.

like image 156
Joshua Goldberg Avatar answered Oct 13 '22 01:10

Joshua Goldberg


I had a simpler idea. The following just uses a custom formatter. The first message formatted spits out a header record then after that just does normal formatting.

import logging

class FormatterWithHeader(logging.Formatter):
    def __init__(self, header, fmt=None, datefmt=None, style='%'):
        super().__init__(fmt, datefmt, style)
        self.header = header # This is hard coded but you could make dynamic
        # Override the normal format method
        self.format = self.first_line_format

    def first_line_format(self, record):
        # First time in, switch back to the normal format function
        self.format = super().format
        return self.header + "\n" + self.format(record)

def test_logger():
    logger = logging.getLogger("test")
    logger.setLevel(logging.DEBUG)
    formatter = FormatterWithHeader('First Line Only')
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    logger.info("This line will kick out a header first.")
    logger.info("This line will *not* kick out a header.")
like image 29
RobertB Avatar answered Oct 13 '22 00:10

RobertB