Tratamento de erros no SDK do Azure para C++

O tratamento de erros no SDK do Azure para C++ é implementado principalmente por meio de exceções do C++. Essa abordagem se alinha às práticas padrão do C++ e permite relatórios de erros e tratamento claros em todo o SDK. Quando o aplicativo C++ interage com os serviços do Azure, as operações podem falhar por vários motivos, como problemas de autenticação, indisponibilidade do serviço, solicitações inválidas ou restrições de recurso. O SDK captura esses erros como exceções que fornecem informações detalhadas sobre a falha.

Hierarquia de exceções

Tipos de exceção principais

O SDK do Azure para C++ usa uma hierarquia de classes de exceção, sendo as mais importantes:

  1. std::runtime_error - A exceção base padrão C++ da qual as exceções específicas do Azure herdam.

  2. Azure::Core::RequestFailedException – Derivado de std::runtime_error, essa é a exceção base para todas as falhas de solicitação de serviço do Azure. Definida em azure/core/exception.hpp, essa exceção é gerada quando uma solicitação para um serviço do Azure falha. Ele fornece:

    • Código de status de HTTP
    • Códigos de erro do serviço
    • Mensagens de erro
    • IDs de solicitação para solução de problemas
    • A resposta HTTP bruta
  3. Azure::Core::OperationCancelledException – Derivado de std::runtime_error, essa exceção é gerada quando uma operação é cancelada, normalmente por meio de um objeto de contexto.

  4. Azure::Core::Http::TransportException – Derivado de Azure::Core::RequestFailedException, essa exceção é gerada quando há um erro na camada de transporte HTTP, como falhas de conexão.

  5. Azure::Core::Credentials::AuthenticationException – Derivada de std::exception, essa exceção é gerada quando a autenticação com os serviços do Azure falha.

Tipos de exceção específicos do serviço

Diferentes serviços do Azure estendem os tipos de exceção base para fornecer informações de erro específicas do serviço:

  1. Azure::Storage::StorageException - Estende RequestFailedException com outras informações específicas do armazenamento. Essa exceção inclui:

    • Códigos de erro específicos do armazenamento
    • Informações adicionais no corpo da resposta
    • Detalhes sobre a operação de armazenamento com falha
  2. Azure::Messaging::EventHubs::EventHubsException - Uma exceção específica às operações dos Hubs de Eventos. Ele inclui:

    • Condição de erro (um valor simbólico do AMQP (Advanced Message Queuing Protocol))
    • Descrição do erro
    • Código de status
    • Informações sobre se o erro é transitório

Informações de erro em exceções

A RequestFailedException classe contém informações avançadas sobre falhas de serviço:

class RequestFailedException : public std::runtime_error {
public:
    // The entire HTTP raw response
    std::unique_ptr<Azure::Core::Http::RawResponse> RawResponse;
    
    // The HTTP response code
    Azure::Core::Http::HttpStatusCode StatusCode;
    
    // The HTTP reason phrase from the response
    std::string ReasonPhrase;
    
    // The client request header (x-ms-client-request-id) from the HTTP response
    std::string ClientRequestId;
    
    // The request ID header (x-ms-request-id) from the HTTP response
    std::string RequestId;
    
    // The error code from service returned in the HTTP response
    std::string ErrorCode;
    
    // The error message from the service returned in the HTTP response
    std::string Message;
    
    /* ... constructors and other methods ... */
};

Exceções específicas do serviço podem adicionar campos extras. Por exemplo, StorageException adiciona AdditionalInformation:

struct StorageException final : public Azure::Core::RequestFailedException {
    // Some storage-specific information in response body
    std::map<std::string, std::string> AdditionalInformation;
    
    /* ... constructors and other methods ... */
};

Exemplos e padrões de tratamento de exceção

Usar códigos de erro

As exceções de serviço contêm ErrorCode valores que podem ser usados para tomar decisões sobre como lidar com falhas. Aqui está um exemplo com os serviços de Armazenamento:

try {
    containerClient.Delete();
}
catch (Azure::Storage::StorageException& e) {
    if (e.ErrorCode == "ContainerNotFound") {
        // Ignore the error if the container does not exist
    }
    else {
        // Handle other errors here
    }
}

Tratar exceções básicas

Padrão básico para lidar com exceções no SDK do Azure:

try {
    // Perform an Azure SDK operation
    result = client.SomeOperation();
}
catch (Azure::Core::RequestFailedException const& e) {
    std::cout << "Request Failed Exception happened:" << std::endl << e.what() << std::endl;
    if (e.RawResponse) {
        std::cout << "Error Code: " << e.ErrorCode << std::endl;
        std::cout << "Error Message: " << e.Message << std::endl;
    }
    // Handle or rethrow as appropriate
}
catch (std::exception const& e) {
    std::cout << "Other exception: " << e.what() << std::endl;
    // Handle general exceptions
}

Tratamento de erros transitórios

Alguns serviços, como os Hubs de Eventos, fornecem informações sobre se um erro é transitório, permitindo a lógica de repetição:

try {
    // EventHubs operation
}
catch (Azure::Messaging::EventHubs::EventHubsException& e) {
    if (e.IsTransient) {
        // Retry the operation after a delay
    }
    else {
        // Handle permanent failure
    }
}

O SDK implementa políticas de repetição internas para falhas transitórias, mas talvez você ainda queira lidar com casos específicos no código do aplicativo.

Tratamento de erros específico do serviço

Para serviços de armazenamento, como Blobs, Arquivos e Filas, você pode lidar com erros com base tanto em códigos de erro quanto em códigos de status HTTP:

try {
    shareClient.Delete();
}
catch (Azure::Storage::StorageException& e) {
    if (e.ErrorCode == "ShareNotFound") {
        // Ignore the error if the file share does not exist
    }
    else if (e.StatusCode == Azure::Core::Http::HttpStatusCode::Conflict) {
        // Handle conflict error (e.g., resource in use)
        std::cout << "Conflict error: " << e.Message << std::endl;
        
        // Check additional information
        for (auto const& info : e.AdditionalInformation) {
            std::cout << info.first << ": " << info.second << std::endl;
        }
    }
    else {
        // Handle other errors based on status code or error code
        std::cout << "Error: " << e.Message << " (Code: " << e.ErrorCode << ")" << std::endl;
    }
}

Para operações do Key Vault, talvez seja necessário lidar com exceções de autenticação separadamente:

try {
    // Key Vault operation
}
catch (Azure::Core::Credentials::AuthenticationException const& e) {
    std::cout << "Authentication Exception happened:" << std::endl << e.what() << std::endl;
    // Handle authentication failure (e.g., invalid credentials)
}
catch (Azure::Core::RequestFailedException const& e) {
    std::cout << "Key Vault Client Exception happened:" << std::endl << e.Message << std::endl;
    // Handle Key Vault specific errors
}

Considerações sobre segurança de thread

O SDK do Azure para C++ garante que os métodos de instância do cliente sejam thread-safe e independentes uns dos outros. Isso significa que você pode usar com segurança uma instância de cliente em vários threads sem sincronização.

Ao lidar com exceções entre threads, tenha em mente:

  1. Objetos de exceção não devem ser compartilhados entre threads, a menos que sejam sincronizados corretamente
  2. O RequestFailedException inclui um construtor de cópia que cria uma cópia profunda, a qual pode ser usada ao precisar passar informações de exceção entre threads.