Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/sdk-python.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions sdk/python/pyproject.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 104 additions & 2 deletions sdk/python/tests/test_execution_logger.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import json

import pytest

from agentfield.execution_context import (
ExecutionContext,
reset_execution_context,
set_execution_context,
)
from agentfield.logger import log_execution, log_info
from agentfield.logger import log_execution, log_info, AgentFieldLogger


@pytest.mark.unit
Expand Down Expand Up @@ -155,3 +154,106 @@ def capture(*args, **kwargs):
assert captured[1]["attributes"]["duration_ms"] == 15
assert captured[1]["attributes"]["result"] == {"ok": True}
assert captured[2]["attributes"]["error"] == "boom"

@pytest.fixture
def base_logger(monkeypatch):
"""
Initializes an AgentFieldLogger with environment overrides to validate
core observability and data-handling logic.
"""
monkeypatch.setenv("AGENTFIELD_LOG_LEVEL", "DEBUG")
monkeypatch.setenv("AGENTFIELD_LOG_PAYLOADS", "true")
monkeypatch.setenv("AGENTFIELD_LOG_TRUNCATE", "50")
monkeypatch.setenv("AGENTFIELD_LOG_TRACKING", "true")
monkeypatch.setenv("AGENTFIELD_LOG_FIRE", "true")

logger = AgentFieldLogger(name="telemetry")
logger.logger.propagate = True
return logger

@pytest.mark.unit
def test_heartbeat_event(base_logger, caplog):
base_logger.heartbeat("Agent pulsing", status="nominal")
assert "Agent pulsing" in caplog.text

@pytest.mark.unit
def test_logger_track_output(base_logger, caplog):
base_logger.track("token_usage", count=500)
assert "token_usage" in caplog.text

@pytest.mark.unit
def test_logger_fire_output(base_logger, caplog):
base_logger.fire("node_transition", target="reasoning_node")
assert "node_transition" in caplog.text

@pytest.mark.unit
def test_logger_debug_output(base_logger, caplog):
base_logger.debug("Debugging logic")
assert "Debugging" in caplog.text

@pytest.mark.unit
def test_logger_security_output(base_logger, caplog):
base_logger.security("Sanitized keys", level="HIGH")
assert "Sanitized" in caplog.text

@pytest.mark.unit
def test_logger_network_output(base_logger, caplog):
base_logger.network("GET https://api.openai.com", method="GET")
assert "api.openai.com" in caplog.text

@pytest.mark.unit
def test_logger_severity_levels(base_logger, caplog):
base_logger.warn("Warning msg")
base_logger.error("Error msg")
base_logger.critical("Failure msg")
out = caplog.text
assert "Warning" in out
assert "Error" in out
assert "Failure" in out

@pytest.mark.unit
def test_logger_success_and_setup(base_logger, caplog):
base_logger.success("Operation complete")
base_logger.setup("Environment ready")
out = caplog.text
assert "Operation" in out and "Environment" in out

# --- FORMATTING & PAYLOAD EDGE CASES ---

@pytest.mark.unit
def test_truncate_message_at_limit(base_logger):
"""Verifies message longer than truncate_length -> ends with '...'"""
long_msg = "A" * 100
truncated = base_logger._truncate_message(long_msg)
assert len(truncated) == 53
assert truncated.endswith("...")

@pytest.mark.unit
def test_logger_short_message_handling(base_logger):
"""Verifies that messages under the truncate limit are untouched"""
msg = "Short"
assert base_logger._truncate_message(msg) == "Short"

@pytest.mark.unit
def test_format_payload_hides_by_default(monkeypatch):
"""Verifies dict -> '[payload hidden]' when flag is false"""
monkeypatch.setenv("AGENTFIELD_LOG_PAYLOADS", "false")
logger = AgentFieldLogger(name="privacy-test")
test_data = {"secret": "key"}
formatted = logger._format_payload(test_data)
assert formatted == "[payload hidden - set AGENTFIELD_LOG_PAYLOADS=true to show]"

@pytest.mark.unit
def test_format_payload_shows_when_enabled(base_logger):
"""Verifies AGENTFIELD_LOG_PAYLOADS=true -> JSON string"""
test_data = {"id": "123", "meta": "data"}
formatted = base_logger._format_payload(test_data)
decoded = json.loads(formatted)
assert decoded["id"] == "123"

@pytest.mark.unit
def test_format_payload_handles_non_serializable(base_logger):
"""Verifies fallback to str() for objects with no __dict__ or JSON support"""
test_data = {1, 2, 3}
formatted = base_logger._format_payload(test_data)
assert "{1, 2, 3}" in formatted
Loading