Print stacktrace with variables values on uncaught exception in Flask - python

I use next code to caught uncaught exceptions in my Flask app:
#app.errorhandler(Exception)
def uncaught_exception_handler(error):
if not app.config['DEVELOPMENT']:
app.logger.error(error, exc_info=True)
else:
raise error
return 'Internal Server Error', 500
How can I add extra information about values of variables in each stack frame to my log file? It looks like cgitb is doing desired things but it print error info to stdout or to a file and not returned it as a string. I know that it is possible to do what I want with inspect and traceback modules but as this is Python I think that there already exists appropriate solution for such problem.

It looks like you want something like this:
https://github.com/ActiveState/code/blob/3b27230f418b714bc9a0f897cb8ea189c3515e99/recipes/Python/52215_Get_more/recipe-52215.py

Related

What's the best way to display Exception in Flask?

I'm a newbie in Flask and I am trying to display the Built-In Exceptions in python but I can't seem to have them display on my end.
NOTE:
set FLASK_DEBUG = 0
CODE:
def do_something:
try:
doing_something()
except Exception as err:
return f"{err}"
Expectation:
It will display one of the built-in exceptions:
KeyError
IndexError
NameError
Etc.
Reality:
It will return the line of code that didn't worked which is more ambiguous to the end user.
Also:
I have no problem seeing the errors when the debug mode is ON but that's not something that I want to do if I open them in public
Flask supplies you with a function that enables you to register an error handler throughout your entire app; you can do something as shown below:
def handle_exceptions(e):
# Log exception in your logs
# get traceback and sys exception info and log as required
# app.logger.error(getattr(e, 'description', str(e)))
# Print traceback
# return your response using getattr(e, 'code', 500) etc.
# Exception is used to catch all exceptions
app.register_error_handler(Exception, handle_exceptions)
In my honest opinion, this is the way to go. - Following the structure found in werkzeug.exceptions.HTTPException as an example is a solid foundation.
Having a unified exception handler that will standardise your Exception handling, visualisation and logging will make your life a tad better. :)
Try with this:
def do_something:
try:
doing_something()
except Exception as err:
return f"{err.__class__.__name__}: {err}"

Python: How to write error in the console in txt file?

I have a python script which every 10 minutes sends me an email with everything written in the console. I am running this with the crontab in my ubuntu 18.04 vps.
Sometimes it doesn't send the mail so I assume that when an error happens execution stops but how can I get the errors to be written in a txt file so I can analyze the error ?
Logging Module
To demonstrate the approach with the logging module, this would be the general approach
import logging
# Create a logging instance
logger = logging.getLogger('my_application')
logger.setLevel(logging.INFO) # you can set this to be DEBUG, INFO, ERROR
# Assign a file-handler to that instance
fh = logging.FileHandler("file_dir.txt")
fh.setLevel(logging.INFO) # again, you can set this differently
# Format your logs (optional)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter) # This will set the format to the file handler
# Add the handler to your logging instance
logger.addHandler(fh)
try:
raise ValueError("Some error occurred")
except ValueError as e:
logger.exception(e) # Will send the errors to the file
And if I cat file_dir.txt
2019-03-14 14:52:50,676 - my_application - ERROR - Some error occurred
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError: Some error occurred
Print to File
As I pointed out in the comments, you could accomplish this with print as well (I'm not sure you will be applauded for it)
# Set your stdout pointer to a file handler
with open('my_errors.txt', 'a') as fh:
try:
raise ValueError("Some error occurred")
except ValueError as e:
print(e, file=fh)
cat my_errors.txt
Some error occurred
Note that logging.exception includes the traceback in this case, which is one of the many huge benefits of that module
Edit
In the interest of completeness, the traceback module leverages a similar approach as print, where you can supply a file handle:
import traceback
import sys
with open('error.txt', 'a') as fh:
try:
raise ValueError("Some error occurred")
except ValueError as e:
e_type, e_val, e_tb = sys.exc_info()
traceback.print_exception(e_type, e_val, e_tb, file=fh)
This will include all of the information you want from logging
You can use the logging module as suggested in the comments (possibly superior but outside the scope of my knowledge), or catch the errors with try and except like:
try:
pass
#run the code you currently have
except Exception as e: # catch ALLLLLL errors!!!
print(e) # or more likely you'd want something like "email_to_me(e)"
Although this is generally frowned upon to catch all exceptions, because then should your program fail for whatever reason it will get gobbled up in the except clause so a better approach is to figure out what specific error you are encountering like IndexError and then just catch this specific error like:
try:
pass
#run the code you currently have
except IndexError as e: # catch only indexing errors!!!
print(e) # or more likely you'd want something like "email_to_me(e)"
To be able to debug and not only know the kind of error that happened, you can also get the error stack using traceback module (usually in the starting package of modules):
import traceback
try:
my_function()
except Exception as e:
print(e)
traceback.print_exc()
And then run your code 'my_code.py' in console usig >>
python my_code.py >> my_prints.txt
All the prints of your code will then be written in this .txt file, including the printed error and its stack. This is very interesting in your case or while running code on a docker if you want to detach yourself from it with ctrl+p+q and still know what is printed.

Can a python program configure logging before running the rest of code?

NOTE: This question was based on an assumption that Python emits its error messages via logging. The answers show that the assumption is wrong.
I'm developing a program that is not started from a command line, but by a daemon. Stderr is redirected to null device.
Normally it logs messages to a file, but when some error is preventing a regular start, there is no error message to read, because it was sent to the null device.
To save a little debugging time in such case I tried a little "launcher" which adds a file handler to the root logger as the very first thing.
I have tested it with a deliberate syntax error in the realprog module. It logs the two "start" messages to the file, but the traceback from the syntax error is still printed to stderr. Could you please help?
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.FileHandler('test.log'))
logger.info("logging start")
def real_start():
# assume e.g. a syntax error in the realprog
import realprog
realprog.main()
if __name__ == '__main__':
logger.info("program start")
real_start()
You could use:
if __name__ == '__main__':
logger.info("program start")
try:
real_start()
except Exception:
# This will log the traceback.
logger.exception("An error ocurred.")
However, you should increase your logger level to, at least, logging.ERROR.
Hope it helps!
You are not passing your exception to your logger, so there is no way for it to write it.
inside your real_start(), put your import statement and your function call in a try, catch and then log the exception.
Lets say for example your realprog.main() divides a number by zero, I want to log the exception, so I do this.
def real_start():
try:
import realprog
realprog.main()
except ZeroDivisionError as e:
logger.info(e, exc_info=True)
If you check your file, you should have the exception inside it.

