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

# Realtime

> Streame Live-Logs, Status und Systemereignisse einer Anwendung mit client.apps.realtime(), einem Iterator (Async Iterator mit AsyncSquareCloud), der Status-Frames zusammenführt und sich selbst neu verbindet.

`client.apps.realtime(app_id)` öffnet einen Live-Stream der Logs, des Status und der Systemereignisse einer App. Mit `SquareCloud` ist es ein **Iterator**, den du mit `for` verarbeitest; mit `AsyncSquareCloud` ist es ein **Async Iterator**, den du mit `async for` verarbeitest. Verwende ihn in einem `with`- / `async with`-Block, damit das Verlassen des Blocks die Verbindung schließt.

<Tabs>
  <Tab title="Synchron">
    ```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="Asynchron">
    ```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>

Mit `AsyncSquareCloud` gibt `apps.realtime(app_id)` direkt ein `AsyncRealtime` zurück: Rufe es **nicht** mit `await` auf. Ein Lese-Thread im Hintergrund liefert die Ereignisse an deine Event Loop, sodass während des Wartens kein Executor-Thread belegt ist.

## Ereignisse

Jedes Ereignis ist ein Dict mit `event`, `data` (dem rohen Text des Frames) und `id` (`None`, wenn der Frame keine hat).

| `event`   | Zusätzliche Felder | Beschreibung                                                                                                                                                                                           |
| --------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `logs`    | `stream`, `line`   | Eine Logzeile. Das erste Zeichen von `data` gibt den Stream an (`\u0001` stdout, `\u0002` stderr); das SDK entfernt es in `line` und setzt `stream` auf `"stdout"` oder `"stderr"`                     |
| `status`  | `status`           | Live-Metriken des Containers. Der erste Frame ist vollständig, die folgenden enthalten nur, was sich geändert hat: Das SDK führt sie zusammen, sodass `status` immer den vollständigen Zustand enthält |
| `system`  |                    | Verbindungsereignisse wie `REALTIME_CONNECTING \| <id>`, `REALTIME_TIMEOUT` oder `REALTIME_DISCONNECTED`                                                                                               |
| `error`   |                    | Ein Fehlercode wie `CONTAINER_NOT_FOUND`                                                                                                                                                               |
| `message` |                    | Ein Frame ohne `event`-Namen                                                                                                                                                                           |

### Das Objekt `status`

| Feld       | Beschreibung                                                  |
| ---------- | ------------------------------------------------------------- |
| `cpu`      | CPU-Auslastung (%)                                            |
| `cpuLimit` | Anzahl der zugewiesenen CPU-Kerne                             |
| `ram`      | `[usedMB, limitMB]`                                           |
| `status`   | Zustand des Containers, z. B. `running`                       |
| `netIO`    | Netzwerksummen `{ i, o }`, mit `new` für das letzte Intervall |
| `bIO`      | Festplattensummen `{ i, o }`                                  |
| `uptime`   | Unix-ms des Container-Starts                                  |

## Den Stream beenden

Die Schleife endet von selbst, wenn:

* der Server die Verbindung sauber schließt (jede Verbindung dauert bis zu **10 Minuten**);
* der Server `REALTIME_DISCONNECTED` sendet;
* du mit `break` aus der Schleife aussteigst (verlasse dann den `with`-Block, der die Verbindung schließt);
* du `stream.close()` aus einem beliebigen Thread aufrufst, selbst während die Schleife auf Daten oder auf eine Neuverbindung wartet (die Schleife endet einfach, sie wirft nicht).

`close()` ist sowohl bei `Realtime` als auch bei `AsyncRealtime` synchron. Um länger als 10 Minuten zuzusehen, öffne einen neuen Stream, wenn die Schleife endet.

```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"])
```

## Neuverbindung

Eine abgebrochene Verbindung, oder wenn der Server die Neuverbindung an den Client übergibt (`REALTIME_RECONNECT`), öffnet den Stream von selbst erneut:

* bis zu **3 Mal hintereinander**; jedes `logs`- oder `status`-Ereignis setzt den Zähler zurück;
* jedes erneute Öffnen mindestens **5,5 s** nach dem vorherigen, um unter dem Takt der API von einem Öffnen pro 5 Sekunden pro App zu bleiben.

Nach 3 fehlgeschlagenen Neuverbindungen wirft die Schleife einen `SquareCloudAPIError` mit `NETWORK_ERROR`. Jedes Öffnen ist ein `GET`, daher wird auch ein Netzwerkfehler beim Öffnen bis zu `max_retries` Mal [wiederholt](/de/sdks/py/errors#wiederholungen); ein Öffnen, das weiterhin fehlschlägt, wirft.

## Limits und Fehler

* **5** gleichzeitige Verbindungen pro Konto und **30** pro App, über alle Benutzer hinweg. Eine Überschreitung ergibt 429 `REALTIME_MAX_CONNECTIONS` oder `REALTIME_MAX_CONNECTIONS_APP`.
* Der HTTP-Status wird vor dem Streaming geprüft, daher werfen diese Fehler (und 404 für eine unbekannte App) bei der ersten Iteration.
* Das Timeout deckt nur das Öffnen ab, bis die Antwort-Header eintreffen. Der Stream selbst hat kein Timeout: Eine halb offene Verbindung (ein Laptop, der aus dem Ruhezustand erwacht) wartet, bis du `close()` aufrufst.
