כתיבת לוגים וטיפול בשגיאות
הגדירו את קוד ה-workflow כך שיכתוב לוגים, ידווח על שגיאות למחברת המקומית שלכם וישמור עבודה חלקית כשמשימה מבוטלת. כדי לקרוא את הפלטים האלה מהמחברת המקומית שלכם, ראו הרצת workflow מנוהל.
| ב-workflow | במחברת המקומית שלכם |
|---|---|
כתבו עם logger מהמודול logging, או קראו ל-print(). | job.logs() מחזירה את השורות. |
| העלו חריגה, או צאו עם קוד שאינו אפס. | job.status() מחזירה ERROR, ו-job.logs() מציגה את ה-traceback. |
קראו ל-send_error(). | job.error_message() ו-job.events(type="ERROR") מחזירות את הקוד, ההודעה והפרטים. |
קראו ל-save_result() ב-handler של SIGTERM. | job.result() מחזירה את התוצאה החלקית של משימה שבוטלה. |
כתיבת לוגים
כל מה שה-workflow כותב לפלט הסטנדרטי או לשגיאה הסטנדרטית מגיע ללוגים של המשימה. קראו את הלוגים עם job.logs() בזמן שהמשימה רצה או אחרי שהיא מסתיימת.
הגדרת logger
השתמשו במודול 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 עבור ה-logger שלכם. qiskit_ibm_runtime קובעת את הרמה שלה בעצמה באמצעות משתנה הסביבה QISKIT_IBM_RUNTIME_LOG_LEVEL.
גם print() מגיעה ללוגים, אבל הפלט הסטנדרטי מבוצע ב-buffer, ולכן שורות שהודפסו עשויות לא להופיע עד שהמשימה מסתיימת. אם אתם משתמשים ב-print(), העבירו flush=True.
הגדרת רמת הלוג לכל הרצה
כדי לנפות באגים בהרצה אחת בלי לשנות את הקוד, העבירו את רמת הלוג כארגומנט:
run_workflow_remotely.ipynb
job = my_workflow.run(circuits=circuits, log_level="DEBUG")
כדי לאפשר זאת, הגדירו את הרמה של ה-logger שלכם מתוך הארגומנט:
source_files/my_workflow.py
from qiskit_serverless import get_arguments
arguments = get_arguments()
logger.setLevel(arguments.get("log_level", "INFO"))
אם ל-workflow יש סכמת ארגומנטים, הצהירו בה על log_level, למשל כ-{"enum": ["DEBUG", "INFO", "WARNING"]}.
שיטה מומלצת: החליטו מה לרשום ללוג
רשמו ללוג מספיק כדי לשחזר הרצה מהלוגים שלה:
-
בהתחלה, רשמו ללוג מה ההרצה קיבלה. רשמו את שם ה-Backend, את מספר ה-Circuits ואת ההגדרות האחרות שמשתנות בין הרצות.
-
רשמו ללוג את תחילת וסוף כל שלב. השתמשו בהודעות קבועות שאפשר לחפש.
-
רשמו איטרציות במרווח קבוע. למשל, רשמו שורה כמו
iteration 10/200, energy=-1.1000כל 10 איטרציות, במקום בכל איטרציה. -
רשמו חריגות עם
logger.exception()בתוך בלוקexcept. השורה כוללת את ה-traceback המלא, גם אם ה-workflow מתאושש מהשגיאה. -
כבו פסי התקדמות. הלוגים שומרים כל רענון של פס התקדמות, כמו זה של
tqdm, כשורה נפרדת. העבירוdisable=Trueל-tqdm, או רשמו ללוג במרווחים. -
שמרו על לוגים קטנים. אל תרשמו ללוג נתונים גדולים, כמו כל shot. workflow מנוהל שומר רק את 50 MB האחרונים של לוגים לכל משימה.
-
השתמשו במילים "error" ו-"exception" רק בהודעות על כשלים. אם משימה שנכשלה לא דיווחה על השגיאה שלה, החריגה ש-
job.result()מעלה מכילה רק את שורות הלוג שכוללות את המילים האלה.
שיטה מומלצת: דווחו על שגיאות עם send_error()
כש-workflow מעלה חריגה, המשימה מסתיימת בסטטוס ERROR, וה-traceback הולך ללוגים. workflow מנוהל לא שומר את השגיאה, גם לא עבור ServerlessError. job.error_message() מחזירה {}, והחריגה ש-job.result() מעלה מכילה רק את שורות הלוג שכוללות error או exception. בדיקות של סוגי ארגומנטים וטווחים שייכות לסכמת ארגומנטים, שדוחה הרצה פגומה לפני שהמשימה מתחילה.
דווחו עם send_error() על כל שגיאה שעוצרת את ה-workflow, כדי שהמחברת המקומית שלכם תקבל שגיאה קצרה עם קוד ופרטים:
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(). workflow מוסיף handler כזה ל-Catalog Functions, אבל לא ל-workflows מנוהלים:
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כדי לתת שם לקבוצת שגיאות. ה-handler שולח את שם המחלקה, כמוInputError, בשדהexception. -
גם חריגות אחרות מגיעות ל-handler. הן מקבלות את הקוד
1, וה-repr()שלהן הופך להודעה, כמוValueError('bad shots').
דיווח על בעיה והמשך
send_error() אינה עוצרת את ה-workflow ואינה משנה את הסטטוס שלו, ולכן אפשר לדווח גם על בעיות שה-workflow מתאושש מהן, כמו כל מעגל שהוא מדלג עליו:
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") מחזירה כל שגיאה ש-workflow דיווח עליה, כולל שגיאות של job שהסתיים בסטטוס DONE. ראה מציאת הסיבה לשגיאה.
-
העבר מספרים ומחרוזות ב-
args.error_message()מציגה רק את הפריט הראשון ברשימה. -
בדוק את ערך ההחזרה. אם workflow לא יכול לשמור את השגיאה, למשל כי הקוד רץ מחוץ ל-job,
send_error()כותבת את השגיאה ללוגים ומחזירהFalse.
ניקוי כשמבטלים job
כשמבטלים job, ה-workflow שולח SIGTERM לנקודת הכניסה שלך, מעלה את הלוגים הסופיים וסוגר את הסביבה. בלי handler, Python נעצרת מיד, ובלוקים של finally לא רצים. ה-workflow גם מנסה לבטל את ה-jobs וה-sessions של QPU שה-workflow התחיל באמצעות get_runtime_service(). ראה ביטול jobs מקושרים של QPU.
כדי לשמור עבודה חלקית, רשום handler עבור SIGTERM. ה-notebook המקומי שלך יקבל אז את התוצאה החלקית מ-job שבוטל:
run_workflow_remotely.ipynb
job.cancel()
print(job.result()) # {'status': 'canceled', 'partial_results': [...]}
כדי להפעיל זאת, הוסף את ה-handler בתחילת נקודת הכניסה שלך:
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() ב-handler מעלה SystemExit בקוד שרץ כרגע, ולכן בלוקים של finally רצים לפני שהסקריפט מסתיים. ה-job עדיין מסתיים בסטטוס CANCELED.
שמור על ה-handler קצר, כי ה-workflow סוגר את הסביבה זמן קצר אחרי SIGTERM. בהרצות ארוכות שמור גם checkpoints, כדי שהרצה מאוחרת יותר תוכל להמשיך אחרי ש-job נכשל או מגיע למגבלה של 24 שעות. ראה שיטה מומלצת: checkpoint ל-jobs ארוכים.
השלבים הבאים
- הרצת managed workflow כדי להתחיל job ולקרוא את הסטטוס, הלוגים והתוצאה שלו.
- ניהול קבצים בין הרצות כדי לשמור checkpoints שהרצה מאוחרת יותר יכולה להמשיך מהם.
- בחירת פרופיל מחשוב כדי להריץ את ה-workflow שלך עם יותר CPU, זיכרון או משאבי GPU.