I am writing some code that I would like to add prometheus metrics. In the metrics we want to measure the time it took, but also in case a exception was raised.
I ended up with this code, but it feels as if it can be written a bit better, but i dont know how to achieve it:
def async_load_one(self, result, **fields):
start = time()
end = 0
status = ""
try:
result.set(self.table.query(**fields))
end = time()
status = "ok"
except Exception as e:
result.set_exception(e)
end = time()
status = "error"
finally:
elapsed = end - start
DYNAMO_ACCESS_DURATION.labels(
operation="load_one",
status=status
).observe(elapsed)
The finally
clause runs whether or not the try
statement produces an exception. Meaning it will execute in any event.
So you can do:
def func():
start = time()
try:
print("do something")
status = "ok"
except Exception as e:
print("an error has occurred", e)
status = "error"
finally:
end = time()
elapsed = end - start
print(f"{elapsed=}")
print(f"{status=}")
Which prints something like this when no errors are raised:
do something
elapsed=1.3828277587890625e-05
status='ok'
Or this if an error were to be raised in the try
block:
do something
an error has occurred
elapsed=2.09808349609375e-05
status='error'
If you need to do this in more than one method/function, you can consider using a decorator:
def timing_decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
try:
result = func(*args, **kwargs)
status = "ok"
except Exception as e:
print("an error has occurred", e)
status = "error"
result = None
finally:
end = time.time()
elapsed = end - start
print(f"{elapsed=}")
print(f"{status=}")
return result
return wrapper
@timing_decorator
def func():
print("do something")