Pular para o conteúdo principal
Este projeto está em fase beta. Encontrou um bug? Abra uma issue no GitHub. Tem uma pergunta ou ideia? Pergunte no Discussions — adoraríamos receber seu feedback!
Como está a qualidade da tradução nesta página?

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 trabalhoNo 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.1000 a cada 10 iterações, em vez de a cada iteração.

  • Registre exceções com logger.exception() dentro de um bloco except. 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. Passe disable=True para tqdm ou 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 raise final mantém o traceback nos logs e termina o job com o status ERROR.

  • Crie uma subclasse de ServerlessError para nomear um grupo de erros. O handler envia o nome da classe, como InputError, como o campo exception.

  • Outras exceções também chegam ao handler. Elas recebem o código 1, e o repr() delas vira a mensagem, como ValueError('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 retorna False.

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​

Recomendações