OpenHands / OpenHands/software-agent-sdk

Route Laminar and agent-server INFO logs through structured non-error logging

Open
#3,797 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug Stale
Dominant language
Python
Stars
1.1k
Forks
542
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Context

Several SDK/agent-server startup observability logs are INFO-level messages, but can be ingested as status:error because they are emitted through third-party/default stderr handlers instead of the SDK's structured stdout logging:

  • "No path found in HTTP endpoint URL"
  • "Laminar is already initialized"
  • Uvicorn startup/shutdown messages such as "Uvicorn running on", "child process", "parent process", and "Received SIGTERM" when emitted by the agent server.

These should remain visible as INFO, not page application-failure monitors.

Investigation notes

Laminar

openhands-sdk/openhands/sdk/observability/laminar.py calls lmnr.Laminar.initialize(...) when observability env vars are present. In lmnr==0.7.49:

  • No path found in HTTP endpoint URL... is emitted with logger.info(...) from lmnr/opentelemetry_lib/tracing/exporter.py.
  • That logger is created with Laminar's get_default_logger(..., propagate=False), which attaches a plain logging.StreamHandler(); the default stream is stderr.
  • Laminar is already initialized. Skipping initialization. is emitted with cls.__logger.info(...) from lmnr/sdk/laminar.py after Laminar._initialize_logger() attaches another default stderr StreamHandler.

So the Python log level is INFO, but the stream/formatter can make Datadog classify the record as status:error.

Agent-server/Uvicorn

In openhands-agent-server:

  • openhands-agent-server/openhands/agent_server/logging_config.py defines the JSON access handler with stream: ext://sys.stderr.
  • openhands-sdk/openhands/sdk/logger/logger.py uses logging.StreamHandler() without an explicit stream, which defaults to stderr for the root SDK handler.
  • Uvicorn's startup/shutdown messages are INFO logs. If they are written to stderr, container log ingestion can classify them as error status.

Proposed fix

  1. Normalize Laminar loggers after initialization:
    • remove Laminar-installed stderr handlers for lmnr* loggers, or replace them with SDK-compatible structured handlers;
    • set propagation so INFO records flow through the SDK logger once;
    • preserve INFO visibility rather than silencing the messages globally.
  2. Update SDK/agent-server logging to route INFO/WARNING JSON logs to stdout and reserve stderr for real ERROR/CRITICAL records, or otherwise emit a Datadog-compatible status/severity field that overrides stream classification.
  3. Adjust openhands-agent-server Uvicorn logging config so access/error INFO logs do not use stderr in production JSON mode.
  4. Add tests that simulate Laminar/Uvicorn INFO records and assert they are emitted with INFO severity/status and not via stderr-only handlers.

Acceptance criteria

  • No path found in HTTP endpoint URL and Laminar is already initialized are ingested as INFO, not status:error.
  • Agent-server Uvicorn startup/shutdown INFO logs are ingested as INFO.
  • Real ERROR/CRITICAL logs still surface as errors.

This issue was created by an AI agent (OpenHands) on behalf of the user.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read openhands-sdk/openhands/sdk/observability/laminar.py and openhands-sdk/openhands/sdk/logger/logger.py, then inspect openhands-agent-server/openhands/agent_server/logging_config.py. Reproduce Laminar and Uvicorn INFO records and trace their handlers and streams. Add tests for INFO severity and stdout routing while confirming ERROR and CRITICAL logs remain errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, observability
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.