Zápis logů a zpracování chyb
Nastav kód workflow tak, aby zapisoval logy, hlásil chyby do tvého lokálního notebooku a při zrušení jobu ukládal částečnou práci. Jak tyto výstupy přečíst z lokálního notebooku, najdeš v části Spuštění spravovaného workflow.
| Ve workflow | V tvém lokálním notebooku |
|---|---|
Zapisuj pomocí loggeru z modulu logging nebo zavolej print(). | job.logs() vrátí řádky. |
| Vyvolej výjimku nebo skonči s nenulovým kódem. | job.status() vrátí ERROR a job.logs() zobrazí traceback. |
Zavolej send_error(). | job.error_message() a job.events(type="ERROR") vrátí kód, zprávu a podrobnosti. |
Zavolej save_result() v handleru SIGTERM. | job.result() vrátí částečný výsledek zrušeného jobu. |
Zápis logů
Cokoli, co workflow zapíše na standardní výstup nebo standardní chybový výstup, jde do logů jobu. Logy přečteš pomocí job.logs() během běhu jobu nebo po jeho skončení.
Nastavení loggeru
Použij modul logging ze standardní knihovny Pythonu. logging.basicConfig() zapisuje na standardní chybový výstup, který se do logů dostane bez zpoždění. Formát v následujícím příkladu přidává ke každému řádku časové razítko:
source_files/my_workflow.py
import logging
logging.basicConfig(
level=logging.WARNING,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logger = logging.getLogger("my_workflow")
logger.setLevel(logging.INFO)
logger.info("Transpiled %d circuits: depth %d -> %d", len(circuits), 1250, 730)
2026-11-01 21:57:35,195 INFO my_workflow: Transpiled 12 circuits: depth 1250 -> 730
level=logging.WARNING skryje zprávy INFO z jiných balíčků a logger.setLevel(logging.INFO) ponechá zprávy INFO pro tvůj vlastní logger. qiskit_ibm_runtime nastavuje svou vlastní úroveň pomocí proměnné prostředí QISKIT_IBM_RUNTIME_LOG_LEVEL.
print() se do logů také dostane, ale standardní výstup je ukládán do vyrovnávací paměti, takže vypsané řádky se nemusí zobrazit až do skončení jobu. Pokud používáš print(), předej flush=True.
Nastavení úrovně logování pro každý běh
Chceš-li ladit jeden běh bez změny kódu, předej úroveň logování jako argument:
run_workflow_remotely.ipynb
job = my_workflow.run(circuits=circuits, log_level="DEBUG")
Aby to fungovalo, nastav úroveň svého loggeru z argumentu:
source_files/my_workflow.py
from qiskit_serverless import get_arguments
arguments = get_arguments()
logger.setLevel(arguments.get("log_level", "INFO"))
Pokud workflow má schéma argumentů, deklaruj v něm log_level, například jako {"enum": ["DEBUG", "INFO", "WARNING"]}.
Osvědčený postup: rozhodni, co logovat
Loguj tolik, abys mohl běh z logů zrekonstruovat:
-
Na začátku zaloguj, co běh obdržel. Zaloguj název backendu, počet obvodů a další nastavení, která se mezi běhy mění.
-
Loguj začátek a konec každé fáze. Používej pevné zprávy, které můžeš vyhledávat.
-
Loguj iterace v pevném intervalu. Například loguj řádek jako
iteration 10/200, energy=-1.1000každých 10 iterací, nikoli každou iteraci. -
Loguj výjimky pomocí
logger.exception()uvnitř blokuexcept. Řádek obsahuje úplný traceback, i když se workflow z chyby zotaví. -
Vypni indikátory průběhu. Logy ukládají každé překreslení indikátoru průběhu, například z
tqdm, jako samostatný řádek. Předejtqdmargumentdisable=Truenebo loguj v intervalech. -
Udržuj logy malé. Neloguj velká data, například každý shot. Spravovaný workflow uchovává pro každý job jen nejnovějších 50 MB logů.
-
Slova „error“ a „exception“ používej jen ve zprávách o selháních. Pokud neúspěšný job nenahlásil svou chybu, výjimka, kterou
job.result()vyvolá, obsahuje jen řádky logu, které tato slova obsahují.
Osvědčený postup: hlas chyby pomocí send_error()
Když workflow vyvolá výjimku, job skončí se stavem ERROR a traceback jde do logů. Spravovaný workflow chybu neukládá, ani u ServerlessError. job.error_message() vrací {} a výjimka, kterou job.result() vyvolá, obsahuje jen řádky logu, které obsahují error nebo exception. Kontroly typů a rozsahů argumentů patří do schématu argumentů, které chybný běh odmítne ještě před spuštěním jobu.
Hlas každou chybu, která zastaví workflow, pomocí send_error(), aby tvůj lokální notebook dostal stručnou chybu s kódem a podrobnostmi:
run_workflow_remotely.ipynb
print(job.error_message())
| Message: The circuit has more qubits than the backend.
| Code: 4500
| Exception: InputError
| Details:
| - circuit_qubits: 10
| - backend_qubits: 5
job.result() vyvolá QiskitServerlessException se stejným textem.
Abys to povolil, vyvolej ServerlessError v místě, kde chyba nastane, a kód svého entrypointu obal blokem try, který odešle každou chybu pomocí send_error(). Workflow přidá tento handler do Catalog Functions, ale ne do spravovaných workflow:
source_files/my_workflow.py
from qiskit_serverless import ServerlessError, send_error
class InputError(ServerlessError):
"""The arguments are valid, but the workflow cannot use them."""
def main():
...
if num_qubits > backend.num_qubits:
raise InputError(
code="4500",
message="The circuit has more qubits than the backend.",
details={"circuit_qubits": num_qubits, "backend_qubits": backend.num_qubits},
)
...
try:
main()
except Exception as error:
send_error(
code=getattr(error, "code", "1"),
message=getattr(error, "message", repr(error)),
exception=type(error).__name__,
args=getattr(error, "details", None),
)
raise
-
Vyvolej chybu znovu. Závěrečný
raisezachová traceback v protokolech a ukončí úlohu se stavemERROR. -
Vytvoř podtřídu
ServerlessErrorpro pojmenování skupiny chyb. Handler odešle název třídy, napříkladInputError, jako poleexception. -
Handler zachytí i další výjimky. Dostanou kód
1a jejichrepr()se stane zprávou, napříkladValueError('bad shots').
Nahlášení problému a pokračování
send_error() workflow nezastaví ani nezmění jeho stav, takže můžeš hlásit i problémy, ze kterých se workflow zotaví, například každý obvod, který přeskočí:
source_files/my_workflow.py
valid_circuits = []
for index, circuit in enumerate(circuits):
if circuit.num_qubits > backend.num_qubits:
send_error(
code="4501",
message="Skipped a circuit that is wider than the backend.",
exception="InputError",
args={"circuit_index": index},
)
continue
valid_circuits.append(circuit)
job.events(type="ERROR") vrací všechny chyby, které workflow nahlásilo, včetně chyb úlohy, která skončila se stavem DONE. Viz Zjištění příčiny chyby.
-
Předávej čísla a řetězce v
args.error_message()zobrazí pouze první položku seznamu. -
Zkontroluj návratovou hodnotu. Pokud workflow nemůže chybu uložit, například protože kód běží mimo úlohu,
send_error()zapíše chybu do protokolů a vrátíFalse.
Úklid při zrušení úlohy
Když zrušíš úlohu, workflow pošle tvému entrypointu SIGTERM, nahraje závěrečné protokoly a vypne prostředí. Bez handleru se Python okamžitě zastaví a bloky finally se nespustí. Workflow se také pokusí zrušit úlohy a sessions na QPU, které workflow spustilo pomocí get_runtime_service(). Viz Zrušení propojených úloh na QPU.
Chceš-li zachovat částečnou práci, zaregistruj handler pro SIGTERM. Tvůj lokální notebook pak získá částečný výsledek ze zrušené úlohy:
run_workflow_remotely.ipynb
job.cancel()
print(job.result()) # {'status': 'canceled', 'partial_results': [...]}
Abys to povolil, přidej handler na začátek svého entrypointu:
source_files/my_workflow.py
import signal
import sys
from qiskit_serverless import save_result
partial_results = []
def handle_sigterm(signum, frame):
save_result({"status": "canceled", "partial_results": partial_results})
sys.exit(0)
signal.signal(signal.SIGTERM, handle_sigterm)
sys.exit() v handleru vyvolá SystemExit v kódu, který právě běží, takže se bloky finally spustí dříve, než skript skončí. Úloha stále skončí se stavem CANCELED.
Udržuj handler krátký, protože workflow vypne prostředí brzy po SIGTERM. U dlouhých běhů také ukládej kontrolní body, aby mohl pozdější běh pokračovat poté, co úloha selže nebo dosáhne 24hodinového limitu. Viz Doporučený postup: kontrolní body u dlouhých úloh.
Další kroky
- Spusť spravované workflow, abys spustil úlohu a přečetl její stav, protokoly a výsledek.
- Spravuj soubory napříč běhy, abys ukládal kontrolní body, ze kterých může pozdější běh pokračovat.
- Vyber výpočetní profil, abys spouštěl své workflow s více prostředky CPU, paměti nebo GPU.