Logging with Python – The hidden features

Written by

Logs levels and priorities

By default the logging library is configured to display only the logs with a priority higher or equal to WARNING.

In production, a lot of logs are generated. If an error occurred. it is like finding a needle in a hail.

That’s why the default log level is WARNING.

Here a summary with logs priorities in Python.

LevelDescriptionPriority
NOTSETDisplay no logs at all0
DEBUGUsed for debugging purposes10
INFOLogs display when program working as expected20
WARNWarning logs when something is not harmful but a bit strange30
ERRORSomething has failed, but the program can still continue running (e.g., a file failed to load, but the app can keep going).40
CRITICALA serious error has occurred, and the program might have to stop entirely (e.g., database connection failure in a critical service).50

Let’s validate this

# 1
import logging

logging.basicConfig()

logging.info("Exercise 1 completed!")

Nothing is displayed to the screen

# 2
import logging

logging.basicConfig(level="INFO")

logging.info("Exercise 1 completed!")

Log is displayed to the screen

INFO:root:Exercise 1 completed!

Different ways to redirect the logs

  • Output of the terminal
  • To a file
  • To an API (GET, POST)
  • To a file

To the output of the console:

INFO:root:Exercise 1 completed!

To a file:

We add the named parameter filename to the basicConfiguration.

# 3
import logging

logging.basicConfig(filename="out.log", level=logging.DEBUG)

logging.warning("I am logging a warning")
logging.info("I am logging an info when the program is running by default")
logging.exception("I am logging an exception when an exception is raised")
logging.error("I am logging an error when a value is incorrect but not exception is raised")
logging.debug("I am logging a debug when debug mode is enabled")
logging.critical("I am logging a critical message")
logging.error("An other important error")
logging.error("Final important error")

Content of the file generated:

# out.log
WARNING:root:I am logging a warning
INFO:root:I am logging an info when the program is running by default
ERROR:root:I am logging an exception when an exception is raised
NoneType: None
ERROR:root:I am logging an error when a value is incorrect but not exception is raised
DEBUG:root:I am logging a debug when debug mode is enabled
CRITICAL:root:I am logging a critical message
ERROR:root:An other important error
ERROR:root:Final important error

To an API

Let’s create a small flask API to test this feature.

pip install flask
# app.py
from pprint import pprint

import flask
from flask import Flask, request

# create the Flask app
app = Flask(__name__)


@app.route('/log', methods=["GET", "POST"])
def log_view():
    if request.method == 'POST':
        pprint(request.form)
    else:
        pprint(request.args)

    return 'Query String Example'


@app.route('/safelog', methods=["GET", "POST"])
def safelog_view():
    if request.authorization:
        if (
            request.authorization.username != "user"
            | request.authorization.password != "valid_password"
        ):
            flask.abort(401)

    if request.method == 'POST':
        pprint(request.form)
    else:
        pprint(request.args)

    return 'Query String Example'


if __name__ == '__main__':
    # run app in debug mode on port 5000
    app.run(debug=True, port=5000)
flask run

Script to send the logs to the API using a GET request

# 4

# Redirect logs (DEBUG to CRITICAL level) to an API via a GET request without displaying those

import logging
import logging.handlers


logger = logging.getLogger("fragment_upload_logger")
logger.setLevel(logging.DEBUG)

# choose the best handler for the task
# create a new instance of the handler
hh = logging.handlers.HTTPHandler("127.0.0.1:5000", "/log")
# set up level for handler
hh.setLevel(logging.DEBUG)
# add handler to logger
logger.addHandler(hh)

logger.warning("I am logging a warning")
logger.info("I am logging an info when the program is running by default")
logger.exception("I am logging an exception when an exception is raised")
logger.error("I am logging an error when a value is incorrect but not exception is raised")
logger.debug("I am logging a debug when debug mode is enabled")
logger.critical("I am logging a critical message")
logger.error("An other important error")
logger.error("Final important error")

