Skip to content

Commit 9257d4e

Browse files
authored
Feat: Improved exception logging (#351)
1 parent 54033ea commit 9257d4e

File tree

4 files changed

+62
-19
lines changed

4 files changed

+62
-19
lines changed

airbyte/_connector_base.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,8 @@ def check(self) -> None:
298298
except exc.AirbyteConnectorFailedError as ex:
299299
raise exc.AirbyteConnectorCheckFailedError(
300300
connector_name=self.name,
301-
log_text=ex.log_text,
302-
) from ex
301+
original_exception=ex,
302+
) from None
303303

304304
def install(self) -> None:
305305
"""Install the connector if it is not yet installed."""
@@ -373,11 +373,22 @@ def _execute(
373373
# This is likely a log message, so log it as INFO.
374374
self._print_info_message(line)
375375

376+
except exc.AirbyteSubprocessFailedError as ex:
377+
# Generic subprocess failure, so raise a connector error.
378+
raise exc.AirbyteConnectorFailedError(
379+
connector_name=self.name,
380+
log_text=ex.log_text,
381+
context={
382+
"exit_code": ex.exit_code,
383+
},
384+
) from None
376385
except Exception as e:
386+
# This is an unexpected error, so wrap the original exception when raising.
377387
raise exc.AirbyteConnectorFailedError(
378388
connector_name=self.name,
379389
log_text=self._last_log_messages,
380-
) from e
390+
original_exception=e,
391+
) from None
381392

382393

383394
__all__ = [

airbyte/_executors/base.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,12 @@ def _stream_from_subprocess(
8484
)
8585
input_thread.start()
8686
input_thread.join() # Ensure the input thread has finished
87-
if exception_holder.exception:
87+
88+
# Don't bother raising broken pipe errors, as they only
89+
# indicate that a subprocess has terminated early.
90+
if exception_holder.exception and not isinstance(
91+
exception_holder.exception, BrokenPipeError
92+
):
8893
raise exception_holder.exception
8994

9095
else:
@@ -133,6 +138,11 @@ def _stream_from_subprocess(
133138
raise exc.AirbyteSubprocessFailedError(
134139
run_args=args,
135140
exit_code=exit_code,
141+
original_exception=(
142+
exception_holder.exception
143+
if not isinstance(exception_holder.exception, BrokenPipeError)
144+
else None
145+
),
136146
)
137147

138148

airbyte/destinations/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ def _write_airbyte_message_stream(
295295
raise exc.AirbyteConnectorWriteError(
296296
connector_name=self.name,
297297
log_text=self._last_log_messages,
298-
) from ex
298+
original_exception=ex,
299+
) from None
299300

300301

301302
__all__ = [

airbyte/exceptions.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
DOCS_URL_BASE = "https://airbytehq.github.io/PyAirbyte"
5555
DOCS_URL = f"{DOCS_URL_BASE}/airbyte.html"
5656

57+
VERTICAL_SEPARATOR = "\n" + "-" * 60
58+
5759

5860
# Base error class
5961

@@ -68,6 +70,7 @@ class PyAirbyteError(Exception):
6870
log_file: Path | None = None
6971
context: dict[str, Any] | None = None
7072
message: str | None = None
73+
original_exception: Exception | None = None
7174

7275
def get_message(self) -> str:
7376
"""Return the best description for the exception.
@@ -83,7 +86,15 @@ def get_message(self) -> str:
8386

8487
def __str__(self) -> str:
8588
"""Return a string representation of the exception."""
86-
special_properties = ["message", "guidance", "help_url", "log_text", "context", "log_file"]
89+
special_properties = [
90+
"message",
91+
"guidance",
92+
"help_url",
93+
"log_text",
94+
"context",
95+
"log_file",
96+
"original_exception",
97+
]
8798
display_properties = {
8899
k: v
89100
for k, v in self.__dict__.items()
@@ -93,24 +104,32 @@ def __str__(self) -> str:
93104
context_str = "\n ".join(
94105
f"{str(k).replace('_', ' ').title()}: {v!r}" for k, v in display_properties.items()
95106
)
96-
exception_str = f"{self.__class__.__name__}: {self.get_message()}\n"
107+
exception_str = (
108+
f"{self.get_message()} ({self.__class__.__name__})"
109+
+ VERTICAL_SEPARATOR
110+
+ f"\n{self.__class__.__name__}: {self.get_message()}"
111+
)
112+
113+
if self.guidance:
114+
exception_str += f"\n {self.guidance}"
115+
116+
if self.help_url:
117+
exception_str += f"\n More info: {self.help_url}"
118+
97119
if context_str:
98-
exception_str += " " + context_str
120+
exception_str += "\n " + context_str
121+
122+
if self.log_file:
123+
exception_str += f"\n Log file: {self.log_file.absolute()!s}"
99124

100125
if self.log_text:
101126
if isinstance(self.log_text, list):
102127
self.log_text = "\n".join(self.log_text)
103128

104129
exception_str += f"\n Log output: \n {indent(self.log_text, ' ')}"
105130

106-
if self.log_file:
107-
exception_str += f"\n Log file: {self.log_file.absolute()!s}"
108-
109-
if self.guidance:
110-
exception_str += f"\n Suggestion: {self.guidance}"
111-
112-
if self.help_url:
113-
exception_str += f"\n More info: {self.help_url}"
131+
if self.original_exception:
132+
exception_str += VERTICAL_SEPARATOR + f"\nCaused by: {self.original_exception!s}"
114133

115134
return exception_str
116135

@@ -275,6 +294,8 @@ class AirbyteConnectorError(PyAirbyteError):
275294
def __post_init__(self) -> None:
276295
"""Set the log file path for the connector."""
277296
self.log_file = self._get_log_file()
297+
if not self.guidance and self.log_file:
298+
self.guidance = "Please review the log file for more information."
278299

279300
def _get_log_file(self) -> Path | None:
280301
"""Return the log file path for the connector."""
@@ -306,15 +327,15 @@ class AirbyteConnectorReadError(AirbyteConnectorError):
306327

307328

308329
class AirbyteConnectorWriteError(AirbyteConnectorError):
309-
"""Error when reading from the connector."""
330+
"""Error when writing to the connector."""
310331

311332

312333
class AirbyteConnectorSpecFailedError(AirbyteConnectorError):
313-
"""Error when reading from the connector."""
334+
"""Error when getting spec from the connector."""
314335

315336

316337
class AirbyteConnectorDiscoverFailedError(AirbyteConnectorError):
317-
"""Error when reading from the connector."""
338+
"""Error when running discovery on the connector."""
318339

319340

320341
class AirbyteNoDataFromConnectorError(AirbyteConnectorError):

0 commit comments

Comments
 (0)