-
Notifications
You must be signed in to change notification settings - Fork 406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: Automatically log exceptions using sys.eventhook #1636
Comments
hey @lorengordon, great to hear from you again! I have one question
Are you looking to use this feature in a non-Lambda function (e.g., CLI) ? or are you also wanting to log all exceptions ever raised in a call stack within Lambda? Python supports Exception Chaining for this particular use case. This allows you have to a complete stack trace and a single exception logged ( From Lambda Powertools, logging all exceptions via the If you find this would still be best handled by Powertools, I can label this feature request to hear from more customers as we sort by Let me know if that makes sense and if there's any area I can dig deeper! Thank you for raising this feature request! |
Hmm, maybe we're misunderstanding each other. The With this code, the exception will be captured twice to cloudwatch logs. Once because the logger wrote it (because we want structured logs), and once because Lambda captures the stderr from the from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.typing import LambdaContext
logger = Logger()
@logger.inject_lambda_context
def handler(event: dict, context: LambdaContext) -> str:
try:
raise "Testing exceptions"
except Exception:
logger.exception("Caught an error")
raise Using a custom from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.typing import LambdaContext
logger = Logger()
def exception_hook(exc_type, exc_value, exc_traceback):
"""Log all exceptions with hook for sys.excepthook."""
logger.exception(
"%s: %s",
exc_type.__name__,
exc_value,
exc_info=(exc_type, exc_value, exc_traceback),
)
@logger.inject_lambda_context
def handler(event: dict, context: LambdaContext) -> str:
raise "Testing exceptions"
sys.excepthook = exception_hook I guess more accurately, I am asking for a way to simplify that boilerplate. We want to capture a terminating exception as a structured log. But adding the So I thought, maybe there's a way to do this in powertools? Something like this? from aws_lambda_powertools import Logger, exception_hook
from aws_lambda_powertools.utilities.typing import LambdaContext
logger = Logger()
@logger.inject_lambda_context(excepthook=exception_hook)
def handler(event: dict, context: LambdaContext) -> str:
raise "Testing exceptions" |
AH! Completely my bad @lorengordon!! Definitely see value in this as it's uncaught exception. Two final questions:
For now, I can see this being a boolean, unless we want to guard against overwrites or make it extensible for 3P we might be unaware. This being an opt-in feature makes me more comfortable either way. |
Definitely opt-in. And yes, there is a single exception hook for the program. I have never seen a 3P library try to set it, but I guess anything is possible. I like the idea of a
|
Yes, we can figure out naming in a PR. Perhaps This makes it easier for other Powertools languages to reword I'm working on a few other issues now, but if you have the bandwidth to send a PR, we can include it in the next release!! |
another useful trick for this feature: Flush buffered debug logs on uncaught exceptions too. I think I might have bandwidth to have a go at this in 10 days or so. |
I agree on this feature, it can be very useful. Just a question: My 2 cents: thank you! |
Great to hear @davideicardi ! As to your question, this is a hook for any exception that was either raised intentionally OR unforeseen. For the For the flexibility, we could still have a method where you provide your own function to handle any uncaught exception, like For example: Raised intentionally from aws_lambda_powertools import Logger
import sys
logger = Logger(service="sample")
def exception_hook(exc_type, exc_value, exc_traceback):
logger.exception("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = exception_hook
def main():
raise ValueError("Oops!") # this will crash the program thus calling `sys.excepthook`.
main() Unexpected/unforeseen exception from aws_lambda_powertools import Logger
import sys
logger = Logger(service="sample")
def exception_hook(exc_type, exc_value, exc_traceback):
logger.exception("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = exception_hook
def main():
a = {"key": "value"}
a["non_existent_key"] # KeyError will crash the program thus calling `sys.excepthook`.
main() |
hmmm turns out it's "impossible" to test this thing. Using it locally works just fine, so I'll ignore test coverage for now, schedule it for this week's release. I would love to hear from you @lorengordon or @davideicardi if you have ideas on how to test this properly, as I ran out of immediate options. Working example from aws_lambda_powertools import Logger
logger = Logger(service="sample", log_uncaught_exceptions=True)
raise ValueError("Catch me if you can!") Results {
"level": "ERROR",
"location": "log_uncaught_exception_hook:757",
"message": "Unhandled exception",
"timestamp": "2022-11-16 11:58:39,819+0100",
"service": "sample",
"exception": "Traceback (most recent call last):\n File \"/Users/lessa/DEV/aws-lambda-powertools-python/blah.py\", line 38, in <module>\n raise ValueError(\"Catch me if you can!\")\nValueError: Catch me if you can!",
"exception_name": "ValueError"
} Testing attempts only to confirm exception hook is never triggered because they are caught so to not fail the test abruptly:
Test attempt with redirect def test_logger_log_uncaught_exceptions(service_name, stdout):
# GIVEN Logger is initialized
Logger(service=service_name, stream=stdout, log_uncaught_exceptions=True)
from subprocess import Popen, PIPE
proc = Popen([sys.executable, "non_existed_file.py"], stderr=PIPE)
_, stderr = proc.communicate()
# THEN the decorator should included them
log = capture_logging_output(stdout)
assert "Unhandled exception" in log["message"] |
|
This should be available by EOW. I spent some time explaining what |
I imagine you could use |
This is now released under 2.3.0 version! |
Use case
I find myself often writing a generic try/except statement in my handler to always log all exceptions. I'm not sure if this is a good pattern, maybe it already does this, so I might just be looking for guidance on current practices. (I didn't see anything in the docs or other issues on this.)
Solution/User Experience
What I've done in CLI utilities to capture those exceptions to the logger, is to override the
sys.eventhook
. This way, every exception is logged automatically. Would it be useful to do something similar for lambda functions?Alternative solutions
No response
Acknowledgment
The text was updated successfully, but these errors were encountered: