Advanced Topics

View as Markdown

Logging

The SDK uses Python’s standard logging module for all log messages and follows shared-library best practices.

Logging Behavior

  • Module-level loggers - Each module uses logging.getLogger(__name__)
  • No handlers in the library - Your application owns logging configuration
  • NullHandler by default - Prevents “No handler found” warnings when your app has not configured logging yet

Configuring Logging

1import logging
2
3logging.basicConfig(
4 level=logging.INFO,
5 format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
6)
7
8logging.getLogger("telcoflow_sdk").setLevel(logging.DEBUG)
9logging.getLogger("telcoflow_sdk.control_connection").setLevel(logging.INFO)
10logging.getLogger("telcoflow_sdk.media_connection").setLevel(logging.INFO)

Log Levels

LevelWhat’s Logged
DEBUGDetailed diagnostics such as sent commands and received message types
INFOGeneral events such as connection establishment and call activity
WARNINGReconnection attempts and missing or unexpected data
ERRORConnection failures and parsing errors

Security

The SDK does not log sensitive information such as API keys, authentication tokens, certificate contents, or private keys. Connection URLs and call IDs are logged when useful for diagnostics.


Thread Safety and Concurrency

  • Each incoming call is processed in its own dedicated background task
  • Each ActiveCall runs independently in its own asyncio task
  • Control connection heartbeat runs in a separate task
  • Reconnection logic runs in a separate task
  • All operations are non-blocking
  • The SDK automatically manages task lifecycle and cleanup

Operational Notes

  • Prefer the context manager for long-lived apps - async with TelcoflowClient(config) as client plus await client.run_forever() is the clearest default pattern
  • Use close() and disconnect() deliberately - close() is state-dependent: after connect() it removes the agent while the other parties stay connected, but after answer() without connect() it ends the call for the caller too. disconnect() asks the server to end the call for everyone
  • connect() routes to the original callee - The public 0.24.0 surface documents connect() as a handoff to the original callee associated with the connector
  • Handle CALL_UNANSWERED explicitly - connect() can raise WSSCallCommandError with error_code == "CALL_UNANSWERED", and the call remains active afterwards
  • Split audio pipelines when needed - For apps that must read and write audio concurrently, use asyncio.TaskGroup and a queue to decouple receive and send work

Current Limitations

  • Alternate-destination transfer is not documented in the public 0.24.0 surface - The published package docs only describe connect() to the original callee, so these docs do the same

Requirements

  • Python 3.11+
  • websockets >= 15.0
  • typing-extensions >= 4.8.0

Next Steps