axiaoxin
axiaoxin

Reputation: 318

How to save python log in different file by different level in flask

My log code:

import os
import logging
import logging.handlers
import sys
from logging import raiseExceptions
from logging import Logger

LOG_PATH = '/tmp/'


class AppLogger(Logger):

    def __init__(self, name, level=logging.NOTSET):
        super(AppLogger, self).__init__(name, level)

    def callHandlers(self, record):
        """
        Pass a record to all relevant handlers.

        Loop through all handlers for this logger and its parents in the
    logger hierarchy. If no handler was found, output a one-off error
        message to sys.stderr. Stop searching up the hierarchy whenever a
        logger with the "propagate" attribute set to zero is found - that
        will be the last logger whose handlers are called.
        """
        c = self
        found = 0
        while c:
            for hdlr in c.handlers:
                found = found + 1
                if hdlr.name == 'console':
                    if record.levelno >= hdlr.level:
                        hdlr.handle(record)
                else:
                    if record.levelno == hdlr.level:
                        hdlr.handle(record)
            if not c.propagate:
                c = None  # break out
            else:
                c = c.parent
        if (found == 0) and raiseExceptions and not self.manager.emittedNoHandlerWarning:
            sys.stderr.write("No handlers could be found for logger"
                             " \"%s\"\n" % self.name)
            self.manager.emittedNoHandlerWarning = 1


def get_logger(logfile_name=__name__, log_path=LOG_PATH):
    '''save log to diffrent file by different log level into the log path
and print all log in console'''
    logging.setLoggerClass(AppLogger)
    formatter = logging.Formatter(
        '%(asctime)s %(name)s %(levelname)s %(message)s',
        '%Y-%m-%d %H:%M:%S')

    log_files = {
        logging.DEBUG: os.path.join(log_path, logfile_name + '-debug.log'),
    logging.INFO: os.path.join(log_path, logfile_name + '-info.log'),
        logging.WARNING: os.path.join(log_path, logfile_name + '-warning.log'),
        logging.ERROR: os.path.join(log_path, logfile_name + '-error.log'),
        logging.CRITICAL: os.path.join(log_path, logfile_name + '-critical.log')
    }
    logger = logging.getLogger()
    logger.name = 'app'
    logger.setLevel(logging.DEBUG)
    for log_level, log_file in log_files.items():
        file_handler = logging.handlers.TimedRotatingFileHandler(log_file, 'midnight')
        file_handler.setLevel(log_level)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

    console_handler = logging.StreamHandler()
    console_handler.name = "console"
    console_handler.setLevel(logging.DEBUG)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    return logger


logger = get_logger()

my flask code:

from log import logger

from flask import Flask
app = Flask(__name__)
app.debug = True


@app.route("/")
def hello():
    return "Hello World!"

logger.debug('----')
logger.info('----')
logger.error('----')
logger.warning('----')
app.run()

I want to save the DEBUG level log in debug.log, INFO level log in info.log, WARNING level log in warning.log, ERROR level log in error.log, both the flask framework's log and my custom log, and I need print all the log in console.

I custom the AppLogger, but now this just working on the flask framework's log, my custom log not save in right file, the info,warning,error all write in the info.log, the name app is my custom log, it save log together.

how to make the info.log just save the app's INFO log?

the info.log:

2017-11-08 20:07:31 app INFO ----
2017-11-08 20:07:31 app ERROR ----
2017-11-08 20:07:31 app WARNING ----
2017-11-08 20:07:31 werkzeug INFO  * Restarting with stat
2017-11-08 20:07:31 app INFO ----
2017-11-08 20:07:31 app ERROR ----
2017-11-08 20:07:31 app WARNING ----
2017-11-08 20:07:31 werkzeug INFO  * Debugger PIN: 971-444-041
2017-11-08 20:07:31 werkzeug INFO  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

the warning.log

2017-11-08 20:07:31 app ERROR ----
2017-11-08 20:07:31 app WARNING ----
2017-11-08 20:07:31 app ERROR ----
2017-11-08 20:07:31 app WARNING ----
2017-11-08 20:07:31 werkzeug WARNING  * Debugger is active!

Upvotes: 2

Views: 2415

Answers (2)

Chris2048
Chris2048

Reputation: 511

You can create a filter:

import logging

class LevelFilter(object):

    def __init__(self, level):
        self.level = logging._checkLevel(level)

    def filter(self, record):
        return record.levelno == self.level

Then associate this with each file:

(In YAML):

handlers:
  info_handler:
    class: logging.handlers.RotatingFileHandler
    filename: /tmp/info.log
    ...
    filters: [info_filter]
  error_handler:
    class: logging.handlers.RotatingFileHandler
    filename: /tmp/errors.log
    ...
    filters: [err_filter]

filters:
  info_filter:
    (): <somelogclassmodule>.LevelFilter
    level: INFO
  err_filter:
    (): <somelogclassmodule>.LevelFilter
    level: ERROR

root:
  handlers: [info_handler, error_handler]

(in raw python):

from <somelogclassmodule> import LevelFilter

root = logging.getLogger()

info_handler = logging.handlers.RotatingFileHandler('info.log', ...)
error_handler = logging.handlers.RotatingFileHandler('error.log', ...)

info_filter = LevelFilter('INFO')
err_filter = LevelFilter('ERROR')

info_handler.addFilter(info_filter)
error_handler.addFilter(err_filter)

# set formatters, etc..
...

root.addHandler(info_handler)
root.addHandler(error_handler)

Upvotes: 2

Argus Malware
Argus Malware

Reputation: 787

you have to add only file name for each log handler change your code by getting idea from this

import logging

class MultiFileHandler(logging.FileHandler):

    def __init__(self, filename, mode, encoding=None, delay=0):
        logging.FileHandler.__init__(self, filename, mode, encoding, delay)

    def emit(self, record):
        if self.should_change_file(record):
            self.change_file(record.file_id)
        logging.FileHandler.emit(self, record)

    def should_change_file(self, record):
        if not hasattr(record, 'file_id') or record.file_id == self.baseFilename:
             return False
        return True

    def change_file(self, file_id):
        self.stream.close()

        self.baseFilename = file_id
        self.stream = self._open()

if __name__ == '__main__':

    logger = logging.getLogger('request_logger')
    logger.setLevel(logging.DEBUG)    
    handler = MultiFileHandler(filename='out.log', mode='a')
    handler.setLevel(logging.DEBUG)    
    logger.addHandler(handler)

    # Log some messages to the original file
    logger.debug('debug message')
    logger.info('info message')

    # Log some messages to a different file
    logger.debug('debug message',       extra={'file_id':'debug.log'})
    logger.info('info message',         extra={'file_id':'info.log'})
    logger.warn('warn message',         extra={'file_id':'warn.log'})
    logger.error('error message',       extra={'file_id':'error.log'})

Upvotes: 1

Related Questions