Sentry only shows <unknown>:None error

I want to detect errors in a standalone Python script with Sentry+Raven.
I tried to configure it and raven test ... is workging.
Then I place this on top of the script:
from raven import Client
client = Client('http://...#.../1')
client.captureException()
the exception is generated later on this:
import django
django.setup()
from django.conf import settings
And I want to see the actual stack for this error:
ImportError: Could not import settings 'settings' (Is it on sys.path? Is there an import error in the settings file?): No module named 'settings'
But all I see in Sentry is
which is completely useless.
How can I change this to have a normal traceback?
You misunderstand how client.captureException() works, its not a configuration parameter. You use it when you are catching an exception and it will capture the exception type and message:
try:
f = open('oogah-boogah.txt')
except IOError:
client.captureException()
# do something here
To capture any exceptions that could be generated in a block of code, you can use capture_exceptions:
#client.capture_exceptions
def load_django():
import django
django.setup()
from django.conf import settings
Yes you're right, but is there a way to catch an exception not
wrapping a block of code in a try-except. I can see the error in a
terminal, can I see it in Sentry?
There is a default exception handler - and when an exception is not caught, this default handler catches it and then displays the exception. This is what you see in the terminal.
The function that generates this output is sys.excepthook and it will output to stderr by default.
So, in order for you to catch all exception globally, you'll have to create a global exception handler or map your own function to sys.excepthook.
I would strongly recommend against this, though as you don't know what other side effects it may have.

Print Python Exception Type (Raised in Fabric)

I'm using Fabric to automate, including the task of creating a directory. Here is my fabfile.py:
#!/usr/bin/env python
from fabric.api import *
def init():
try:
local('mkdir ./www')
except ##what exception?##:
#print exception name to put in above
Run fab fabfile.py and f I already have ./www created an error is raised, but I don't know what kind, so I don't know how to handle the error yet. Fabric only prints out the following:
mkdir: cannot create directory ‘./www’: File exists
Fatal error: local() encountered an error (return code 1) while executing 'mkdir ./www'
Aborting.
What I want to do is be able to find out the error type so that I can except my errors properly without blanket statements. It would be really helpful if an answer does not just tell me how to handle a mkdir exception, but print (or otherwise find the name to) any exception I may run into down the line (mkdir is just an example).
Thank you!
The issue is that fabric uses subprocess for doing these sorts of things. If you look at the source code for local you can see it doesn't actually raise an exception. It calls suprocess.Popen and uses communicate() to read stdout and stderr. If there is a non-zero return code then it returns a call to either warn or abort. The default is abort. So, to do what you want, try this:
def init():
with settings(warn_only=True):
local('mkdir ./www')
If you look at the source for abort, it looks like this:
10 def abort(msg):
21 from fabric.state import output
22 if output.aborts:
23 sys.stderr.write("\nFatal error: %s\n" % str(msg))
24 sys.stderr.write("\nAborting.\n")
25 sys.exit(1)
So, the exception would be a SystemExit exception. While you could catch this, the proper way to do it is outlined above using settings.
It is nothing to handle with exception, it is from the fabric api
try to set the entire script's warn_only setting to be true with
env.warn_only = True
Normally, when you get an uncaught exception, Python will print the exception type along with the error message:
>>> raise IOError("Error message.")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: Error message.
If that's not happening, you're probably not getting an exception at all.
If you really want to catch an arbitrary exception and print it, you want to catch Exception or BaseException. BaseException will include even things like KeyboardInterrupt, though, so be careful with that.
def init():
try:
local('mkdir ./www')
except BaseException as e:
print "local() threw a", type(e).__name__
raise # Reraise the exception
In general:
try:
some_code()
except Exception, e:
print 'Hit An Exception', e
raise
Will tell you what the exception was but if you are not planning on actually handling some of the exceptions then simply getting rid of the try: except: lines will have exactly the same effect.
Also if you run your code under a debugger then you can look at the exception(s) that you hit in more detail.
def init():
try:
local('mkdir ./www')
except Exception as e:
print e.__class__.__name__
That's all there is to it!
edit: Just re-read your question and realized that my code would only print "Fatal" in your case. It looks like fabric is throwing an error and returning their own error code so you would have to look at the documentation. I don't have any experience with fabric so I'd suggest to look here if you haven't already. Sorry if this isn't helpful!

Categories

Resources