The data received by the API for GET request.

ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging a warning'), ('args', '()'), ('levelname', 'WARNING'), ('levelno', '30'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py'), ('filename', 'exercise_7.py'), ('module', 'exercise_7'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '18'), ('funcName', '<module>'), ('created', '1740746414.978643'), ('msecs', '978.6429405212402'), ('relativeCreated', '6.928920745849609'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47364')])
127.0.0.1 - - [28/Feb/2025 12:40:14] "GET /log?name=fragment_upload_logger&msg=I+am+logging+a+warning&args=()&levelname=WARNING&levelno=30&pathname=/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py&filename=exercise_7.py&module=exercise_7&exc_info=None&exc_text=None&stack_info=None&lineno=18&funcName=<module>&created=1740746414.978643&msecs=978.6429405212402&relativeCreated=6.928920745849609&thread=8570805056&threadName=MainThread&processName=MainProcess&process=47364 HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging an info when the program is running by default'), ('args', '()'), ('levelname', 'INFO'), ('levelno', '20'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py'), ('filename', 'exercise_7.py'), ('module', 'exercise_7'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '19'), ('funcName', '<module>'), ('created', '1740746414.992008'), ('msecs', '992.0079708099365'), ('relativeCreated', '20.2939510345459'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47364')])
127.0.0.1 - - [28/Feb/2025 12:40:14] "GET /log?name=fragment_upload_logger&msg=I+am+logging+an+info+when+the+program+is+running+by+default&args=()&levelname=INFO&levelno=20&pathname=/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py&filename=exercise_7.py&module=exercise_7&exc_info=None&exc_text=None&stack_info=None&lineno=19&funcName=<module>&created=1740746414.992008&msecs=992.0079708099365&relativeCreated=20.2939510345459&thread=8570805056&threadName=MainThread&processName=MainProcess&process=47364 HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging an exception when an exception is raised'), ('args', '()'), ('levelname', 'ERROR'), ('levelno', '40'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py'), ('filename', 'exercise_7.py'), ('module', 'exercise_7'), ('exc_info', '(None, None, None)'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '20'), ('funcName', '<module>'), ('created', '1740746414.9926798'), ('msecs', '992.6798343658447'), ('relativeCreated', '20.9658145904541'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47364')])
127.0.0.1 - - [28/Feb/2025 12:40:14] "GET /log?name=fragment_upload_logger&msg=I+am+logging+an+exception+when+an+exception+is+raised&args=()&levelname=ERROR&levelno=40&pathname=/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py&filename=exercise_7.py&module=exercise_7&exc_info=(None,+None,+None)&exc_text=None&stack_info=None&lineno=20&funcName=<module>&created=1740746414.9926798&msecs=992.6798343658447&relativeCreated=20.9658145904541&thread=8570805056&threadName=MainThread&processName=MainProcess&process=47364 HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging an error when a value is incorrect but not exception is raised'), ('args', '()'), ('levelname', 'ERROR'), ('levelno', '40'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py'), ('filename', 'exercise_7.py'), ('module', 'exercise_7'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '21'), ('funcName', '<module>'), ('created', '1740746414.993303'), ('msecs', '993.3030605316162'), ('relativeCreated', '21.589040756225586'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47364')])
127.0.0.1 - - [28/Feb/2025 12:40:14] "GET /log?name=fragment_upload_logger&msg=I+am+logging+an+error+when+a+value+is+incorrect+but+not+exception+is+raised&args=()&levelname=ERROR&levelno=40&pathname=/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py&filename=exercise_7.py&module=exercise_7&exc_info=None&exc_text=None&stack_info=None&lineno=21&funcName=<module>&created=1740746414.993303&msecs=993.3030605316162&relativeCreated=21.589040756225586&thread=8570805056&threadName=MainThread&processName=MainProcess&process=47364 HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging a debug when debug mode is enabled'), ('args', '()'), ('levelname', 'DEBUG'), ('levelno', '10'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py'), ('filename', 'exercise_7.py'), ('module', 'exercise_7'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '22'), ('funcName', '<module>'), ('created', '1740746414.9939709'), ('msecs', '993.9708709716797'), ('relativeCreated', '22.256851196289062'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47364')])
127.0.0.1 - - [28/Feb/2025 12:40:14] "GET /log?name=fragment_upload_logger&msg=I+am+logging+a+debug+when+debug+mode+is+enabled&args=()&levelname=DEBUG&levelno=10&pathname=/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py&filename=exercise_7.py&module=exercise_7&exc_info=None&exc_text=None&stack_info=None&lineno=22&funcName=<module>&created=1740746414.9939709&msecs=993.9708709716797&relativeCreated=22.256851196289062&thread=8570805056&threadName=MainThread&processName=MainProcess&process=47364 HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging a critical message'), ('args', '()'), ('levelname', 'CRITICAL'), ('levelno', '50'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py'), ('filename', 'exercise_7.py'), ('module', 'exercise_7'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '23'), ('funcName', '<module>'), ('created', '1740746414.9946082'), ('msecs', '994.6081638336182'), ('relativeCreated', '22.89414405822754'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47364')])
127.0.0.1 - - [28/Feb/2025 12:40:14] "GET /log?name=fragment_upload_logger&msg=I+am+logging+a+critical+message&args=()&levelname=CRITICAL&levelno=50&pathname=/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py&filename=exercise_7.py&module=exercise_7&exc_info=None&exc_text=None&stack_info=None&lineno=23&funcName=<module>&created=1740746414.9946082&msecs=994.6081638336182&relativeCreated=22.89414405822754&thread=8570805056&threadName=MainThread&processName=MainProcess&process=47364 HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'An other important error'), ('args', '()'), ('levelname', 'ERROR'), ('levelno', '40'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py'), ('filename', 'exercise_7.py'), ('module', 'exercise_7'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '24'), ('funcName', '<module>'), ('created', '1740746414.995287'), ('msecs', '995.2869415283203'), ('relativeCreated', '23.572921752929688'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47364')])
127.0.0.1 - - [28/Feb/2025 12:40:14] "GET /log?name=fragment_upload_logger&msg=An+other+important+error&args=()&levelname=ERROR&levelno=40&pathname=/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py&filename=exercise_7.py&module=exercise_7&exc_info=None&exc_text=None&stack_info=None&lineno=24&funcName=<module>&created=1740746414.995287&msecs=995.2869415283203&relativeCreated=23.572921752929688&thread=8570805056&threadName=MainThread&processName=MainProcess&process=47364 HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'Final important error'), ('args', '()'), ('levelname', 'ERROR'), ('levelno', '40'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py'), ('filename', 'exercise_7.py'), ('module', 'exercise_7'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '25'), ('funcName', '<module>'), ('created', '1740746414.995926'), ('msecs', '995.9259033203125'), ('relativeCreated', '24.211883544921875'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47364')])
127.0.0.1 - - [28/Feb/2025 12:40:14] "GET /log?name=fragment_upload_logger&msg=Final+important+error&args=()&levelname=ERROR&levelno=40&pathname=/Users/aldazar/Projects/loggingActivity/solution/exercise_7.py&filename=exercise_7.py&module=exercise_7&exc_info=None&exc_text=None&stack_info=None&lineno=25&funcName=<module>&created=1740746414.995926&msecs=995.9259033203125&relativeCreated=24.211883544921875&thread=8570805056&threadName=MainThread&processName=MainProcess&process=47364

Script to send the logs to the API using a POST request

# 5

# Redirect logs (DEBUG to CRITICAL level) to an API via a POST request without displaying those

import logging
import logging.handlers

logging.basicConfig(level=logging.DEBUG)

logger = logging.getLogger("fragment_upload_logger")
logger.setLevel(logging.DEBUG)

hh = logging.handlers.HTTPHandler("127.0.0.1:5000", "/log", method="POST")
hh.setLevel(logging.DEBUG)

logger.addHandler(hh)

logger.warning("I am logging a warning")
logger.info("I am logging an info when the program is running by default")
logger.exception("I am logging an exception when an exception is raised")
logger.error("I am logging an error when a value is incorrect but not exception is raised")
logger.debug("I am logging a debug when debug mode is enabled")
logger.critical("I am logging a critical message")
logger.error("An other important error")
logger.error("Final important error")

The data received by the API for POST request.

ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging a warning'), ('args', '()'), ('levelname', 'WARNING'), ('levelno', '30'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_8.py'), ('filename', 'exercise_8.py'), ('module', 'exercise_8'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '16'), ('funcName', '<module>'), ('created', '1740746480.444159'), ('msecs', '444.15903091430664'), ('relativeCreated', '7.8868865966796875'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47415')])
127.0.0.1 - - [28/Feb/2025 12:41:20] "POST /log HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging an info when the program is running by default'), ('args', '()'), ('levelname', 'INFO'), ('levelno', '20'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_8.py'), ('filename', 'exercise_8.py'), ('module', 'exercise_8'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '17'), ('funcName', '<module>'), ('created', '1740746480.463577'), ('msecs', '463.5770320892334'), ('relativeCreated', '27.304887771606445'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47415')])
127.0.0.1 - - [28/Feb/2025 12:41:20] "POST /log HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging an exception when an exception is raised'), ('args', '()'), ('levelname', 'ERROR'), ('levelno', '40'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_8.py'), ('filename', 'exercise_8.py'), ('module', 'exercise_8'), ('exc_info', '(None, None, None)'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '18'), ('funcName', '<module>'), ('created', '1740746480.4643378'), ('msecs', '464.3378257751465'), ('relativeCreated', '28.06568145751953'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47415')])
127.0.0.1 - - [28/Feb/2025 12:41:20] "POST /log HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging an error when a value is incorrect but not exception is raised'), ('args', '()'), ('levelname', 'ERROR'), ('levelno', '40'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_8.py'), ('filename', 'exercise_8.py'), ('module', 'exercise_8'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '19'), ('funcName', '<module>'), ('created', '1740746480.465029'), ('msecs', '465.0290012359619'), ('relativeCreated', '28.75685691833496'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47415')])
127.0.0.1 - - [28/Feb/2025 12:41:20] "POST /log HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging a debug when debug mode is enabled'), ('args', '()'), ('levelname', 'DEBUG'), ('levelno', '10'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_8.py'), ('filename', 'exercise_8.py'), ('module', 'exercise_8'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '20'), ('funcName', '<module>'), ('created', '1740746480.465652'), ('msecs', '465.6519889831543'), ('relativeCreated', '29.379844665527344'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47415')])
127.0.0.1 - - [28/Feb/2025 12:41:20] "POST /log HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'I am logging a critical message'), ('args', '()'), ('levelname', 'CRITICAL'), ('levelno', '50'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_8.py'), ('filename', 'exercise_8.py'), ('module', 'exercise_8'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '21'), ('funcName', '<module>'), ('created', '1740746480.4785762'), ('msecs', '478.5761833190918'), ('relativeCreated', '42.304039001464844'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47415')])
127.0.0.1 - - [28/Feb/2025 12:41:20] "POST /log HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'An other important error'), ('args', '()'), ('levelname', 'ERROR'), ('levelno', '40'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_8.py'), ('filename', 'exercise_8.py'), ('module', 'exercise_8'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '22'), ('funcName', '<module>'), ('created', '1740746480.479579'), ('msecs', '479.57897186279297'), ('relativeCreated', '43.306827545166016'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47415')])
127.0.0.1 - - [28/Feb/2025 12:41:20] "POST /log HTTP/1.1" 200 -
ImmutableMultiDict([('name', 'fragment_upload_logger'), ('msg', 'Final important error'), ('args', '()'), ('levelname', 'ERROR'), ('levelno', '40'), ('pathname', '/Users/aldazar/Projects/loggingActivity/solution/exercise_8.py'), ('filename', 'exercise_8.py'), ('module', 'exercise_8'), ('exc_info', 'None'), ('exc_text', 'None'), ('stack_info', 'None'), ('lineno', '23'), ('funcName', '<module>'), ('created', '1740746480.480315'), ('msecs', '480.3149700164795'), ('relativeCreated', '44.04282569885254'), ('thread', '8570805056'), ('threadName', 'MainThread'), ('processName', 'MainProcess'), ('process', '47415')])

To an email

# 6

# Send logs (CRITICAL level) to your email
# You will need to authorise your gmail to send a mail from a script first
# https://support.google.com/accounts/answer/185833?visit_id=638209478478332918-4005418056&p=InvalidSecondFactor&rd=1

import logging
import logging.handlers

logger = logging.getLogger("fragment_upload_logger")
logger.setLevel(logging.DEBUG)


sm = logging.handlers.SMTPHandler(
    mailhost=("smtp.ionos.fr", 587),
    fromaddr="alexandreblanchet@upidev.fr",
    toaddrs=["alexandreblanchet@upidev.fr"],
    subject='Application Error',
    credentials=('alexandreblanchet@upidev.fr', ''),
    secure=()
)
sm.setLevel(logging.CRITICAL)

logger.addHandler(sm)

logger.warning("I am logging a warning")
logger.info("I am logging an info when the program is running by default")
logger.exception("I am logging an exception when an exception is raised")
logger.error("I am logging an error when a value is incorrect but not exception is raised")
logger.debug("I am logging a debug when debug mode is enabled")
logger.critical("I am logging a critical message")
logger.error("An other important error")
logger.error("Final important error")

Critical Logs received by email

Custom Log Formatting

We can customise how the logs are formatted.
Below the parameters allowed by the logging system.

https://docs.python.org/3/library/logging.html#logrecord-attributes

Here an example in code:

# 7

# Send logs (CRITICAL level) to your email
# You will need to authorise your gmail to send a mail from a script first
# https://support.google.com/accounts/answer/185833?visit_id=638209478478332918-4005418056&p=InvalidSecondFactor&rd=1

import logging
import logging.handlers

logger = logging.getLogger("fragment_upload_logger")
logger.setLevel(logging.DEBUG)


sm = logging.handlers.SMTPHandler(
    mailhost=("smtp.ionos.fr", 587),
    fromaddr="alexandreblanchet@upidev.fr",
    toaddrs=["alexandreblanchet@upidev.fr"],
    subject='Application Error',
    credentials=('alexandreblanchet@upidev.fr', ''),
    secure=()
)
sm.setLevel(logging.CRITICAL)

logger.addHandler(sm)

logger.warning("I am logging a warning")
logger.info("I am logging an info when the program is running by default")
logger.exception("I am logging an exception when an exception is raised")
logger.error("I am logging an error when a value is incorrect but not exception is raised")
logger.debug("I am logging a debug when debug mode is enabled")
logger.critical("I am logging a critical message")
logger.error("An other important error")
logger.error("Final important error")

Custom Log Config

Now we learnt how to handle logs in specific ways (to a file, to an API, to an email) and how to format our logs. Previously, we were using the basicConfig generated by Python. Let’s create a custom config.

# 8
import logging
import logging.config

CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "simple": {
            "format": "%(levelname)-8s - %(message)s"
        }
    },
    "filters": {
        "fruit_message": {
            "()" : "__main__.filter_fruit_message",
            "fruit": "KIWI"
        }
    },
    "handlers": {
        "stdout": {
            "class": "logging.FileHandler",
            "level": "INFO",
            "formatter": "simple",
            "filename": "filtered_logs.log",
            "filters": ["fruit_message"]
        }
    },
    "root": {
        "level": "DEBUG",
        "handlers": [
            "stdout"
        ]
    }
}

# method used to filter the logs
def filter_fruit_message(fruit):

    def filter(record):
        return fruit in record.msg

    return filter


logging.config.dictConfig(CONFIG)
logging.debug('A DEBUG message - APPLE')
logging.info('An INFO message - APPLE')
logging.warning('A WARNING message - APPLE')
logging.error('An ERROR message - KIWI')
logging.critical('A CRITICAL message - KIWI')