> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squarecloud.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Tiempo real

> Transmite en vivo los logs, el estado y los eventos del sistema de una aplicación con client.apps.realtime(), un iterador (iterador asíncrono con AsyncSquareCloud) que combina los frames de estado y se reconecta por sí solo.

`client.apps.realtime(app_id)` abre un stream en vivo de los logs, el estado y los eventos del sistema de una aplicación. Con `SquareCloud` es un **iterador** que se consume con `for`; con `AsyncSquareCloud` es un **iterador asíncrono** que se consume con `async for`. Úsalo en un bloque `with` / `async with` para que al salir del bloque se cierre la conexión.

<Tabs>
  <Tab title="Síncrono">
    ```python theme={"system"}
    with client.apps.realtime(app_id) as stream:
        for event in stream:
            if event["event"] == "logs":
                print(event["stream"], event["line"])  # "stdout" | "stderr"
            elif event["event"] == "status":
                print(event["status"].get("cpu"), event["status"].get("ram"))  # always the full, merged state
            else:
                print(event["event"], event["data"])  # "system" or "error"
    ```
  </Tab>

  <Tab title="Asíncrono">
    ```python theme={"system"}
    async with client.apps.realtime(app_id) as stream:  # no await: realtime() is not a coroutine
        async for event in stream:
            if event["event"] == "logs":
                print(event["stream"], event["line"])  # "stdout" | "stderr"
            elif event["event"] == "status":
                print(event["status"].get("cpu"), event["status"].get("ram"))  # always the full, merged state
            else:
                print(event["event"], event["data"])  # "system" or "error"
    ```
  </Tab>
</Tabs>

Con `AsyncSquareCloud`, `apps.realtime(app_id)` devuelve directamente un `AsyncRealtime`: **no** lo esperes con `await`. Un hilo lector en segundo plano entrega los eventos a tu event loop, así que no se ocupa ningún hilo del executor mientras esperas.

## Eventos

Cada evento es un dict con `event`, `data` (el texto sin procesar del frame) e `id` (`None` cuando el frame no tiene).

| `event`   | Campos adicionales | Descripción                                                                                                                                                                     |
| --------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `logs`    | `stream`, `line`   | Una línea de log. El primer carácter de `data` indica el stream (`\u0001` stdout, `\u0002` stderr); el SDK lo elimina en `line` y establece `stream` en `"stdout"` o `"stderr"` |
| `status`  | `status`           | Métricas del contenedor en vivo. El primer frame es completo y los siguientes solo llevan lo que cambió: el SDK los combina, así que `status` siempre es el estado completo     |
| `system`  |                    | Eventos de conexión como `REALTIME_CONNECTING \| <id>`, `REALTIME_TIMEOUT` o `REALTIME_DISCONNECTED`                                                                            |
| `error`   |                    | Un código de error como `CONTAINER_NOT_FOUND`                                                                                                                                   |
| `message` |                    | Un frame sin nombre de `event`                                                                                                                                                  |

### El objeto `status`

| Campo      | Descripción                                                   |
| ---------- | ------------------------------------------------------------- |
| `cpu`      | Uso de CPU (%)                                                |
| `cpuLimit` | Número de núcleos de CPU asignados                            |
| `ram`      | `[usedMB, limitMB]`                                           |
| `status`   | Estado del contenedor, p. ej. `running`                       |
| `netIO`    | Totales de red `{ i, o }`, con `new` para el último intervalo |
| `bIO`      | Totales de disco `{ i, o }`                                   |
| `uptime`   | Ms Unix del inicio del contenedor                             |

## Finalizar el stream

El bucle termina por sí solo cuando:

* el servidor cierra la conexión limpiamente (cada conexión dura hasta **10 minutos**);
* el servidor envía `REALTIME_DISCONNECTED`;
* sales del bucle con `break` (después sales del bloque `with`, lo que cierra la conexión);
* llamas a `stream.close()`, desde cualquier hilo, incluso mientras el bucle espera datos o una reconexión (el bucle simplemente termina, no lanza ningún error).

`close()` es síncrono tanto en `Realtime` como en `AsyncRealtime`. Para seguir observando más allá de 10 minutos, abre un nuevo stream cuando termine el bucle.

```python theme={"system"}
import threading

stream = client.apps.realtime(app_id)
threading.Timer(60, stream.close).start()  # stop after one minute

for event in stream:
    print(event["event"], event["data"])
```

## Reconexión

Una conexión caída, o que el servidor delegue la reconexión en el cliente (`REALTIME_RECONNECT`), reabre el stream por sí sola:

* hasta **3 veces seguidas**; cualquier evento `logs` o `status` reinicia el contador;
* cada reapertura al menos **5,5 s** después de la apertura anterior, para respetar el ritmo de la API de una apertura cada 5 segundos por aplicación.

Tras 3 reconexiones fallidas, el bucle lanza un `SquareCloudAPIError` con `NETWORK_ERROR`. Cada apertura es un `GET`, así que un error de red al abrir también se [reintenta](/es/sdks/py/errors#reintentos) hasta `max_retries` veces; una apertura que sigue fallando lanza el error.

## Límites y errores

* **5** conexiones simultáneas por cuenta y **30** por aplicación, sumando todos los usuarios. Superarlas da 429 `REALTIME_MAX_CONNECTIONS` o `REALTIME_MAX_CONNECTIONS_APP`.
* El estado HTTP se comprueba antes del streaming, así que estos errores (y el 404 de una aplicación desconocida) se lanzan en la primera iteración.
* El timeout solo cubre la apertura, hasta que llegan los encabezados de la respuesta. El stream en sí no tiene timeout: una conexión semiabierta (un portátil que sale de suspensión) espera hasta que llames a `close()`.
