דלג לתוכן הראשי
פרויקט זה בשלב בטא. מצאת באג? פתח issue ב-GitHub. יש שאלה או רעיון? שאל ב-Discussions — נשמח לשמוע ממך!
מה איכות התרגום בדף זה?

כתיבת לוגים וטיפול בשגיאות

הגדירו את קוד ה-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 ארוכים.

השלבים הבאים​

המלצות