I am attempting to get an email sent to me any time an error occurs in my Flask application. The email is not being sent despite the handler being registered. I used smtplib to verify that my SMTP login details are correct. The error is displayed in Werkzeug's debugger, but no emails are sent. How do I log exceptions that occur in my app?
import logging
from logging.handlers import SMTPHandler
from flask import Flask
app = Flask(__name__)
app.debug = True
app.config['PROPAGATE_EXCEPTIONS'] = True
if app.debug:
logging.basicConfig(level=logging.INFO)
# all of the $ names have actual values
handler = SMTPHandler(
mailhost = 'smtp.mailgun.org',
fromaddr = 'Application Bug Reporter <$mailgun_email_here>',
toaddrs = ['$personal_email_here'],
subject = 'Test Application Logging Email',
credentials = ('$mailgun_email_here', '$mailgun_password_here')
)
handler.setLevel(logging.ERROR)
app.logger.addHandler(handler)
#app.route('/')
def index():
raise Exception('Hello, World!') # should trigger an email
app.run()
The issue was which logger the handler was added to. Flask uses the werkzeug logger to log exceptions during view functions, not the base app.logger. I had to register my handler with the werkzeug logger:
logging.getLogger('werkzeug').addHandler(handler)
In addition, I had to include the port in mailhost:
handler = SMTPHandler(
mailhost=('smtp.mailgun.org', 587),
fromaddr='Application Bug Reporter <$mailgun_email_here>',
toaddrs=['$personal_email_here'],
subject='Test Application Logging Email',
credentials=('$mailgun_email_here', '$mailgun_password_here')
)
Related
I have a web app running on Cloudlinux with Litespeed server.
Logging was working fine, but recently started to mess the log files and is loosing most of logged messages when the log file is rotated at midnight. The information I got is that it is a multiprocessing related issue.
Here is my main app (app.py):
from log_module import logger
from flask import Flask
app = Flask(__name__)
#app.route('/')
def index():
logger.debug ('User arrived at index page')
logger.critical ('Wow, we have a visitor!')
return 'Index page'
if __name__ == '__main__':
app.run()
And the logging code is in a separated module, logs DEBUG and WARNING in separated files, and the log file is rotated at midnight UTC time, creating one single file per day for DEBUG and another for WARNING (log_module.py):
import logging
import logging.handlers as handlers
import time
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
logging.Formatter.converter = time.gmtime
formatter = logging.Formatter('[%(asctime)s] %(name)-30s %(levelname)-8s %(message)s [%(funcName)s:%(lineno)d]')
logHandler = handlers.TimedRotatingFileHandler('logs/UTC_time__DEBUG.log',
encoding='utf-8',
when='midnight',
interval=1,
backupCount=0,
utc=True)
logHandler.setLevel(logging.DEBUG)
logHandler.setFormatter(formatter)
logHandler.suffix = "%Y-%m-%d__%H_%M_%S.log"
logHandler.encoding = 'utf-8'
errorLogHandler = handlers.TimedRotatingFileHandler('logs/UTC_time__WARNING.log',
encoding='utf-8',
when='midnight',
interval=1,
backupCount=0,
utc=True)
errorLogHandler.setLevel(logging.WARNING)
errorLogHandler.setFormatter(formatter)
errorLogHandler.suffix = "%Y-%m-%d__%H_%M_%S.log"
errorLogHandler.encoding = 'utf-8'
logger.addHandler(logHandler)
logger.addHandler(errorLogHandler)
How to adapt my code to make it work properly on multiprocessing environment, that is, at the server with multiple WSGI "workers"?
I had tried the code below for getting the exception in https://learn.microsoft.com/en-us/azure/azure-monitor/app/opencensus-python
from opencensus.ext.azure.log_exporter import AzureLogHandler
logger = logging.getLogger(__name__)
# TODO: replace the all-zero GUID with your instrumentation key.
logger.addHandler(AzureLogHandler(
connection_string='InstrumentationKey=00000000-0000-0000-0000-000000000000')
)
properties = {'custom_dimensions': {'key_1': 'value_1', 'key_2': 'value_2'}}
# Use properties in exception logs
try:
result = 1 / 0 # generate a ZeroDivisionError
except Exception:
logger.exception('Captured an exception.', extra=properties)
It is working. I can catch the exception.
However, I want to ask if there is an easy way to catch the exception automatically in the python flask?
Since I try the below code, it just gives me a request record, not the exception.
app = Flask(__name__)
app.logger.addHandler(file_handler)
handler = AzureEventHandler(
connection_string="InstrumentationKey={}".format(app.config['APPINSIGHTS_INSTRUMENTATIONKEY']))
handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]'))
handler.setLevel(logging.ERROR)
app.logger.addHandler(handler)
Thank you for helping
Your code should like below. For more details, you can check offical sample code.
Flask "To-Do" Sample Application
import logging
import sys
from flask import Flask
sys.path.append('..')
from config import Config
from flask_sqlalchemy import SQLAlchemy
from opencensus.ext.azure import metrics_exporter
from opencensus.ext.azure.log_exporter import AzureLogHandler
from opencensus.ext.flask.flask_middleware import FlaskMiddleware
from opencensus.trace import config_integration
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
# Import here to avoid circular imports
from app import routes # noqa isort:skip
# Trace integrations for sqlalchemy library
config_integration.trace_integrations(['sqlalchemy'])
# Trace integrations for requests library
config_integration.trace_integrations(['requests'])
# FlaskMiddleware will track requests for the Flask application and send
# request/dependency telemetry to Azure Monitor
middleware = FlaskMiddleware(app)
# Processor function for changing the role name of the app
def callback_function(envelope):
envelope.tags['ai.cloud.role'] = "To-Do App"
return True
# Adds the telemetry processor to the trace exporter
middleware.exporter.add_telemetry_processor(callback_function)
# Exporter for metrics, will send metrics data
exporter = metrics_exporter.new_metrics_exporter(
enable_standard_metrics=False,
connection_string='InstrumentationKey=' + Config.INSTRUMENTATION_KEY)
# Exporter for logs, will send logging data
logger.addHandler(
AzureLogHandler(
connection_string='InstrumentationKey=' + Config.INSTRUMENTATION_KEY
)
)
if __name__ == '__main__':
app.run(host='localhost', port=5000, threaded=True, debug=True)
When sending any message via Flask-Mail, such as below, the last line with mail.send(msg) will also result in the mail headers and content being logged.
Message("Hello", sender="from#example.com", recipients=["to#example.com"])
msg.body = 'anything'
mail.send(msg)
As my mail may contain sensitive information, I want to disable this logging entirely. Yet, playing around with the logging module, I could not find a logger configured for Flask-Mail.
How do I disable logging in Flask-Mail?
Figured this one out:
Flask-Mail uses Python's smtplib to send mail. smtplib does not use the logging module, but it prints information for debugging to stderr.
smtplib includes following method:
def set_debuglevel(self, debuglevel):
"""Set the debug output level.
A non-false value results in debug messages for connection and for all
messages sent to and received from the server.
"""
self.debuglevel = debuglevel
If we use Flask-Mail, we can set this variable when we initialize our app like this:
app = Flask(__name__)
mail = Mail(app)
app.extensions['mail'].debug = 0
Any output is now suppressed.
I'm using SMTPHandler to log my caught exceptions in my python script which requests new data to my backend.
Here's how I initialized my SMTPHandler:
# write errors to email
error_mail_subject = "ERROR: Script error in %s" % sys.argv[0]
error_mail_handler = logging.handlers.SMTPHandler(mailhost=("smtp.gmail.com", 587),
fromaddr="test#gmail.com",
toaddrs="test#gmail.com",
subject=error_mail_subject)
error_mail_handler.setLevel(logging.ERROR)
error_mail_handler.setFormatter(debug_format)
# Build the logger
logger = logging.getLogger()
logger.addHandler(error_mail_handler)
But unfortunately, I get this following error during handling of a script exception
smtplib.SMTPSenderRefused: (530, b'5.7.0 Must issue a STARTTLS command first. w15sm3562054qta.16 - gsmtp', 'test#gmail.com')
When the script calls:
logger.exception(e)
This successfully sent an email from my gmail, tested just now:
import logging.handlers
error_mail_subject = "hello, it's me"
error_mail_handler = logging.handlers.SMTPHandler(mailhost=("smtp.gmail.com", 587),
fromaddr="email",
toaddrs="email",
subject=error_mail_subject,
credentials=('email', 'password'),
secure=())
error_mail_handler.setLevel(logging.ERROR)
logger = logging.getLogger()
logger.addHandler(error_mail_handler)
logger.exception(Exception("hey"))
I also had to sign into gmail and then go here to allow access to less secure apps: https://www.google.com/settings/security/lesssecureapps
I am trying to tie Flask's logger to a FileHandler, so I can save custom log messages to a file. Anytime I try to log a message when a POST requests hits foo, nothing happens, at all. How can I work around this?
import logging
from logging import FileHandler
from time import strftime
from flask import Flask, Response
app = Flask(__name__)
#app.route('/foo', methods=['POST'])
def bar():
app.logger.info('post request')
...
return Response(), 200
if __name__ == "__main__":
file_date = strftime('%d_%m_%Y')
handler = FileHandler(f'./log/{file_date}.log')
handler.setLevel(logging.INFO)
app.logger.setLevel(logging.INFO)
app.logger.addHandler(handler)
app.run()