Skip to content

Commit

Permalink
Fix: add more details in error logging (#668)
Browse files Browse the repository at this point in the history
* feat: improve error logging

* fix: handle non-critical errors

* fix: improve error message

* feat: enhance error logging with args used
  • Loading branch information
gcharest authored Oct 3, 2024
1 parent 0dce4a0 commit 50b7c94
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 16 deletions.
39 changes: 32 additions & 7 deletions app/integrations/google_workspace/google_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,28 +84,53 @@ def handle_google_api_errors(func: Callable[..., Any]) -> Callable[..., Any]:

@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
non_critical_errors = {
"get_user": ["timed out"],
}
argument_string = ", ".join(
[str(arg) for arg in args] + [f"{k}={v}" for k, v in kwargs.items()]
)
argument_string = f"({argument_string})"

try:
result = func(*args, **kwargs)
# Check if the result is a tuple and has two elements (for backward compatibility)
if isinstance(result, tuple) and len(result) == 2:
result, unsupported_params = result
if unsupported_params:
logging.warning(
f"Unknown parameters in '{func.__name__}' were detected: {', '.join(unsupported_params)}"
f"Unknown parameters in '{func.__module__}:{func.__name__}' were detected: {', '.join(unsupported_params)}"
)
return result
except HttpError as e:
logging.error(f"An HTTP error occurred in function '{func.__name__}': {e}")
logging.error(
f"An HTTP error occurred in function '{func.__module__}:{func.__name__}': {e}"
)
except ValueError as e:
logging.error(f"A ValueError occurred in function '{func.__name__}': {e}")
logging.error(
f"A ValueError occurred in function '{func.__module__}:{func.__name__}': {e}"
)
except RefreshError as e:
logging.error(f"A RefreshError occurred in function '{func.__name__}': {e}")
logging.error(
f"A RefreshError occurred in function '{func.__module__}:{func.__name__}': {e}"
)
except Error as e:
logging.error(f"An error occurred in function '{func.__name__}': {e}")
except Exception as e: # Catch-all for any other types of exceptions
logging.error(
f"An unexpected error occurred in function '{func.__name__}': {e}"
f"An error occurred in function '{func.__module__}:{func.__name__}': {e}"
)
except Exception as e: # Catch-all for any other types of exceptions
message = str(e)
func_name = func.__name__
if func_name in non_critical_errors and any(
error in message for error in non_critical_errors[func_name]
):
logging.warning(
f"A non critical error occurred in function '{func.__module__}:{func.__name__}{argument_string}': {e}"
)
else:
logging.error(
f"An unexpected error occurred in function '{func.__module__}:{func.__name__}': {e}"
)
return None

return wrapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ def test_insert_event_api_call_error(
document_id = "test_document_id"
google_calendar.insert_event(start, end, emails, title, document_id)
assert (
"An unexpected error occurred in function 'insert_event': API call error"
"An unexpected error occurred in function 'integrations.google_workspace.google_calendar:insert_event': API call error"
in caplog.text
)
assert not mock_convert_string_to_camel_case.called
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def test_create_file_with_invalid_type_raises_value_error(

assert result is None
mocked_logging_error.assert_called_once_with(
"A ValueError occurred in function 'create_file': Invalid file_type: invalid_file_type"
"A ValueError occurred in function 'integrations.google_workspace.google_drive:create_file': Invalid file_type: invalid_file_type"
)


Expand Down
47 changes: 40 additions & 7 deletions app/tests/integrations/google_workspace/test_google_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,73 +97,105 @@ def test_handle_google_api_errors_catches_http_error(mocked_logging_error):
mock_resp.reason = "Bad Request"
mock_func = MagicMock(side_effect=HttpError(resp=mock_resp, content=b""))
mock_func.__name__ = "mock_func"
mock_func.__module__ = "mock_module"
decorated_func = handle_google_api_errors(mock_func)

result = decorated_func()

assert result is None
mocked_logging_error.assert_called_once_with(
"An HTTP error occurred in function 'mock_func': <HttpError 400 \"Bad Request\">"
"An HTTP error occurred in function 'mock_module:mock_func': <HttpError 400 \"Bad Request\">"
)


@patch("logging.error")
def test_handle_google_api_errors_catches_value_error(mocked_logging_error):
mock_func = MagicMock(side_effect=ValueError("ValueError message"))
mock_func.__name__ = "mock_func"
mock_func.__module__ = "mock_module"
decorated_func = handle_google_api_errors(mock_func)

result = decorated_func()

assert result is None
mock_func.assert_called_once()
mocked_logging_error.assert_called_once_with(
"A ValueError occurred in function 'mock_func': ValueError message"
"A ValueError occurred in function 'mock_module:mock_func': ValueError message"
)


@patch("logging.error")
def test_handle_google_api_errors_catches_error(mocked_logging_error):
mock_func = MagicMock(side_effect=Error("Error message"))
mock_func.__name__ = "mock_func"
mock_func.__module__ = "mock_module"
decorated_func = handle_google_api_errors(mock_func)

result = decorated_func()

assert result is None
mock_func.assert_called_once()
mocked_logging_error.assert_called_once_with(
"An error occurred in function 'mock_func': Error message"
"An error occurred in function 'mock_module:mock_func': Error message"
)


@patch("logging.error")
def test_handle_google_api_errors_catches_exception(mocked_logging_error):
def test_handle_google_api_errors_catches_exception(mocked_logging_error: MagicMock):
mock_func = MagicMock(side_effect=Exception("Exception message"))
mock_func.__name__ = "mock_func"
mock_func.__module__ = "mock_module"
decorated_func = handle_google_api_errors(mock_func)

result = decorated_func()

assert result is None
mock_func.assert_called_once()
mocked_logging_error.assert_called_once_with(
"An unexpected error occurred in function 'mock_func': Exception message"
"An unexpected error occurred in function 'mock_module:mock_func': Exception message"
)

mock_func = MagicMock(side_effect=Exception("timed out"))
mock_func.__name__ = "list_groups"
mock_func.__module__ = "mock_module"
decorated_func = handle_google_api_errors(mock_func)

result = decorated_func()
mock_func.assert_called_once()
mocked_logging_error.assert_called_with(
"An unexpected error occurred in function 'mock_module:list_groups': timed out"
)


@patch("logging.warning")
def test_handle_google_api_errors_catches_non_critical_error(mocked_logging_warning):
mock_func = MagicMock(side_effect=Exception("timed out"))
mock_func.__name__ = "get_user"
mock_func.__module__ = "mock_module"
decorated_func = handle_google_api_errors(mock_func)

result = decorated_func("arg1", "arg2", a="b")

assert result is None
mock_func.assert_called_once()
mocked_logging_warning.assert_called_once_with(
"A non critical error occurred in function 'mock_module:get_user(arg1, arg2, a=b)': timed out"
)


@patch("logging.error")
def test_handle_google_api_errors_catches_refresh_error(mocked_logging_error):
mock_func = MagicMock(side_effect=RefreshError("RefreshError message"))
mock_func.__name__ = "mock_func"
mock_func.__module__ = "mock_module"
decorated_func = handle_google_api_errors(mock_func)

result = decorated_func()

assert result is None
mock_func.assert_called_once()
mocked_logging_error.assert_called_once_with(
"A RefreshError occurred in function 'mock_func': RefreshError message"
"A RefreshError occurred in function 'mock_module:mock_func': RefreshError message"
)


Expand All @@ -183,14 +215,15 @@ def test_handle_google_api_errors_processes_unsupported_params(
):
mock_func = MagicMock(return_value=("test", {"unsupported"}))
mock_func.__name__ = "mock_func"
mock_func.__module__ = "mock_module"
decorated_func = handle_google_api_errors(mock_func)

result = decorated_func()

assert result == "test"
mock_func.assert_called_once()
mocked_logging_warning.assert_called_once_with(
"Unknown parameters in 'mock_func' were detected: unsupported"
"Unknown parameters in 'mock_module:mock_func' were detected: unsupported"
)


Expand Down

0 comments on commit 50b7c94

Please sign in to comment.