diff --git a/litellm/integrations/datadog/datadog.py b/litellm/integrations/datadog/datadog.py index 315a35c833..e8a74baa78 100644 --- a/litellm/integrations/datadog/datadog.py +++ b/litellm/integrations/datadog/datadog.py @@ -272,7 +272,7 @@ class DataDogLogger(CustomBatchLogger): # Build the initial payload truncate_standard_logging_payload_content(standard_logging_object) - json_payload = json.dumps(standard_logging_object) + json_payload = json.dumps(standard_logging_object, default=str) verbose_logger.debug("Datadog: Logger - Logging payload = %s", json_payload) @@ -298,7 +298,7 @@ class DataDogLogger(CustomBatchLogger): import gzip import json - compressed_data = gzip.compress(json.dumps(data).encode("utf-8")) + compressed_data = gzip.compress(json.dumps(data, default=str).encode("utf-8")) response = await self.async_client.post( url=self.intake_url, data=compressed_data, # type: ignore @@ -329,7 +329,7 @@ class DataDogLogger(CustomBatchLogger): import json _payload_dict = payload.model_dump() - _dd_message_str = json.dumps(_payload_dict) + _dd_message_str = json.dumps(_payload_dict, default=str) _dd_payload = DatadogPayload( ddsource="litellm", ddtags="", @@ -433,7 +433,7 @@ class DataDogLogger(CustomBatchLogger): "metadata": clean_metadata, } - json_payload = json.dumps(payload) + json_payload = json.dumps(payload, default=str) verbose_logger.debug("Datadog: Logger - Logging payload = %s", json_payload) diff --git a/tests/logging_callback_tests/test_datadog.py b/tests/logging_callback_tests/test_datadog.py index 86ef170bca..2d3cb36046 100644 --- a/tests/logging_callback_tests/test_datadog.py +++ b/tests/logging_callback_tests/test_datadog.py @@ -498,3 +498,37 @@ def test_datadog_static_methods(): expected_custom_tags = "env:production,service:custom-service,version:1.0.0,HOSTNAME:test-host,POD_NAME:pod-123" print("DataDogLogger._get_datadog_tags()", DataDogLogger._get_datadog_tags()) assert DataDogLogger._get_datadog_tags() == expected_custom_tags + + +@pytest.mark.asyncio +async def test_datadog_non_serializable_messages(): + """Test logging events with non-JSON-serializable messages""" + dd_logger = DataDogLogger() + + # Create payload with non-serializable content + standard_payload = create_standard_logging_payload() + non_serializable_obj = datetime.now() # datetime objects aren't JSON serializable + standard_payload["messages"] = [{"role": "user", "content": non_serializable_obj}] + standard_payload["response"] = { + "choices": [{"message": {"content": non_serializable_obj}}] + } + + kwargs = {"standard_logging_object": standard_payload} + + # Test payload creation + dd_payload = dd_logger.create_datadog_logging_payload( + kwargs=kwargs, + response_obj=None, + start_time=datetime.now(), + end_time=datetime.now(), + ) + + # Verify payload can be serialized + assert dd_payload["status"] == DataDogStatus.INFO + + # Verify the message can be parsed back to dict + dict_payload = json.loads(dd_payload["message"]) + + # Check that the non-serializable objects were converted to strings + assert isinstance(dict_payload["messages"][0]["content"], str) + assert isinstance(dict_payload["response"]["choices"][0]["message"]["content"], str)