Could you add an anvil.server.on_error = def…? It would run everytime a server.callable raises an Exception. Ideally we could use the traceback module in this on_error function. There are times we want to return more detailed error messages for multiple callables or introduce more robust logging than what App Logs can handle.
Are you talking about managing on the client side an error risen on the server?
Reading your request it looks like you are thinking javascript instead of Python.
In Python you manage errors with try: ... except:
blocks, where you can distinguish among different type of exceptions, even custom exceptions. The problem is that a custom exception risen on the server side doesn’t easily trickle to the client side. Perhaps this can help you with that.
No i’m talking about logging serverside errors on the server without the need to add logic to everywhere an exception might be raised
I wrap most uplink script with this error management block, so I receive an email when there is a problem. The one second pause is there to allow the connection to be reestablished in case the exception was caused by a connection problem, otherwise sending the email would fail too.
A nicer job could be achieved by creating a decorator, so all you need is to decorate the functions that you want to log. More decorators (or a decorator with arguments) would allow you to manage the exception of different functions in different ways, for example one function could send you an email and one could just log the error.
try:
[...]
except Exception as e:
while True:
try:
if type(e) == 'anvil._server.AnvilWrappedError':
send_error_email(f'<AppName> error:<br>\n{repr(e)}<br>\n{e.error_obj}')
else:
send_error_email(f'<AppName> error:<br>\n{traceback.format_exc()}')
break
except:
time.sleep(1)
As an example, here’s a decorator that catches all Exception
s and prints their string representation:
import anvil.server
def log_exception_and_carry_on(func):
def wrapped(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception as exp:
print(str(exp))
# Preserve the name of the original function,
# so it gets registered correctly.
wrapped.__name__ = func.__name__
return wrapped
@anvil.server.callable
@log_exception_and_carry_on
def my_server_function(arg1, arg2):
print("I'm running with " + arg1 + arg2)
raise ValueError('This is an exception')
print("Should not get here")
There’s one trick here - we need to preserve the original function’s __name__
so that it can still be called by that name from the client side.
wrapped.__name__ = func.__name__
An alternative to this would be to manually set the name of each callable funciton, but that’s a little too ‘manual’ for my taste:
@anvil.server.callable('my_server_function')
@log_exception_and_carry_on
def my_server_function(arg1, arg2):
print("I'm running with " + arg1 + arg2)
raise ValueError('This is an exception')
print("Should not get here")
Here’s a clone link:
https://anvil.works/build#clone:57GUBOIRXX3LFJ54=YIKZTLAILDBTYTGME67P64JB
(I should also mention that if you want to want to change the default error handling for the entire app, you can do that using set_default_error_handling
. See the ref docs for more detail.)
Sweet. This is what I needed thanks Shaun. I was missing the wrapped.__name__ = func.__name__
in my implementation.
Linked topic - server-side decorators
post 8
post 13
Shoddy Decorators (or - Pain, Tears & Decorators) - #13 by stucork