I am not using the built-in flask logging mechanism. Instead of that, I have created the following which was getting created from the main in flask app.
def init_logger(app):
log_file = LOG_FILE
if app.testing:
log_file = TEST_LOG_FILE
create_logging_folder(app)
logger = logging.getLogger("main")
logger.setLevel(logging.INFO)
rotating_file_handler = RotatingFileHandler(
filename=log_file,
maxBytes=10240,
backupCount=10
)
rotating_file_handler.setLevel(logging.INFO)
rotating_file_handler.setFormatter(
logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
)
)
rotating_file_handler.setLevel(logging.INFO)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
logger.addHandler(rotating_file_handler)
logger.addHandler(stream_handler)
logger.info("Test")
logger.info(id(logger))
def create_logging_folder(app):
log_dir = LOG_DIR
if app.testing:
log_dir = TEST_LOG_DIR
if not os.path.exists(log_dir):
os.mkdir(log_dir)
return True
info and the logging id is getting created in the log file, but if I call the logger from another module I found that the handlers were empty and logs are not getting created.
logger = logging.getLogger(__name__)
print(logger.handlers) # []
Entry point as follows
def run():
app = main.create_app()
init_logger(app)
logger = logging.getLogger(__name__)
print(logger.handlers) # stil empty
app.run(
debug=os.environ.get("APP_DEBUG", True),
host=os.environ.get("APP_HOST", "0.0.0.0"),
port=os.environ.get("APP_PORT", 5000),
)
What could possibly go wrong here? How can I make this work ?
Related
logging.py
import logging
def log():
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s:%(name)s:%(message)s')
file_handler = logging.FileHandler('sample.log')
file_handler.setLevel(logging.ERROR)
file_handler.setFormatter(formatter)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
return logger
I have this above piece of code. i want to call this function in every file in the project for logs and to print on command line by streamhandler and store log it in the same sample.log file across a project.
I have a logger function from logging package that after I call it, I can send the message through logging level.
I would like to send this message also to another function, which is a Telegram function called SendTelegramMsg().
How can I get the message after I call the funcion setup_logger send a message through logger.info("Start") for example, and then send this exatcly same message to SendTelegramMsg() function which is inside setup_logger function?
My currently setup_logger function:
# Define the logging level and the file name
def setup_logger(telegram_integration=False):
"""To setup as many loggers as you want"""
filename = os.path.join(os.path.sep, pathlib.Path(__file__).parent.resolve(), 'logs', str(dt.date.today()) + '.log')
formatter = logging.Formatter('%(levelname)s: %(asctime)s: %(message)s', datefmt='%m/%d/%Y %H:%M:%S')
level = logging.DEBUG
handler = logging.FileHandler(filename, 'a')
handler.setFormatter(formatter)
consolehandler = logging.StreamHandler()
consolehandler.setFormatter(formatter)
logger = logging.getLogger('logs')
if logger.hasHandlers():
# Logger is already configured, remove all handlers
logger.handlers = []
else:
logger.setLevel(level)
logger.addHandler(handler)
logger.addHandler(consolehandler)
#if telegram_integration == True:
#SendTelegramMsg(message goes here)
return logger
After I call the function setup_logger():
logger = setup_logger()
logger.info("Start")
The output:
INFO: 01/06/2022 11:07:12: Start
How am I able to get this message and send to SendTelegramMsg() if I enable the integration to True?
Implement a custom logging.Handler:
class TelegramHandler(logging.Handler):
def emit(self, record):
message = self.format(record)
SendTelegramMsg(message)
# SendTelegramMsg(message, record.levelno) # Passing level
# SendTelegramMsg(message, record.levelname) # Passing level name
Add the handler:
def setup_logger(telegram_integration=False):
# ...
if telegram_integration:
telegram_handler = TelegramHandler()
logger.addHandler(telegram_handler)
return logger
Usage, no change:
logger = setup_logger()
logger.info("Start")
Picking up the idea suggested by #gold_cy: You implement a custom logging.Handler. Some hints for that:
for the handler to be able to send message via a bot, you may want to pass the bot to the handlers __init__ so that you have it available later
emit must be implemented by you. Here you'll want to call format which gives you a formatted version of the log record. You can then use that message to send it via the bot
Maybe having a look at the implementation of StreamHandler and FileHandler is helpful as well
#Defining a global flag
tlInt=False
# Define the logging level and the file name
def setup_logger(telegram_integration=False):
"""To setup as many loggers as you want"""
filename = os.path.join(os.path.sep, pathlib.Path(__file__).parent.resolve(), 'logs', str(dt.date.today()) + '.log')
formatter = logging.Formatter('%(levelname)s: %(asctime)s: %(message)s', datefmt='%m/%d/%Y %H:%M:%S')
level = logging.DEBUG
handler = logging.FileHandler(filename, 'a')
handler.setFormatter(formatter)
consolehandler = logging.StreamHandler()
consolehandler.setFormatter(formatter)
logger = logging.getLogger('logs')
if logger.hasHandlers():
# Logger is already configured, remove all handlers
logger.handlers = []
else:
logger.setLevel(level)
logger.addHandler(handler)
logger.addHandler(consolehandler)
if telegram_integration == True:
global tlInt
tlInt=True
return logger
#Logic : If telegram integration is true, it will call SendTelegramMsg to send the message to the app based on the level; and if it is false, it will save the message in local file based on the level
def GenerateLog(logger,levelFlag,data):
global tlInt
if tlInt == True:
SendTelegramMsg(levelFlag,data)
else:
if levelFlag == "warning":
logger.warning(data)
elif levelFlag == "error":
logger.error(data)
elif levelFlag == "debug":
logger.debug(data)
elif levelFlag == "critical":
logger.critical(data)
else:
logger.info(data)
#You can used the same logic in SendTelegramMsg which used in GenerateLog for deciding the level
def SendTelegramMsg(levelFlag,data):
#your code goes here
logger=setup_logger()
GenerateLog(logger,'warning','Start')
I am trying to log all exceptions within my application. I want to have different handlers, one handler to log all information to a file and only critical problems to my email.
I created a file_handler, which should log everything and email_handler for the critical problems. The handlers work, apart from catching unexpected errors. I tried to log everything with the root logger, which has the same configuration as the file_handler, which works. Why doesn't my file_handler catch unexpected errors?
Here is my code:
# root logger
logging.basicConfig(filename='flask.log', level=logging.WARNING)
# own logger
logger = logging.getLogger('flask')
file_handler = logging.handlers.TimedRotatingFileHandler('flask.log', when='D', interval=1)
email_handler = SMTPHandler(...)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger.setLevel(logging.DEBUG)
file_handler.setLevel(logging.DEBUG)
email_handler.setLevel(logging.WARNING)
file_handler.setFormatter(formatter)
email_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(email_handler)
If I have an error in a function (example Class A doesn't has attribute X), the unexpected problem only gets logged by the root logger.
Workaround:
If I don't give "my" logger a name, I get the root logger. My handlers then take his information and distribute them. It is not ideal, because it should work with "my" logger as well.
Workaround code:
# own logger
logger = logging.getLogger() # no name -> logger = root logger
file_handler = logging.handlers.TimedRotatingFileHandler('flask.log', when='D', interval=1)
email_handler = SMTPHandler(...)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger.setLevel(logging.DEBUG)
file_handler.setLevel(logging.DEBUG)
email_handler.setLevel(logging.WARNING)
file_handler.setFormatter(formatter)
email_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(email_handler)
I have a python program that utilizes multiprocessing to increase efficiency, and a function that creates a logger for each process. The logger function looks like this:
import logging
import os
def create_logger(app_name):
"""Create a logging interface"""
# create a logger
if logging in os.environ:
logging_string = os.environ["logging"]
if logging_string == "DEBUG":
logging_level = loggin.DEBUG
else if logging_string == "INFO":
logging_level = logging.INFO
else if logging_string == "WARNING":
logging_level = logging.WARNING
else if logging_string == "ERROR":
logging_level = logging.ERROR
else if logging_string == "CRITICAL":
logging_level = logging.CRITICAL
else:
logging_level = logging.INFO
logger = logging.getLogger(app_name)
logger.setLevel(logging_level)
# Console handler for error output
console_handler = logging.StreamHandler()
console_handler.setLevel(logging_level)
# Formatter to make everything look nice
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
# Add the handlers to the logger
logger.addHandler(console_handler)
return logger
And my processing functions look like this:
import custom_logging
def do_capture(data_dict_access):
"""Process data"""
# Custom logging
LOGGER = custom_logging.create_logger("processor")
LOGGER.debug("Doing stuff...")
However, no matter what the logging environment variable is set to, I still receive debug log messages in the console. Why is my logging level not taking effect, surely the calls to setLevel() should stop the debug messages from being logged?
Here is an easy way to create a logger object:
import logging
import os
def create_logger(app_name):
"""Create a logging interface"""
logging_level = os.getenv('logging', logging.INFO)
logging.basicConfig(
level=logging_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(app_name)
return logger
Discussion
There is no need to convert from "DEBUG" to logging.DEBUG, the logging module understands these strings.
Use basicConfig to ease the pain of setting up a logger. You don't need to create handler, set format, set level, ... This should work for most cases.
Update
I found out why your code does not work, besides the else if. Consider your line:
if logging in os.environ:
On this line loggging without quote refers to the logging library package. What you want is:
if 'logging' in os.environ:
I encountered such a problem and couldn't solve it. I used python's logger to log info, logger level set to logging.DEBUG. I used gunicorn to log
info at the same time. Normally, the error message goes to python's logger, and the link messages and other messages written by logger.info or logger.debug goes to the log file of gunicorn. However with one application it doesn't behave so. The messages output by logger.info also goes to python's logger. The problem is, I only want to see error messages in python's logger, all the other messages would be seen from gunicorn's logger. Can anyone give me a clue where I might do wrong in this situation?
thx in advance,
alex
The following is my config:
LOGGER_LEVEL = logging.DEBUG
LOGGER_ROOT_NAME = "root"
LOGGER_ROOT_HANLDERS = [logging.StreamHandler, logging.FileHandler]
LOGGER_ROOT_LEVEL = LOGGER_LEVEL
LOGGER_ROOT_FORMAT = "[%(asctime)s %(levelname)s %(name)s %(funcName)s:%(lineno)d] %(message)s"
LOGGER_LEVEL = logging.ERROR
LOGGER_FILE_PATH = "/data/log/web/"
Code:
def config_root_logger(self):
formatter = logging.Formatter(self.config.LOGGER_ROOT_FORMAT)
logger = logging.getLogger()
logger.setLevel(self.config.LOGGER_ROOT_LEVEL)
filename = os.path.join(self.config.LOGGER_FILE_PATH, "secondordersrv.log")
handler = logging.FileHandler(filename)
handler.setFormatter(formatter)
logger.addHandler(handler)
# 测试环境配置再增加console的日志记录
self._add_test_handler(logger, formatter)
def _add_test_handler(self, logger, formatter):
# 测试环境配置再增加console的日志记录
if self.config.RUN_MODE == 'test':
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
My gunicorn config looks like this:
errorlog = '/data/log/web/%s.log' % APP_NAME
loglevel = 'info'
accesslog = '-'
You did not set the level of your handler.
After handler.setFormatter(formatter), add the following line:
handler.setLevel(self.config.LOGGER_LEVEL)