Grave logs e trate erros
Configure o código do seu fluxo de trabalho para gravar logs, relatar erros ao seu notebook local e salvar o trabalho parcial quando um job for cancelado. Para ler essas saídas no seu notebook local, consulte Execute um fluxo de trabalho gerenciado.
| No fluxo de trabalho | No seu notebook local |
|---|---|
Grave com um logger do módulo logging ou chame print(). | job.logs() retorna as linhas. |
| Gere uma exceção ou saia com um código diferente de zero. | job.status() retorna ERROR, e job.logs() mostra o traceback. |
Chame send_error(). | job.error_message() e job.events(type="ERROR") retornam o código, a mensagem e os detalhes. |
Chame save_result() em um handler de SIGTERM. | job.result() retorna o resultado parcial de um job cancelado. |
Grave logs
Tudo o que o fluxo de trabalho grava na saída padrão ou no erro padrão vai para os logs do job. Leia os logs com job.logs() enquanto o job é executado ou depois que ele termina.
Configure um logger
Use o módulo logging da biblioteca padrão do Python. logging.basicConfig() grava no erro padrão, que chega aos logs sem atraso. O formato do exemplo a seguir adiciona um carimbo de data/hora a cada linha:
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 oculta as mensagens INFO de outros pacotes, e logger.setLevel(logging.INFO) mantém as mensagens INFO do seu próprio logger. qiskit_ibm_runtime define o próprio nível com a variável de ambiente QISKIT_IBM_RUNTIME_LOG_LEVEL.
print() também chega aos logs, mas a saída padrão usa buffer, então as linhas impressas podem não aparecer até o job terminar. Se você usar print(), passe flush=True.
Defina o nível de log para cada execução
Para depurar uma execução sem alterar o código, passe o nível de log como um argumento:
run_workflow_remotely.ipynb
job = my_workflow.run(circuits=circuits, log_level="DEBUG")
Para habilitar isso, defina o nível do seu logger a partir do argumento:
source_files/my_workflow.py
from qiskit_serverless import get_arguments
arguments = get_arguments()
logger.setLevel(arguments.get("log_level", "INFO"))
Se o fluxo de trabalho tiver um esquema de argumentos, declare log_level nele, por exemplo como {"enum": ["DEBUG", "INFO", "WARNING"]}.
Prática recomendada: decida o que registrar
Registre o suficiente para reconstruir uma execução a partir dos logs:
-
No início, registre o que a execução recebeu. Registre o nome do Backend, o número de circuitos e as outras configurações que mudam entre execuções.
-
Registre o início e o fim de cada etapa. Use mensagens fixas que você possa pesquisar.
-
Registre as iterações em um intervalo fixo. Por exemplo, registre uma linha como
iteration 10/200, energy=-1.1000a cada 10 iterações, em vez de a cada iteração. -
Registre exceções com
logger.exception()dentro de um blocoexcept. A linha inclui o traceback completo, mesmo que o fluxo de trabalho se recupere do erro. -
Desative as barras de progresso. Os logs armazenam cada atualização de uma barra de progresso, como a de
tqdm, como uma linha separada. Passedisable=Trueparatqdmou registre em um intervalo. -
Mantenha os logs pequenos. Não registre dados grandes, como cada shot. Um fluxo de trabalho gerenciado mantém apenas os 50 MB mais recentes de logs para cada job.
-
Use as palavras "error" e "exception" apenas em mensagens sobre falhas. Se um job com falha não relatou seu erro, a exceção que
job.result()gera contém apenas as linhas de log que incluem essas palavras.
Prática recomendada: relate erros com send_error()
Quando o fluxo de trabalho gera uma exceção, o job termina com o status ERROR, e o traceback vai para os logs. Um fluxo de trabalho gerenciado não armazena o erro, nem mesmo para um ServerlessError. job.error_message() retorna {}, e a exceção que job.result() gera contém apenas as linhas de log que incluem error ou exception. As verificações de tipos e intervalos de argumentos pertencem a um esquema de argumentos, que rejeita uma execução incorreta antes de o job começar.
Relate todo erro que interrompe o fluxo de trabalho com send_error(), para que seu notebook local receba um erro curto com um código e detalhes:
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() gera uma QiskitServerlessException com o mesmo texto.
Para habilitar isso, gere ServerlessError onde o erro acontece e envolva o código do seu ponto de entrada em um bloco try que envia qualquer erro com send_error(). Um fluxo de trabalho adiciona esse handler às Catalog Functions, mas não aos fluxos de trabalho gerenciados:
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
-
Gere o erro novamente. O
raisefinal mantém o traceback nos logs e termina o job com o statusERROR. -
Crie uma subclasse de
ServerlessErrorpara nomear um grupo de erros. O handler envia o nome da classe, comoInputError, como o campoexception. -
Outras exceções também chegam ao handler. Elas recebem o código
1, e orepr()delas vira a mensagem, comoValueError('bad shots').
Reportar um problema e continuar
send_error() não interrompe o fluxo de trabalho nem altera o status dele, então você também pode reportar problemas dos quais o fluxo de trabalho se recupera, como cada circuito que ele pula:
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") retorna todos os erros que o fluxo de trabalho reportou, incluindo os erros de um job que terminou com o status DONE. Veja Encontre a causa de um erro.
-
Passe números e strings em
args.error_message()mostra apenas o primeiro item de uma lista. -
Verifique o valor de retorno. Se um fluxo de trabalho não consegue armazenar o erro, por exemplo porque o código roda fora de um job,
send_error()grava o erro nos logs e retornaFalse.
Limpar recursos quando um job é cancelado
Quando você cancela um job, o fluxo de trabalho envia SIGTERM ao seu entrypoint, envia os logs finais e desliga o ambiente. Sem um handler, o Python para imediatamente, e os blocos finally não são executados. O fluxo de trabalho também tenta cancelar os jobs e as sessões de QPU que ele iniciou com get_runtime_service(). Veja Cancelar jobs de QPU vinculados.
Para manter o trabalho parcial, registre um handler para SIGTERM. Assim, seu notebook local obtém o resultado parcial de um job cancelado:
run_workflow_remotely.ipynb
job.cancel()
print(job.result()) # {'status': 'canceled', 'partial_results': [...]}
Para habilitar isso, adicione o handler no início do seu entrypoint:
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() no handler gera SystemExit no código que está em execução, então os blocos finally são executados antes de o script terminar. O job ainda termina com o status CANCELED.
Mantenha o handler curto, porque o fluxo de trabalho desliga o ambiente logo depois do SIGTERM. Para execuções longas, salve também checkpoints, para que uma execução posterior possa continuar depois que um job falhar ou atingir o limite de 24 horas. Veja Prática recomendada: faça checkpoint de jobs longos.
Próximos passos
- Execute um fluxo de trabalho gerenciado para iniciar um job e ler seu status, logs e resultado.
- Gerencie arquivos entre execuções para salvar checkpoints a partir dos quais uma execução posterior possa continuar.
- Escolha um perfil de computação para executar seu fluxo de trabalho com mais recursos de CPU, memória ou GPU.