Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion datadog_logger/handler.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Iterable
from datadog.api.events import Event
from datadog.api.exceptions import ApiError
import logging
from typing import Any, Optional

Expand Down Expand Up @@ -43,7 +44,14 @@ def emit(self, record: logging.LogRecord) -> None:
if record.levelno in LOG_LEVEL_ALERT_TYPE_MAPPINGS:
create_args["alert_type"] = LOG_LEVEL_ALERT_TYPE_MAPPINGS[record.levelno]

Event.create(**create_args) # type: ignore[arg-type]
response = Event.create(**create_args) # type: ignore[arg-type]

# The datadog client is muted by default, which means it logs
# errors returned by the API and returns them to the caller
# instead of raising them, so failures to submit the event have
# to be detected here to be reported by handleError
if isinstance(response, dict) and response.get("errors"):
raise ApiError(response)

except Exception:
self.handleError(record)
62 changes: 62 additions & 0 deletions tests/test_datadog_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,65 @@ def test_emit_calls_handle_error_when_it_raises_an_exception(
handler.emit(record)

mock_handle_error.assert_called_once_with(handler, record)

@mock.patch("logging.Handler.handleError", autospec=True)
@mock.patch("datadog_logger.handler.Event", autospec=True)
def test_emit_calls_handle_error_when_response_contains_errors(
self,
mock_event_class: mock.Mock,
mock_handle_error: mock.Mock
) -> None:
# The datadog client is muted by default, so it returns errors
# reported by the API instead of raising them
mock_event_class.create.return_value = {"errors": ["Forbidden"]}

handler = DatadogLogHandler()

record = logging.makeLogRecord({
"msg": "Some message"
})

handler.emit(record)

mock_handle_error.assert_called_once_with(handler, record)

@mock.patch("logging.Handler.handleError", autospec=True)
@mock.patch("datadog_logger.handler.Event", autospec=True)
def test_emit_does_not_call_handle_error_when_event_is_created(
self,
mock_event_class: mock.Mock,
mock_handle_error: mock.Mock
) -> None:
mock_event_class.create.return_value = {
"status": "ok",
"event": {"id": 1234, "title": "Some message"}
}

handler = DatadogLogHandler()

record = logging.makeLogRecord({
"msg": "Some message"
})

handler.emit(record)

mock_handle_error.assert_not_called()

@mock.patch("logging.Handler.handleError", autospec=True)
@mock.patch("datadog_logger.handler.Event", autospec=True)
def test_emit_does_not_call_handle_error_when_response_has_no_errors(
self,
mock_event_class: mock.Mock,
mock_handle_error: mock.Mock
) -> None:
mock_event_class.create.return_value = {"errors": []}

handler = DatadogLogHandler()

record = logging.makeLogRecord({
"msg": "Some message"
})

handler.emit(record)

mock_handle_error.assert_not_called()