Tratamento de erros no SDK do Azure para C++

A manipulação de erros no SDK do Azure para C++ é implementada principalmente por meio de exceções C++. Essa abordagem está alinhada com as práticas padrão de C++ e permite relatórios e tratamento de erros claros em todo o SDK. Quando seu 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 recursos. O SDK captura esses erros como exceções que fornecem informações detalhadas sobre a falha.

Hierarquia de exceção

Principais tipos de exceção

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 padrão de C++ base da qual as exceções específicas do Azure herdamos.

  2. Azure::Core::RequestFailedException - Derivado de std::runtime_error, isto é 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 é lançada quando uma solicitação para um serviço do Azure falha. Dispõe o seguinte:

    • Código de estado 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 - Derivada de std::runtime_error, esta exceção é lançada quando uma operação é cancelada, normalmente através de um objeto de contexto.

  4. Azure::Core::Http::TransportException - Derivada de Azure::Core::RequestFailedException, esta exceção é lançada quando há um erro na camada de transporte HTTP, como falhas de conexão.

  5. Azure::Core::Credentials::AuthenticationException - Derivada do std::exception, essa exceção é lançada 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 de armazenamento. Esta 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 para operações de Hubs de Eventos. Inclui:

    • Condição de erro (um valor simbólico do Advanced Message Queuing Protocol (AMQP))
    • Descrição do erro
    • Código de estado
    • 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 acrescenta 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ções

Usando 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. Veja um exemplo com 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
    }
}

Tratamento de 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 Hubs de Eventos, fornecem informações sobre se um erro é transitório, permitindo a lógica de novas tentativas:

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 internas de repetição para falhas transitórias, mas poderá ainda assim querer tratar casos específicos no código da sua aplicação.

Tratamento de erros específicos do serviço

Para serviços de armazenamento como Blobs, Ficheiros e Filas, pode tratar de erros com base tanto em códigos de erro como em códigos de estado 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 Cofre de Chaves, 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 roscas

O SDK do Azure para C++ garante que os métodos de instância do cliente sejam seguros para execução simultânea 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, lembre-se:

  1. Os 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, que pode ser usada quando precisar passar informações de exceção entre threads