Запис журналів і обробка помилок
Налаштуй код свого робочого процесу так, щоб він записував журнали, повідомляв про помилки в локальний ноутбук і зберігав частковий результат, коли завдання скасовано. Щоб прочитати ці дані зі свого локального ноутбука, див. Запуск керованого робочого процесу.
| У робочому процесі | У твоєму локальному ноутбуці |
|---|---|
Пиши через логер із модуля logging або виклич print(). | job.logs() повертає рядки. |
| Викличи виняток або заверши роботу з ненульовим кодом. | job.status() повертає ERROR, а job.logs() показує трасування. |
Виклич send_error(). | job.error_message() і job.events(type="ERROR") повертають код, повідомлення та подробиці. |
Виклич save_result() в обробнику SIGTERM. | job.result() повертає частковий результат скасованого завдання. |
Запис журналів
Усе, що робочий процес записує в стандартний вивід або стандартний потік помилок, потрапляє до журналів завдання. Читай журнали через job.logs() під час виконання завдання або після його завершення.
Налаштування логера
Використовуй модуль logging зі стандартної бібліотеки Python. logging.basicConfig() записує у стандартний потік помилок, який потрапляє до журналів без затримки. Формат у наведеному нижче прикладі додає часову мітку до кожного рядка:
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 приховує повідомлення INFO з інших пакетів, а logger.setLevel(logging.INFO) зберігає повідомлення INFO для твого власного логера. qiskit_ibm_runtime задає свій рівень через змінну середовища QISKIT_IBM_RUNTIME_LOG_LEVEL.
print() також потрапляє до журналів, але стандартний вивід буферизується, тому надруковані рядки можуть з'явитися лише після завершення завдання. Якщо ти використовуєш print(), передай flush=True.
Налаштування рівня журналу для кожного запуску
Щоб налагодити один запуск без зміни коду, передай рівень журналу як аргумент:
run_workflow_remotely.ipynb
job = my_workflow.run(circuits=circuits, log_level="DEBUG")
Щоб це працювало, задай рівень свого логера з аргументу:
source_files/my_workflow.py
from qiskit_serverless import get_arguments
arguments = get_arguments()
logger.setLevel(arguments.get("log_level", "INFO"))
Якщо робочий процес має схему аргументів, оголоси в ній log_level, наприклад як {"enum": ["DEBUG", "INFO", "WARNING"]}.
Найкраща практика: вирішуй, що логувати
Логуй достатньо, щоб відтворити запуск за його логами:
-
На початку логуй, що отримав запуск. Логуй назву бекенда, кількість схем та інші налаштування, що змінюються між запусками.
-
Логуй початок і завершення кожного етапу. Використовуй фіксовані повідомлення, які можна шукати.
-
Логуй ітерації з фіксованим інтервалом. Наприклад, логуй рядок на кшталт
iteration 10/200, energy=-1.1000кожні 10 ітерацій, а не кожну ітерацію. -
Логуй винятки за допомогою
logger.exception()усередині блокуexcept. Рядок містить повний traceback, навіть якщо робочий процес відновлюється після помилки. -
Вимкни індикатори прогресу. Логи зберігають кожне оновлення індикатора прогресу, наприклад від
tqdm, як окремий рядок. Передайdisable=Trueдоtqdmабо логуй з інтервалом. -
Не роздувай логи. Не логуй великі дані, як-от кожен шот. Кероване виконання робочого процесу зберігає лише найновіші 50 МБ логів для кожного завдання.
-
Використовуй слова "error" та "exception" лише в повідомленнях про збої. Якщо невдале завдання не повідомило про свою помилку, виняток, який викликає
job.result(), містить лише ті рядки логів, що містять ці слова.
Найкраща практика: повідомляй про помилки за допомогою send_error()
Коли робочий процес викликає виняток, завдання завершується зі статусом ERROR, а traceback потрапляє до логів. Кероване виконання робочого процесу не зберігає помилку, навіть для ServerlessError. job.error_message() повертає {}, а виняток, який викликає job.result(), містить лише ті рядки логів, що містять error або exception. Перевірки типів і діапазонів аргументів належать до схеми аргументів, яка відхиляє некоректний запуск до початку завдання.
Повідомляй про кожну помилку, що зупиняє робочий процес, за допомогою send_error(), щоб твій локальний ноутбук отримав коротку помилку з кодом і подробицями:
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() викликає QiskitServerlessException з тим самим текстом.
Щоб це ввімкнути, викликай ServerlessError там, де виникає помилка, і оберни код своєї точки входу в блок try, який надсилає будь-яку помилку через send_error(). Робочий процес додає цей обробник до Catalog Functions, але не до кермованих робочих процесів:
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
-
Викликай помилку повторно. Завершальний
raiseзберігає traceback у логах і завершує завдання зі статусомERROR. -
Створи підклас
ServerlessError, щоб назвати групу помилок. Обробник надсилає назву класу, наприкладInputError, як полеexception. -
Інші винятки також потрапляють до обробника. Вони отримують код
1, а їхнійrepr()стає повідомленням, наприкладValueError('bad shots').
Повідом про проблему й продовжуй
send_error() не зупиняє робочий процес і не змінює його статус, тож ти також можеш повідомляти про проблеми, після яких робочий процес відновлюється, наприклад про кожну схему, яку він пропускає:
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") повертає кожну помилку, про яку повідомив робочий процес, зокрема помилки завдання, що завершилося зі статусом DONE. Див. Пошук причини помилки.
-
Передавай числа й рядки в
args.error_message()показує лише перший елемент списку. -
Перевіряй повернуте значення. Якщо робочий процес не може зберегти помилку, наприклад тому, що код виконується поза завданням,
send_error()записує помилку в логи й повертаєFalse.
Очищення під час скасування завдання
Коли ти скасовуєш завдання, робочий процес надсилає SIGTERM твоїй точці входу, вивантажує фінальні логи й вимикає середовище. Без обробника Python зупиняється негайно, а блоки finally не виконуються. Робочий процес також намагається скасувати завдання та сесії QPU, які він запустив через get_runtime_service(). Див. Скасування пов'язаних завдань QPU.
Щоб зберегти часткові результати, зареєструй обробник для SIGTERM. Тоді твій локальний ноутбук отримає частковий результат зі скасованого завдання:
run_workflow_remotely.ipynb
job.cancel()
print(job.result()) # {'status': 'canceled', 'partial_results': [...]}
Щоб це ввімкнути, додай обробник на початку своєї точки входу:
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() в обробнику викликає SystemExit у коді, що виконується, тож блоки finally виконуються до завершення скрипту. Завдання все одно завершується зі статусом CANCELED.
Роби обробник коротким, бо робочий процес вимикає середовище невдовзі після SIGTERM. Для тривалих запусків також зберігай контрольні точки, щоб наступний запуск міг відновитися після збою завдання або досягнення 24-годинного ліміту. Див. Найкраща практика: контрольні точки для тривалих завдань.
Наступні кроки
- Запусти кероване виконання робочого процесу, щоб почати завдання та прочитати його статус, логи й результат.
- Керуй файлами між запусками, щоб зберігати контрольні точки, з яких може відновитися наступний запуск.
- Обери профіль обчислень, щоб запускати свій робочий процес з більшою кількістю ресурсів CPU, пам'яті або GPU.