Describe the bug
|
def _emit_structured_record(self, record: Dict[str, Any]) -> Dict[str, Any]: |
|
line = json.dumps( |
|
record, ensure_ascii=False, separators=(",", ":"), default=str |
|
) |
|
print(line, file=sys.stdout, flush=True) |
|
self._dispatch_to_cp(record) |
|
return record |
_emit_structured_record do 2 things at once:
- Dumps to stdout, that depending on the size of the payload (ie >1MB) it will block the event loop if the stdout is for any reason busy or slow. logging default
StreamHandler uses a threading.RLock internally also, for double trouble, besides the Agentfield logger also using another global lock instance
|
def _setup_logger(self): |
|
"""Setup logger with console handler if not already configured""" |
|
|
|
if not self.logger.handlers: |
|
handler = logging.StreamHandler(stream=sys.stdout) |
|
formatter = logging.Formatter("%(message)s") |
|
handler.setFormatter(formatter) |
|
self.logger.addHandler(handler) |
|
self.logger.propagate = False |
In highly concurrent or heavy I/O driven deployments, it's a dice roll until something stalls.
- The only way to omit this JSON dump is to manually patch
_emit_structured_record and _emit_plain the agentfield logger module during initialization and make it a no-op.
Ideally it should be an opt-in. There's a ambiguous env var AGENTFIELD_LOGS_ENABLED but it's a misnomer, since what it does is to watch for its own process stdout / stderr and intercept that.
|
def install_stdio_tee() -> None: |
|
"""Replace sys.stdout/sys.stderr with tees into the process log ring.""" |
|
global _tee_installed |
|
if _tee_installed or not logs_enabled(): |
|
return |
|
ring = get_ring() |
|
ml = max_line_bytes() |
|
sys.stdout = _TeeTextIO("stdout", cast(TextIO, sys.__stdout__), ring, ml) |
|
sys.stderr = _TeeTextIO("stderr", cast(TextIO, sys.__stderr__), ring, ml) |
|
_tee_installed = True |
Steps to reproduce
- Go to '...'
- Run '...'
- See error
Expected behavior
When logs are enabled but no stdout setting (that doesn't exist today, let's say AGENTFIELD_ONLY_EMIT_LOGS), only sends to the Postgres instance, through _dispatch_to_cp and no print line.
Additionally if locks must be used to ensure order, either use a non-blocking Lock (with a tiny timeout that constantly yields) or use a FIFO queue that is async friendly, and will deal with global locks only when it needs to output.
Screenshots / Logs
Environment
- Control plane version:
- SDK version (if applicable):
- Deployment environment (local, docker, kubernetes, etc.):
Additional context
Describe the bug
agentfield/sdk/python/agentfield/logger.py
Lines 178 to 184 in 4d4d54e
_emit_structured_recorddo 2 things at once:StreamHandleruses athreading.RLockinternally also, for double trouble, besides the Agentfield logger also using another global lock instanceagentfield/sdk/python/agentfield/logger.py
Lines 81 to 89 in 4d4d54e
_emit_structured_recordand_emit_plainthe agentfield logger module during initialization and make it a no-op.Ideally it should be an opt-in. There's a ambiguous env var
AGENTFIELD_LOGS_ENABLEDbut it's a misnomer, since what it does is to watch for its own process stdout / stderr and intercept that.agentfield/sdk/python/agentfield/node_logs.py
Lines 223 to 232 in 4d4d54e
Steps to reproduce
Expected behavior
When logs are enabled but no stdout setting (that doesn't exist today, let's say
AGENTFIELD_ONLY_EMIT_LOGS), only sends to the Postgres instance, through_dispatch_to_cpand noprintline.Additionally if locks must be used to ensure order, either use a non-blocking Lock (with a tiny timeout that constantly yields) or use a FIFO queue that is async friendly, and will deal with global locks only when it needs to output.
Screenshots / Logs
Environment
Additional context