How do I enable DEBUG logging for the LiveKit RTC and agents core to debug WebRTC connection issues?

My agent occasionally hangs when connecting to a room. The dispatch job is received and the entrypoint starts, but ctx.connect() seems to stall and I eventually see a warning like “Room not connected within 10 seconds after calling job_entry.” Setting LIVEKIT_LOG_LEVEL=DEBUG doesn’t seem to give me the connection-layer detail I need to diagnose what’s happening between my worker and LiveKit. How do I get full debug logs for the RTC and connection layers specifically?

LIVEKIT_LOG_LEVEL=DEBUG mainly raises the verbosity of the agents framework. The RTC and connection layer, which is usually what you need for connection stalls, is silenced by the SDK at startup, so the environment variable alone won’t surface it. To capture that detail you have to override the Python loggers directly in your agent code.

Target exactly three loggers and set them to DEBUG:

  • livekit
  • livekit.agents
  • livekit.rtc

Deliberately leave the plugin loggers (livekit.plugins.*) and aiohttp at their normal levels, since enabling DEBUG on those adds a lot of noise without helping with connection diagnosis.

There are two pieces to this. First, a logging filter that only allows DEBUG records through from the LiveKit core loggers, so DEBUG output stays scoped to what you care about:

class LiveKitCoreDebugFilter(logging.Filter):
  """Allow DEBUG records only from the LiveKit RTC and agents core loggers."""

  def filter(self, record: logging.LogRecord) -> bool:
    if record.levelno == logging.DEBUG:
      name = record.name
      return name == "livekit" or name.startswith(
        ("livekit.agents", "livekit.rtc")
      )
    return True

Second, a context manager that temporarily escalates the core loggers to DEBUG, attaches the filter to the root handlers, and then cleanly restores the original levels and removes the filter when it exits. Because the SDK sets these logger levels itself at startup, overriding them directly (rather than relying on the env var) is what makes the debug output actually appear. The scoped cleanup is important: it prevents the filter from leaking onto shared handlers across subsequent calls or test runs.

@contextlib.contextmanager
def _debug_ctx_connect_logs() -> Generator[None, None, None]:
  """Temporarily escalate livekit core loggers to DEBUG.

  The LiveKit SDK silences its own loggers at startup, so we must
  override those levels directly. Scoped cleanup prevents leaking the
  filter onto shared handlers across calls or test runs.
  """
  root = logging.getLogger()
  lk_logger = logging.getLogger("livekit")
  lk_agents_logger = logging.getLogger("livekit.agents")
  lk_rtc_logger = logging.getLogger("livekit.rtc")

  orig_root = root.level
  orig_lk = lk_logger.level
  orig_lk_agents = lk_agents_logger.level
  orig_lk_rtc = lk_rtc_logger.level

  debug_filter = LiveKitCoreDebugFilter()
  root.setLevel(logging.DEBUG)
  lk_logger.setLevel(logging.DEBUG)
  lk_agents_logger.setLevel(logging.DEBUG)
  lk_rtc_logger.setLevel(logging.DEBUG)
  for handler in root.handlers:
    handler.addFilter(debug_filter)

  try:
    yield
  finally:
    root.setLevel(orig_root)
    lk_logger.setLevel(orig_lk)
    lk_agents_logger.setLevel(orig_lk_agents)
    lk_rtc_logger.setLevel(orig_lk_rtc)
    for handler in root.handlers:
      handler.filters = [f for f in handler.filters if f is not debug_filter]

Wrap the connection-sensitive portion of your agent’s entrypoint (for example, the ctx.connect() call) with this context manager so debug logs are captured only during that window.

with _debug_ctx_connect_logs():
     await ctx.connect()