'Python flask - Log to 2 different destinations with different log error levels

I am having a python flask app. We have used local logger with file handler and stream handler for logging messages. Handler levels INFO

    fh.setLevel(logging.INFO)
    ch.setLevel(logging.INFO)

Then we have added one more logger for elastic search. Only error logs should be sent to this elastic search logger.

apm = ElasticAPM(app, logging=True)
handler = LoggingHandler(client=apm.client)
handler.setLevel(logging.ERROR)
app.logger.addHandler(handler)

But after inclusion of elastic search handler, INFO messages from local logger are also sent to elastic search. This fills the elastic search with unimportant logs.

How to send only error logs to elastic search while sending INFO logs to local file and local stream handlers?

Attached the python flask code with the above mentioned loggers.

import datetime
import logging

from elasticapm.contrib.flask import ElasticAPM
from elasticapm.handlers.logging import LoggingHandler
from flask import Flask
from flask import request, Blueprint, current_app

app = Flask("app_name")
LOGGING_LOCATION = "D:/logs/es"

secret_token = "abc"
elastic_search_url = "https://aaaaaaa.apm.westus.azure.elastic-cloud.com"

app.config['ELASTIC_APM'] = {
    'SERVICE_NAME': 'test_error_logs',
    'SECRET_TOKEN': secret_token,
    'SERVER_URL': elastic_search_url
}

# Creating handler for elastic search logs and adding to flask app's logger
apm = ElasticAPM(app, logging=True)
handler = LoggingHandler(client=apm.client)
handler.setLevel(logging.ERROR)
app.logger.addHandler(handler)

bp1 = Blueprint('app', __name__)


def create_logger(_app_name):
    _logging_location = LOGGING_LOCATION

    logger = logging.getLogger(_app_name)
    print("Logger: {}".format(logger))
    # If the logger has handlers, it means that logger is already created

    has_local_logger = False
    print("logger.handlers: {}".format(logger.handlers))
    if len(logger.handlers) > 0:
        for lh in logger.handlers:
            handler_type = str(type(lh))
            print(handler_type)
            if "StreamHandler" in handler_type or "FileHandler" in handler_type:
                has_local_logger = True

    if has_local_logger:
        print("Returning...")
        return logger

    _logfile = _logging_location + _app_name + '_' + "test_es.log"

    logger = logging.getLogger(_app_name)
    logger.setLevel(logging.ERROR)

    fh = logging.FileHandler(_logfile)
    fh.setLevel(logging.INFO)

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

    formatter = logging.Formatter(
        "%(asctime)s - [%(filename)s - %(funcName)10s():%(lineno)s ] - %(levelname)s - %(message)s",
        datefmt='%Y-%m-%d %H:%M:%S')
    ch.setFormatter(formatter)
    fh.setFormatter(formatter)

    logger.addHandler(ch)
    logger.addHandler(fh)
    # logger.propagate = False
    print('New logger')
    return logger


local_logger = create_logger("local_log")


@bp1.route('/test', methods=['GET'])
def url_test_api():
    print("Handlers: {} ".format(local_logger.handlers))
    print("App Handlers: {} ".format(current_app.logger.handlers))

    curr = datetime.datetime.utcnow()
    val = request.args.get('val')
    if val == "a":
        # This logger to send to local file handler and local stream handler
        local_logger.info("Test log 1: @ {} ".format(curr))
        return "Correct"

    # This logger to send to elastic search
    current_app.logger.error("Error value {} @ {}".format(val, curr), exc_info=True)
    return "Incorrect"


app.register_blueprint(bp1)

app.run(port=3002)

Code for making API calls to above server

import requests

res = requests.get("http://localhost:3002/test?val=a")
print(res.text)

Python 3.7 Flask=1.1.1 elastic-apm=5.10.0



Solution 1:[1]

import logging

from elasticapm.contrib.flask import ElasticAPM

apm = ElasticAPM(app, logging=logging.ERROR)

you will be able to control via logging=logging.ERROR

Source Documentation

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Garvit Jain