Skip to content

feat: support logging of exceptions#277

Draft
IzaakGough wants to merge 3 commits into
mainfrom
@invertase/feat-support-exception-logging
Draft

feat: support logging of exceptions#277
IzaakGough wants to merge 3 commits into
mainfrom
@invertase/feat-support-exception-logging

Conversation

@IzaakGough
Copy link
Copy Markdown

@IzaakGough IzaakGough commented Jun 1, 2026

Summary

Add exception-aware logging support so firebase_functions.logger can accept exception objects without failing JSON serialization, and include stack trace information in logged output.

Problem/Root Cause

Issue #172 reports that the current logging API does not handle exceptions properly. Passing an exception to logger.error(..., error=e) can raise a JSON serialization error instead of writing a log entry, and the API does not provide a direct way to log full exception stack traces.

The root cause is that exception objects were being passed through the existing log serialization path without being converted into a JSON-safe structure. That path handled common container types and circular references, but it did not recognize BaseException values or extract structured exception details such as type, message, arguments, and traceback information.

Solution/Changes

Add exception-specific serialization in the logger so BaseException values are converted into a JSON-safe dictionary containing the exception type, message, arguments, and, when available, a formatted stack trace.

Update circular-reference handling so exceptions and exception payloads can be serialized safely even when they contain self-referential data. The PR also adds a new logger.exception(...) helper that logs at error severity and attaches the active exception stack trace directly to the top-level log entry.

Testing

  • Added automated tests in tests/test_logger.py covering logger.error(..., error=exception) with normal exceptions, self-referential exception arguments, and cyclic payloads.
  • Added an automated test covering logger.exception(...) and verifying that the emitted log includes a stack trace.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces exception logging capabilities to the logger, including a new exception function to log active stack traces and utility functions to safely serialize exceptions and handle circular references in exception arguments. Comprehensive tests have been added to verify these changes. The review feedback suggests improving type safety by changing the type of the refs parameter from set[_typing.Any] to set[int] in both _exception_from_args and _remove_circular, as it is used to track object IDs.

Comment on lines +75 to +77
def _exception_from_args(
exception: BaseException, refs: set[_typing.Any] | None = None
) -> dict[str, _typing.Any]:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The refs parameter stores the unique integer IDs (id(obj)) of objects to track circular references. Therefore, typing it as set[int] is more precise and type-safe than set[_typing.Any].

Suggested change
def _exception_from_args(
exception: BaseException, refs: set[_typing.Any] | None = None
) -> dict[str, _typing.Any]:
def _exception_from_args(
exception: BaseException, refs: set[int] | None = None
) -> dict[str, _typing.Any]:

return exception.__class__.__name__


def _remove_circular(obj: _typing.Any, refs: set[_typing.Any] | None = None):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The refs parameter stores the unique integer IDs (id(obj)) of objects to track circular references. Therefore, typing it as set[int] is more precise and type-safe than set[_typing.Any].

Suggested change
def _remove_circular(obj: _typing.Any, refs: set[_typing.Any] | None = None):
def _remove_circular(obj: _typing.Any, refs: set[int] | None = None):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants