-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Client core: logging exceptions best practices
This guidance extends the general Azure SDK exception policy.
-
DO log exceptions when creating new instances of exceptions.
For example, write
// good throw logger.throwableAtError().log("This is a test exception", CoreException::from);
instead of
// bad throw new RuntimeException("This is a test exception");
-
It's not required to log trivial input parameter validation errors.
For example, write
public MyClientOptions setName(String name) { // this will throw NullPointerException implicitly Objects.requireNotNull("'name' cannot be null."); if (name.length() == 0) { throw new IllegalArgumentException("'name' cannot be empty"); } }
DO log exceptions in non-trivial cases, for example when validating network responses.
-
DO add all important context about the error into the exception message and log record using
logger.throwableAt*().addKeyValue(key, value)
methods.For example, write
// good try { uploadBlob(containerId, blobId, data); } catch (RuntimeException ex) { throw logger.throwableAtError() .addKeyValuePair("blobId", blobId) .addKeyValuePair("containerId", containerId) .addKeyValuePair("endpoint", endpoint) .log("Error when uploading blob", ex, CoreException::from); }
This code produces an exception message similar to
io.clientcore.core.models.CoreException$CoreExceptionImpl: Error when uploading blob; {"endpoint":"www.xyz.com", "blobId":"foo", "containerId":"bar","cause.type":"java.net.UnknownHostException", "cause.message":"Unable to resolve host www.xyz.com"}
which is consistent with the structured log record
{"message":"Error when uploading blob", "endpoint":"www.xyz.com", "exception.type":"io.clientcore.core.models.CoreException.CoreExceptionImpl", "blobId":"foo", "containerId":"bar", "cause.type":"java.net.UnknownHostException", "cause.message":"Unable to resolve host www.xyz.com"}
Avoid providing dynamic messages - this makes log analysis and grepping more complicated.
-
DO log exceptions that you're rethrowing only if they came from an external dependency. DO make the best effort not to log the same exception as it bubbles up through the stack trace.
For example, write:
// good try { return createIfNotExist(resourceId); } catch (HttpResponseException ex) { if (ex.getResponse().getStatusCode() == 409) { return ex.getResponse(); } // HttpResponseException should be logged at creation time, no need to log it again // unless you're wrapping it into a new error or want to provide additional // details throw ex; }
-
DO NOT create exceptions that you aren't throwing:
For example, don't create fake exception causes. Write
// good throw logger.throwableAtError().log("File does not exist", CoreException::from);
instead of
// bad throw logger.throwableAtError().log("File does not exist", new FileNotFoundException(), CoreException::from); throw UncheckedIOException(new FileNotFoundException());
You can always log error without exception:
// good logger.atError() .addKeyValue("providerType", providerType) .addKeyValue("supportedTypes", KNOWN_TYPES) .log("Unknown provider type.");
- Frequently Asked Questions
- Azure Identity Examples
- Configuration
- Performance Tuning
- Android Support
- Unit Testing
- Test Proxy Migration
- Azure Json Migration
- New Checkstyle and Spotbugs pattern migration
- Protocol Methods
- TypeSpec-Java Quickstart
- Getting Started Guidance
- Adding a Module
- Building
- Writing Performance Tests
- Working with AutoRest
- Deprecation
- BOM guidelines
- Release process
- Access helpers