Skip to content

Commit

Permalink
Add logging of uncaught exceptions to Logger class
Browse files Browse the repository at this point in the history
- Modified Logger to capture uncaught exceptions and log traceback
  using `sys.excepthook`.
- Added `log_exception` method to handle uncaught exceptions.
- Updated test suite to verify exception logging using subprocess.
  • Loading branch information
ioannis-vm committed Oct 28, 2024
1 parent d36706a commit 2e8da31
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 0 deletions.
38 changes: 38 additions & 0 deletions pelicun/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import json
import pprint
import sys
import traceback
import warnings
from pathlib import Path
from typing import TYPE_CHECKING, Any, Optional, TypeVar, overload
Expand All @@ -61,6 +62,7 @@

if TYPE_CHECKING:
from collections.abc import Callable
from types import TracebackType

from pelicun.assessment import AssessmentBase

Expand Down Expand Up @@ -328,6 +330,10 @@ def __init__(
self.reset_log_strings()
control_warnings()

# Set sys.excepthook to handle uncaught exceptions
# https://docs.python.org/3/library/sys.html#sys.excepthook
sys.excepthook = self.log_exception

def reset_log_strings(self) -> None:
"""Populate the string-related attributes of the logger."""
if self.log_show_ms:
Expand Down Expand Up @@ -452,6 +458,38 @@ def print_system_info(self) -> None:
prepend_timestamp=False,
)

def log_exception(
self,
exc_type: type[BaseException],
exc_value: BaseException,
exc_traceback: TracebackType | None,
) -> None:
"""
Log uncaught exceptions and their traceback.
Parameters
----------
exc_type : Type[BaseException]
The exception class.
exc_value : BaseException
The exception instance.
exc_traceback : Optional[TracebackType]
The traceback object representing the call stack at the point
where the exception occurred.
"""
message = (
f"Unhandled exception occurred:\n"
f"{''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))}"
)

if self.log_file is not None:
with Path(self.log_file).open('a', encoding='utf-8') as f:
f.write(message)

if self.print_log:
print(message, file=sys.stderr) # noqa: T201


# get the absolute path of the pelicun directory
pelicun_path = Path(__file__).resolve().parent
Expand Down
42 changes: 42 additions & 0 deletions pelicun/tests/basic/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import argparse
import io
import re
import subprocess # noqa: S404
import tempfile
from contextlib import redirect_stdout
from pathlib import Path
Expand Down Expand Up @@ -208,6 +209,47 @@ def test_logger_div() -> None:
assert f.read()


def test_logger_exception() -> None:
# Create a temporary directory for log files
temp_dir = tempfile.mkdtemp()

# Create a sample Python script that will raise an exception
test_script = Path(temp_dir) / 'test_script.py'
test_script_content = f"""
import sys
import traceback
from pelicun.base import Logger
log_file = "{temp_dir}/log.txt"
log = Logger(log_file=log_file, verbose=True, log_show_ms=True, print_log=True)
raise ValueError("Test exception in subprocess")
"""

# Write the test script to the file
test_script.write_text(test_script_content)

# Use subprocess to run the script
process = subprocess.run( # noqa: S603
['python', str(test_script)], capture_output=True, text=True, check=False # noqa: S607
)

# Check that the process exited with an error
assert process.returncode == 1

# Check the stdout/stderr for the expected output
assert 'Test exception in subprocess' in process.stderr

# Check that the exception was logged in the log file
log_file = Path(temp_dir) / 'log.txt'
assert log_file.exists(), 'Log file was not created'
log_content = log_file.read_text()
assert 'Test exception in subprocess' in log_content
assert 'Traceback' in log_content
assert 'ValueError' in log_content


def test_split_file_name() -> None:
file_path = 'example.file.name.txt'
name, extension = base.split_file_name(file_path)
Expand Down

0 comments on commit 2e8da31

Please sign in to comment.