For clean Markdown of any page, append .md to the page URL. For a complete documentation index, see https://docs.telcoflow.com/reference/llms.txt. For full documentation content, see https://docs.telcoflow.com/reference/llms-full.txt.

# Advanced Topics

## 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

```python
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)

logging.getLogger("telcoflow_sdk").setLevel(logging.DEBUG)
logging.getLogger("telcoflow_sdk.control_connection").setLevel(logging.INFO)
logging.getLogger("telcoflow_sdk.media_connection").setLevel(logging.INFO)
```

### Log Levels

| Level | What's Logged |
|---|---|
| `DEBUG` | Detailed diagnostics such as sent commands and received message types |
| `INFO` | General events such as connection establishment and call activity |
| `WARNING` | Reconnection attempts and missing or unexpected data |
| `ERROR` | Connection 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

- [API Reference](/reference/api-reference) - Full method and property reference
- [Error Handling](/reference/error-handling) - Exception hierarchy and recovery strategies
- [Call States](/concepts/call-states-and-lifecycle) - Call lifecycle and valid transitions