From 60bc8b8e5efa97c7b7c7e24da2a8cc0e92403259 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Sat, 20 Nov 2021 21:47:26 -0800 Subject: [PATCH 01/17] Switch to structured logging in amqp-core --- .../azure/core/amqp/AmqpShutdownSignal.java | 13 +- .../ActiveClientTokenManager.java | 69 +++++--- .../implementation/AmqpChannelProcessor.java | 69 ++++---- .../implementation/AmqpExceptionHandler.java | 4 +- .../amqp/implementation/AmqpLoggingUtils.java | 33 ++++ .../AzureTokenManagerProvider.java | 7 +- .../amqp/implementation/ClientConstants.java | 12 ++ .../implementation/ManagementChannel.java | 16 +- .../implementation/ReactorConnection.java | 147 +++++++++-------- .../implementation/ReactorDispatcher.java | 40 ++--- .../amqp/implementation/ReactorExecutor.java | 42 +++-- .../amqp/implementation/ReactorReceiver.java | 100 ++++++++---- .../amqp/implementation/ReactorSender.java | 113 ++++++------- .../amqp/implementation/ReactorSession.java | 153 +++++++++++------- .../RequestResponseChannel.java | 98 +++++------ .../handler/ConnectionHandler.java | 67 ++++---- .../handler/CustomIOHandler.java | 16 +- .../amqp/implementation/handler/Handler.java | 26 +-- .../implementation/handler/LinkHandler.java | 32 ++-- .../handler/ReactorHandler.java | 12 +- .../handler/ReceiveLinkHandler.java | 83 ++++++---- .../handler/SendLinkHandler.java | 74 ++++++--- .../handler/SessionHandler.java | 82 +++++----- .../handler/WebSocketsConnectionHandler.java | 14 +- .../WebSocketsProxyConnectionHandler.java | 38 +++-- .../AmqpChannelProcessorTest.java | 3 +- .../implementation/ManagementChannelTest.java | 3 +- .../implementation/ReactorSenderTest.java | 1 + .../implementation/handler/HandlerTest.java | 11 +- .../handler/LinkHandlerTest.java | 14 +- .../java/com/azure/core/util/FluxUtil.java | 13 ++ 31 files changed, 829 insertions(+), 576 deletions(-) create mode 100644 sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java index a105e8416fb2..e9e3c9665b2a 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java @@ -3,6 +3,8 @@ package com.azure.core.amqp; +import com.azure.core.util.logging.LoggingEventBuilder; + import java.util.Locale; /** @@ -46,8 +48,17 @@ public boolean isInitiatedByClient() { return isInitiatedByClient; } + public LoggingEventBuilder addContext(LoggingEventBuilder logBuilder) { + return logBuilder + .addKeyValue("isTransient", isTransient) + .addKeyValue("isInitiatedByClient", isInitiatedByClient) + .addKeyValue("shutdownMessage", message); + } + /** - * {@inheritDoc} + * Returns String representing this {@code AmqpShutdownSignal} signal. + * + * To write logs, please use {@link AmqpShutdownSignal#addContext} instead. */ @Override public String toString() { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java index 3270a2f8f8ef..e9e3dce79ee8 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java @@ -19,11 +19,14 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; + /** * Manages the re-authorization of the client to the token audience against the CBS node. */ public class ActiveClientTokenManager implements TokenManager { - private final ClientLogger logger = new ClientLogger(ActiveClientTokenManager.class); + private final ClientLogger logger; private final AtomicBoolean hasScheduled = new AtomicBoolean(); private final AtomicBoolean hasDisposed = new AtomicBoolean(); private final Mono cbsNode; @@ -39,6 +42,7 @@ public ActiveClientTokenManager(Mono cbsNode, String to this.cbsNode = cbsNode; this.tokenAudience = tokenAudience; this.scopes = scopes; + this.logger = new ClientLogger(ActiveClientTokenManager.class); } /** @@ -76,12 +80,14 @@ public Mono authorize() { // If this is the first time authorize is called, the task will not have been scheduled yet. if (!hasScheduled.getAndSet(true)) { - logger.info("Scheduling refresh token task. scopes[{}]", scopes); + logger.atInfo() + .addKeyValue("scopes", scopes) + .log("Scheduling refresh token task."); final Duration firstInterval = Duration.ofMillis(refreshIntervalMS); lastRefreshInterval.set(firstInterval); authorizationResults.emitNext(AmqpResponseCode.ACCEPTED, (signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not emit ACCEPTED.", signalType, emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult).log("Could not emit ACCEPTED."); return false; }); @@ -99,11 +105,13 @@ public void close() { } authorizationResults.emitComplete((signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not close authorizationResults.", signalType, emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult).log("Could not close authorizationResults."); + return false; }); durationSource.emitComplete((signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not close durationSource.", signalType, emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult).log("Could not close durationSource."); + return false; }); @@ -115,15 +123,19 @@ public void close() { private Disposable scheduleRefreshTokenTask(Duration initialRefresh) { // EmitterProcessor can queue up an initial refresh interval before any subscribers are received. durationSource.emitNext(initialRefresh, (signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not emit initial refresh interval.", signalType, - emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult).log(" Could not emit initial refresh interval."); + return false; }); return Flux.switchOnNext(durationSource.asFlux().map(Flux::interval)) .takeUntil(duration -> hasDisposed.get()) .flatMap(delay -> { - logger.info("Refreshing token. scopes[{}] ", scopes); + + logger.atInfo() + .addKeyValue("scopes", scopes) + .log("Refreshing token."); + return authorize(); }) .onErrorContinue( @@ -131,20 +143,26 @@ private Disposable scheduleRefreshTokenTask(Duration initialRefresh) { (amqpException, interval) -> { final Duration lastRefresh = lastRefreshInterval.get(); - logger.error("Error is transient. Rescheduling authorization task at interval {} ms. scopes[{}]", - lastRefresh.toMillis(), scopes, amqpException); + logger.atError() + .addKeyValue("scopes", scopes) + .log("Error is transient. Rescheduling authorization task at interval {} ms", lastRefresh.toMillis(), amqpException); + durationSource.emitNext(lastRefresh, (signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not emit lastRefresh[{}].", signalType, - emitResult, lastRefresh); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .addKeyValue("lastRefresh", lastRefresh) + .log("Could not emit."); return false; }); }) .subscribe(interval -> { - logger.verbose("Authorization successful. Refreshing token in {} ms. scopes[{}]", interval, scopes); + logger.atVerbose() + .addKeyValue("scopes", scopes) + .log("Authorization successful. Refreshing token in {} ms.", interval); + authorizationResults.emitNext(AmqpResponseCode.ACCEPTED, (signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not emit ACCEPTED after refresh.", signalType, - emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .log("Could not emit ACCEPTED after refresh."); return false; }); @@ -152,29 +170,32 @@ private Disposable scheduleRefreshTokenTask(Duration initialRefresh) { lastRefreshInterval.set(nextRefresh); durationSource.emitNext(nextRefresh, (signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not emit nextRefresh[{}].", signalType, - emitResult, nextRefresh); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .addKeyValue("nextRefresh", nextRefresh) + .log("Could not emit nextRefresh."); return false; }); }, error -> { - logger.error("Error occurred while refreshing token that is not retriable. Not scheduling" - + " refresh task. Use ActiveClientTokenManager.authorize() to schedule task again. audience[{}]" - + " scopes[{}]", tokenAudience, scopes, error); + logger.atError() + .addKeyValue("scopes", scopes) + .addKeyValue("audience", tokenAudience) + .log("Error occurred while refreshing token that is not retriable. Not scheduling" + + " refresh task. Use ActiveClientTokenManager.authorize() to schedule task again.", error); // This hasn't been disposed yet. if (!hasDisposed.getAndSet(true)) { hasScheduled.set(false); durationSource.emitComplete((signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not close durationSource.", signalType, - emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .log("Could not close durationSource."); return false; }); authorizationResults.emitError(error, (signalType, emitResult) -> { - logger.verbose("signalType[{}] result[{}] Could not emit authorization error.", signalType, - emitResult, error); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .log("Could not emit authorization error.", error); return false; }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index 7273b753fead..0f95feaec32a 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -9,6 +9,7 @@ import com.azure.core.amqp.exception.AmqpException; import com.azure.core.util.AsyncCloseable; import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.ClientLogger; import org.reactivestreams.Processor; import org.reactivestreams.Subscription; import reactor.core.CoreSubscriber; @@ -18,6 +19,7 @@ import reactor.core.publisher.Operators; import java.time.Duration; +import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.RejectedExecutionException; @@ -26,6 +28,9 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Function; +import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.FULLY_QUALIFIED_NAMESPACE_KEY; + public class AmqpChannelProcessor extends Mono implements Processor, CoreSubscriber, Disposable { @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater UPSTREAM = @@ -40,8 +45,6 @@ public class AmqpChannelProcessor extends Mono implements Processor, private final Object lock = new Object(); private final AmqpRetryPolicy retryPolicy; - private final String fullyQualifiedNamespace; - private final String entityPath; private final Function> endpointStatesFunction; private final AmqpErrorContext errorContext; @@ -53,14 +56,13 @@ public class AmqpChannelProcessor extends Mono implements Processor, private volatile Disposable retrySubscription; public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, - Function> endpointStatesFunction, AmqpRetryPolicy retryPolicy, ClientLogger logger) { - this.fullyQualifiedNamespace = Objects - .requireNonNull(fullyQualifiedNamespace, "'fullyQualifiedNamespace' cannot be null."); - this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); + Function> endpointStatesFunction, AmqpRetryPolicy retryPolicy, String loggerName) { this.endpointStatesFunction = Objects.requireNonNull(endpointStatesFunction, "'endpointStates' cannot be null."); this.retryPolicy = Objects.requireNonNull(retryPolicy, "'retryPolicy' cannot be null."); - this.logger = Objects.requireNonNull(logger, "'logger' cannot be null."); + this.logger = new ClientLogger(loggerName, Map.of( + FULLY_QUALIFIED_NAMESPACE_KEY, Objects.requireNonNull(fullyQualifiedNamespace, "'fullyQualifiedNamespace' cannot be null."), + ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null."))); this.errorContext = new AmqpErrorContext(fullyQualifiedNamespace); } @@ -77,7 +79,7 @@ public void onSubscribe(Subscription subscription) { @Override public void onNext(T amqpChannel) { - logger.info("namespace[{}] entityPath[{}]: Setting next AMQP channel.", fullyQualifiedNamespace, entityPath); + logger.info("Setting next AMQP channel."); Objects.requireNonNull(amqpChannel, "'amqpChannel' cannot be null."); @@ -90,8 +92,7 @@ public void onNext(T amqpChannel) { currentChannel = amqpChannel; final ConcurrentLinkedDeque> currentSubscribers = subscribers; - logger.info("namespace[{}] entityPath[{}]: Next AMQP channel received, updating {} current " - + "subscribers", fullyQualifiedNamespace, entityPath, subscribers.size()); + logger.info("Next AMQP channel received, updating {} current subscribers", subscribers.size()); currentSubscribers.forEach(subscription -> subscription.onNext(amqpChannel)); @@ -100,8 +101,7 @@ public void onNext(T amqpChannel) { // Connection was successfully opened, we can reset the retry interval. if (state == AmqpEndpointState.ACTIVE) { retryAttempts.set(0); - logger.info("namespace[{}] entityPath[{}]: Channel is now active.", - fullyQualifiedNamespace, entityPath); + logger.info("Channel is now active."); } }, error -> { @@ -110,11 +110,9 @@ public void onNext(T amqpChannel) { }, () -> { if (isDisposed()) { - logger.info("namespace[{}] entityPath[{}]: Channel is disposed.", - fullyQualifiedNamespace, entityPath); + logger.info("Channel is disposed."); } else { - logger.info("namespace[{}] entityPath[{}]: Channel is closed. Requesting upstream. ", - fullyQualifiedNamespace, entityPath); + logger.info("Channel is closed. Requesting upstream."); setAndClearChannel(); requestUpstream(); } @@ -140,8 +138,7 @@ public void onError(Throwable throwable) { Objects.requireNonNull(throwable, "'throwable' is required."); if (isRetryPending.get() && retryPolicy.calculateRetryDelay(throwable, retryAttempts.get()) != null) { - logger.warning("namespace[{}] entityPath[{}]: Retry is already pending. Ignoring transient error.", - fullyQualifiedNamespace, entityPath, throwable); + logger.warning("Retry is already pending. Ignoring transient error.", throwable); return; } @@ -183,24 +180,20 @@ public void onError(Throwable throwable) { return; } - logger.info("namespace[{}] entityPath[{}]: Retry #{}. Transient error occurred. Retrying after {} ms.", - fullyQualifiedNamespace, entityPath, attempts, retryInterval.toMillis(), throwable); + logger.info("Retry #{}. Transient error occurred. Retrying after {} ms.", attempts, retryInterval.toMillis(), throwable); retrySubscription = Mono.delay(retryInterval).subscribe(i -> { if (isDisposed()) { - logger.info("namespace[{}] entityPath[{}]: Retry #{}. Not requesting from upstream. Processor is disposed.", - fullyQualifiedNamespace, entityPath, attempts); + logger.info("Retry #{}. Not requesting from upstream. Processor is disposed.", attempts); } else { - logger.info("namespace[{}] entityPath[{}]: Retry #{}. Requesting from upstream.", - fullyQualifiedNamespace, entityPath, attempts); + logger.info("Retry #{}. Requesting from upstream.", attempts); requestUpstream(); isRetryPending.set(false); } }); } else { - logger.warning("namespace[{}] entityPath[{}]: Retry #{}. Retry attempts exhausted or exception was not retriable.", - fullyQualifiedNamespace, entityPath, attempts, throwable); + logger.warning("Retry #{}. Retry attempts exhausted or exception was not retriable.", attempts, throwable); lastError = throwable; isDisposed.set(true); @@ -209,8 +202,7 @@ public void onError(Throwable throwable) { synchronized (lock) { final ConcurrentLinkedDeque> currentSubscribers = subscribers; subscribers = new ConcurrentLinkedDeque<>(); - logger.info("namespace[{}] entityPath[{}]: Error in AMQP channel processor. Notifying {} subscribers.", - fullyQualifiedNamespace, entityPath, currentSubscribers.size()); + logger.info("Error in AMQP channel processor. Notifying {} subscribers.", currentSubscribers.size()); currentSubscribers.forEach(subscriber -> subscriber.onError(throwable)); } @@ -225,8 +217,7 @@ public void onComplete() { synchronized (lock) { final ConcurrentLinkedDeque> currentSubscribers = subscribers; subscribers = new ConcurrentLinkedDeque<>(); - logger.info("namespace[{}] entityPath[{}]: AMQP channel processor completed. Notifying {} " - + "subscribers.", fullyQualifiedNamespace, entityPath, currentSubscribers.size()); + logger.info("AMQP channel processor completed. Notifying {} subscribers.", currentSubscribers.size()); currentSubscribers.forEach(subscriber -> subscriber.onComplete()); } } @@ -238,9 +229,7 @@ public void subscribe(CoreSubscriber actual) { actual.onSubscribe(Operators.emptySubscription()); actual.onError(lastError); } else { - Operators.error(actual, logger.logExceptionAsError(new IllegalStateException( - String.format("namespace[%s] entityPath[%s]: Cannot subscribe. Processor is already terminated.", - fullyQualifiedNamespace, entityPath)))); + Operators.error(actual, logger.atError().log(new IllegalStateException("Cannot subscribe. Processor is already terminated."))); } return; @@ -257,8 +246,7 @@ public void subscribe(CoreSubscriber actual) { } subscribers.add(subscriber); - logger.verbose("namespace[{}] entityPath[{}] subscribers[{}]: Added a subscriber.", - fullyQualifiedNamespace, entityPath, subscribers.size()); + logger.atVerbose().addKeyValue("subscribers", subscribers.size()).log("Added a subscriber."); if (!isRetryPending.get()) { requestUpstream(); @@ -289,25 +277,22 @@ public boolean isDisposed() { private void requestUpstream() { if (currentChannel != null) { - logger.verbose("namespace[{}] entityPath[{}]: Connection exists, not requesting another.", - fullyQualifiedNamespace, entityPath); + logger.verbose("Connection exists, not requesting another."); return; } else if (isDisposed()) { - logger.verbose("namespace[{}] entityPath[{}]: Is already disposed.", fullyQualifiedNamespace, entityPath); + logger.verbose("Is already disposed."); return; } final Subscription subscription = UPSTREAM.get(this); if (subscription == null) { - logger.warning("namespace[{}] entityPath[{}]: There is no upstream subscription.", - fullyQualifiedNamespace, entityPath); + logger.warning("There is no upstream subscription."); return; } // subscribe(CoreSubscriber) may have requested a subscriber already. if (!isRequested.getAndSet(true)) { - logger.info("namespace[{}] entityPath[{}]: Connection not requested, yet. Requesting one.", - fullyQualifiedNamespace, entityPath); + logger.info("Connection not requested, yet. Requesting one."); subscription.request(1); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java index 88ee6c052f83..fa02f50883bd 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java @@ -5,6 +5,7 @@ import com.azure.core.amqp.AmqpShutdownSignal; import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.ClientLogger; /** * Handles exceptions generated by AMQP connections, sessions, and/or links. @@ -33,6 +34,7 @@ void onConnectionError(Throwable exception) { * @param shutdownSignal The shutdown signal that was received. */ void onConnectionShutdown(AmqpShutdownSignal shutdownSignal) { - logger.info("Shutdown received: {}", shutdownSignal); + shutdownSignal + .addContext(logger.atInfo()).log("Shutdown received"); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java new file mode 100644 index 000000000000..d00d778047b7 --- /dev/null +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java @@ -0,0 +1,33 @@ +package com.azure.core.amqp.implementation; + +import com.azure.core.util.logging.LoggingEventBuilder; +import org.apache.qpid.proton.amqp.transport.ErrorCondition; +import reactor.core.publisher.SignalType; +import reactor.core.publisher.Sinks; + +import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.ERROR_CONDITION_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.ERROR_DESCRIPTION_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; +import static com.azure.core.amqp.implementation.ClientConstants.SIGNAL_TYPE_KEY; + +public class AmqpLoggingUtils { + public static LoggingEventBuilder addSignalTypeAndResult(LoggingEventBuilder logBuilder, SignalType signalType, Sinks.EmitResult result) { + return logBuilder + .addKeyValue(SIGNAL_TYPE_KEY, signalType) + .addKeyValue(EMIT_RESULT_KEY, result); + } + + public static LoggingEventBuilder addErrorCondition(LoggingEventBuilder logBuilder, ErrorCondition errorCondition) { + if (errorCondition == null) { + return logBuilder + .addKeyValue(ERROR_CONDITION_KEY, NOT_APPLICABLE) + .addKeyValue(ERROR_DESCRIPTION_KEY, NOT_APPLICABLE); + } + + return logBuilder + .addKeyValue(ERROR_CONDITION_KEY, errorCondition.getCondition()) + .addKeyValue(ERROR_DESCRIPTION_KEY, errorCondition.getDescription()); + } +} + diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AzureTokenManagerProvider.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AzureTokenManagerProvider.java index 9641fb0ef688..fe4488f92fc1 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AzureTokenManagerProvider.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AzureTokenManagerProvider.java @@ -48,7 +48,10 @@ public TokenManager getTokenManager(Mono cbsNodeMono, S final String scopes = getScopesFromResource(resource); final String tokenAudience = String.format(Locale.US, TOKEN_AUDIENCE_FORMAT, fullyQualifiedNamespace, resource); - logger.verbose("Creating new token manager for audience[{}], resource[{}]", tokenAudience, resource); + logger.atVerbose() + .addKeyValue("audience", tokenAudience) + .addKeyValue("resource", resource) + .log("Creating new token manager."); return new ActiveClientTokenManager(cbsNodeMono, tokenAudience, scopes); } @@ -63,7 +66,7 @@ public String getScopesFromResource(String resource) { } else if (CbsAuthorizationType.SHARED_ACCESS_SIGNATURE.equals(authorizationType)) { return String.format(Locale.US, TOKEN_AUDIENCE_FORMAT, fullyQualifiedNamespace, resource); } else { - throw logger.logExceptionAsError(new IllegalArgumentException(String.format(Locale.US, + throw logger.atError().log(new IllegalArgumentException(String.format(Locale.US, "'%s' is not supported authorization type for token audience.", authorizationType))); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java index 885d70d3b14d..04210a669653 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java @@ -12,6 +12,18 @@ public final class ClientConstants { // Base sleep wait time. public static final Duration SERVER_BUSY_WAIT_TIME = Duration.ofSeconds(4); + // Logging context keys + public final static String CONNECTION_ID_KEY = "connectionId"; + public final static String LINK_NAME_KEY = "linkName"; + public final static String ENTITY_PATH_KEY = "entityPath"; + public final static String SESSION_NAME_KEY = "sessionName"; + public final static String FULLY_QUALIFIED_NAMESPACE_KEY = "namespace"; + public final static String ERROR_CONDITION_KEY = "errorCondition"; + public final static String ERROR_DESCRIPTION_KEY = "errorDescription"; + public final static String EMIT_RESULT_KEY = "emitResult"; + public final static String SIGNAL_TYPE_KEY = "signalType"; + public final static String HOSTNAME_KEY = "hostName"; + /** * The default maximum allowable size, in bytes, for a batch to be sent. */ diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ManagementChannel.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ManagementChannel.java index 23b560745666..853a99384519 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ManagementChannel.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ManagementChannel.java @@ -12,13 +12,20 @@ import com.azure.core.amqp.models.AmqpAnnotatedMessage; import com.azure.core.amqp.models.DeliveryOutcome; import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.amqp.transport.DeliveryState; import org.apache.qpid.proton.message.Message; +import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; import reactor.core.publisher.SynchronousSink; +import java.util.Map; import java.util.Objects; +import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.ERROR_CONDITION_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.ERROR_DESCRIPTION_KEY; + /** * AMQP node responsible for performing management and metadata operations on an Azure AMQP message broker. */ @@ -34,8 +41,8 @@ public ManagementChannel(AmqpChannelProcessor createChan this.createChannel = Objects.requireNonNull(createChannel, "'createChannel' cannot be null."); this.fullyQualifiedNamespace = Objects.requireNonNull(fullyQualifiedNamespace, "'fullyQualifiedNamespace' cannot be null."); - this.logger = new ClientLogger(String.format("%s<%s>", ManagementChannel.class.getName(), entityPath)); this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); + this.logger = new ClientLogger(ManagementChannel.class, Map.of(ENTITY_PATH_KEY, entityPath)); this.tokenManager = Objects.requireNonNull(tokenManager, "'tokenManager' cannot be null."); } @@ -104,8 +111,11 @@ private void handleResponse(Message response, SynchronousSink sessionMap = new ConcurrentHashMap<>(); private final ConcurrentHashMap managementNodes = new ConcurrentHashMap<>(); @@ -101,6 +113,7 @@ public ReactorConnection(String connectionId, ConnectionOptions connectionOption this.connectionOptions = connectionOptions; this.reactorProvider = reactorProvider; this.connectionId = connectionId; + this.logger = new ClientLogger(ReactorConnection.class, Map.of(CONNECTION_ID_KEY, connectionId)); this.handlerProvider = handlerProvider; this.tokenManagerProvider = Objects.requireNonNull(tokenManagerProvider, "'tokenManagerProvider' cannot be null."); @@ -123,38 +136,33 @@ public ReactorConnection(String connectionId, ConnectionOptions connectionOption return activeEndpoint.thenReturn(reactorConnection); }) .doOnError(error -> { - final String message = String.format( - "connectionId[%s] Error occurred while connection was starting. Error: %s", connectionId, error); - if (isDisposed.getAndSet(true)) { - logger.verbose("connectionId[{}] was already disposed. {}", connectionId, message); + logger.atVerbose() + .log("Connection was already disposed: Error occurred while connection was starting.", error); } else { - closeAsync(new AmqpShutdownSignal(false, false, message)).subscribe(); + closeAsync(new AmqpShutdownSignal(false, false, String.format( + "Error occurred while connection was starting. Error: %s", error))).subscribe(); } }); this.endpointStates = this.handler.getEndpointStates() .takeUntilOther(shutdownSignalSink.asMono()) .map(state -> { - logger.verbose("connectionId[{}]: State {}", connectionId, state); + logger.verbose("State {}", state); return AmqpEndpointStateUtil.getConnectionState(state); }) .onErrorResume(error -> { if (!isDisposed.getAndSet(true)) { - logger.verbose("connectionId[{}]: Disposing of active sessions due to error.", connectionId); - return closeAsync(new AmqpShutdownSignal(false, false, - error.getMessage())).then(Mono.error(error)); + logger.verbose("Disposing of active sessions due to error."); + return closeAsync(new AmqpShutdownSignal(false, false, error.getMessage())).then(Mono.error(error)); } else { return Mono.error(error); } }) .doOnComplete(() -> { if (!isDisposed.getAndSet(true)) { - logger.verbose("connectionId[{}]: Disposing of active sessions due to connection close.", - connectionId); - - closeAsync(new AmqpShutdownSignal(false, false, - "Connection handler closed.")).subscribe(); + logger.verbose("Disposing of active sessions due to connection close."); + closeAsync(new AmqpShutdownSignal(false, false, "Connection handler closed.")).subscribe(); } }) .cache(1); @@ -185,9 +193,7 @@ public Flux getShutdownSignals() { public Mono getManagementNode(String entityPath) { return Mono.defer(() -> { if (isDisposed()) { - return Mono.error(logger.logExceptionAsError(new IllegalStateException(String.format( - "connectionId[%s]: Connection is disposed. Cannot get management instance for '%s'", - connectionId, entityPath)))); + return monoError(logger.atError().addKeyValue(ENTITY_PATH_KEY, entityPath), new IllegalStateException("Connection is disposed. Cannot get management instance.")); } final AmqpManagementNode existing = managementNodes.get(entityPath); @@ -201,7 +207,7 @@ public Mono getManagementNode(String entityPath) { return tokenManager.authorize().thenReturn(managementNodes.compute(entityPath, (key, current) -> { if (current != null) { - logger.info("A management node exists already, returning it."); + logger.atInfo().addKeyValue(ENTITY_PATH_KEY, entityPath).log("A management node exists already, returning it."); // Close the token manager we had created during this because it is unneeded now. tokenManager.close(); @@ -212,8 +218,11 @@ public Mono getManagementNode(String entityPath) { final String linkName = entityPath + "-" + MANAGEMENT_LINK_NAME; final String address = entityPath + "/" + MANAGEMENT_ADDRESS; - logger.info("Creating management node. entityPath[{}], address[{}], linkName[{}]", - entityPath, address, linkName); + logger.atInfo() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue("address", address) + .log("Creating management node."); final AmqpChannelProcessor requestResponseChannel = createRequestResponseChannel(sessionName, linkName, address); @@ -281,8 +290,9 @@ public Mono createSession(String sessionName) { return; } - logger.info("connectionId[{}] sessionName[{}]: Error occurred. Removing and disposing" - + " session.", connectionId, sessionName, error); + logger.atInfo() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("Error occurred. Removing and disposing session", error); removeSession(key); }, () -> { // If we were already disposing of the connection, the session would be removed. @@ -290,8 +300,9 @@ public Mono createSession(String sessionName) { return; } - logger.verbose("connectionId[{}] sessionName[{}]: Complete. Removing and disposing session.", - connectionId, sessionName); + logger.atVerbose() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("connectionId[{}] sessionName[{}]: Complete. Removing and disposing session."); removeSession(key); }); @@ -389,35 +400,37 @@ protected AmqpChannelProcessor createRequestResponseChan entityPath, reactorSession.session(), connectionOptions.getRetry(), handlerProvider, reactorProvider, messageSerializer, senderSettleMode, receiverSettleMode)) .doOnNext(e -> { - logger.info("connectionId[{}] entityPath[{}] linkName[{}] Emitting new response channel.", - getId(), entityPath, linkName); + logger.atInfo() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName) + .log("Emitting new response channel."); }) .repeat(); return createChannel .subscribeWith(new AmqpChannelProcessor<>(connectionId, entityPath, - channel -> channel.getEndpointStates(), retryPolicy, - new ClientLogger(RequestResponseChannel.class.getName() + ":" + entityPath))); + channel -> channel.getEndpointStates(), retryPolicy, RequestResponseChannel.class.getName())); } @Override public Mono closeAsync() { if (isDisposed.getAndSet(true)) { - logger.verbose("connectionId[{}] Was already closed. Not disposing again.", connectionId); + logger.verbose("Connection was already closed. Not disposing again."); return isClosedMono.asMono(); } - return closeAsync(new AmqpShutdownSignal(false, true, - "Disposed by client.")); + return closeAsync(new AmqpShutdownSignal(false, true, "Disposed by client.")); } Mono closeAsync(AmqpShutdownSignal shutdownSignal) { - logger.info("connectionId[{}] signal[{}]: Disposing of ReactorConnection.", connectionId, shutdownSignal); - + shutdownSignal.addContext(logger.atInfo()).log("Disposing of ReactorConnection."); final Sinks.EmitResult result = shutdownSignalSink.tryEmitValue(shutdownSignal); + if (result.isFailure()) { // It's possible that another one was already emitted, so it's all good. - logger.info("connectionId[{}] signal[{}] result[{}] Unable to emit shutdown signal.", connectionId, result); + shutdownSignal.addContext(logger.atInfo()) + .addKeyValue(EMIT_RESULT_KEY, result) + .log("Unable to emit shutdown signal."); } final Mono cbsCloseOperation; @@ -431,21 +444,19 @@ Mono closeAsync(AmqpShutdownSignal shutdownSignal) { Flux.fromStream(managementNodes.values().stream()).flatMap(node -> node.closeAsync())); final Mono closeReactor = Mono.fromRunnable(() -> { - logger.verbose("connectionId[{}] Scheduling closeConnection work.", connectionId); + logger.verbose("Scheduling closeConnection work."); final ReactorDispatcher dispatcher = reactorProvider.getReactorDispatcher(); if (dispatcher != null) { try { dispatcher.invoke(() -> closeConnectionWork()); } catch (IOException e) { - logger.warning("connectionId[{}] IOException while scheduling closeConnection work. Manually " - + "disposing.", connectionId, e); + logger.warning("IOException while scheduling closeConnection work. Manually disposing", e); closeConnectionWork(); } catch (RejectedExecutionException e) { // Not logging error here again because we have to log the exception when we throw it. - logger.info("connectionId[{}] Could not schedule closeConnection work. Manually disposing.", - connectionId); + logger.info("Could not schedule closeConnection work. Manually disposing."); closeConnectionWork(); } @@ -456,23 +467,24 @@ Mono closeAsync(AmqpShutdownSignal shutdownSignal) { return Mono.whenDelayError( cbsCloseOperation.doFinally(signalType -> - logger.verbose("connectionId[{}] signalType[{}] Closed CBS node.", connectionId, signalType)), + logger.atVerbose() + .addKeyValue(SIGNAL_TYPE_KEY, signalType) + .log("Closed CBS node.")), managementNodeCloseOperations.doFinally(signalType -> - logger.verbose("connectionId[{}] signalType[{}] Closed management nodes.", connectionId, - signalType))) - - + logger.atVerbose() + .log("Closed management nodes."))) .then(closeReactor.doFinally(signalType -> - logger.verbose("connectionId[{}] signalType[{}] Closed reactor dispatcher.", connectionId, - signalType))) + logger.atVerbose() + .addKeyValue(SIGNAL_TYPE_KEY, signalType) + .log("Closed reactor dispatcher."))) .then(isClosedMono.asMono()); } private synchronized void closeConnectionWork() { if (connection == null) { isClosedMono.emitEmpty((signalType, emitResult) -> { - logger.info("connectionId[{}] signal[{}] result[{}] Unable to complete closeMono.", - connectionId, signalType, emitResult); + addSignalTypeAndResult(logger.atInfo(), signalType, emitResult) + .log("Unable to complete closeMono."); return false; }); @@ -490,7 +502,7 @@ private synchronized void closeConnectionWork() { // remaining work after OperationTimeout has elapsed and closes afterwards. final Mono closedExecutor = executor != null ? Mono.defer(() -> { synchronized (this) { - logger.info("connectionId[{}] Closing executor.", connectionId); + logger.info("Closing executor."); return executor.closeAsync(); } }) : Mono.empty(); @@ -499,14 +511,14 @@ private synchronized void closeConnectionWork() { final Mono closeSessionAndExecutorMono = Mono.when(closingSessions) .timeout(operationTimeout) .onErrorResume(error -> { - logger.info("connectionId[{}]: Timed out waiting for all sessions to close.", connectionId); + logger.info("Timed out waiting for all sessions to close."); return Mono.empty(); }) .then(closedExecutor) .then(Mono.fromRunnable(() -> { isClosedMono.emitEmpty((signalType, result) -> { - logger.warning("connectionId[{}] signal[{}] result[{}]: Unable to emit connection closed signal", - connectionId, signalType, result); + addSignalTypeAndResult(logger.atWarning(), signalType, result) + .log("Unable to emit connection closed signal."); return false; }); @@ -531,8 +543,10 @@ private synchronized ClaimsBasedSecurityNode getOrCreateCBSNode() { private synchronized Connection getOrCreateConnection() throws IOException { if (connection == null) { - logger.info("connectionId[{}]: Creating and starting connection to {}:{}", connectionId, - handler.getHostname(), handler.getProtocolPort()); + logger.atInfo() + .addKeyValue(HOSTNAME_KEY, handler.getHostname()) + .addKeyValue("port", handler.getProtocolPort()) + .log("Creating and starting connection."); final Reactor reactor = reactorProvider.createReactor(connectionId, handler.getMaxFrameSize()); connection = reactor.connectionToHost(handler.getHostname(), handler.getProtocolPort(), handler); @@ -587,13 +601,15 @@ private ReactorExceptionHandler() { @Override public void onConnectionError(Throwable exception) { - logger.info( - "onConnectionError connectionId[{}], hostName[{}], message[Starting new reactor], error[{}]", - getId(), getFullyQualifiedNamespace(), exception.getMessage(), exception); + logger.atInfo() + .addKeyValue(FULLY_QUALIFIED_NAMESPACE_KEY, getFullyQualifiedNamespace()) + .log("onConnectionError, Starting new reactor", exception); if (!isDisposed.getAndSet(true)) { - logger.verbose("onReactorError connectionId[{}], hostName[{}]: Disposing.", connectionId, - getFullyQualifiedNamespace()); + logger.atVerbose() + .addKeyValue(FULLY_QUALIFIED_NAMESPACE_KEY, getFullyQualifiedNamespace()) + .log("onReactorError: Disposing."); + closeAsync(new AmqpShutdownSignal(false, false, "onReactorError: " + exception.toString())) .subscribe(); @@ -602,12 +618,15 @@ public void onConnectionError(Throwable exception) { @Override void onConnectionShutdown(AmqpShutdownSignal shutdownSignal) { - logger.info( - "onConnectionShutdown connectionId[{}], hostName[{}], message[Shutting down], shutdown signal[{}]", - getId(), getFullyQualifiedNamespace(), shutdownSignal.isInitiatedByClient(), shutdownSignal); + shutdownSignal.addContext(logger.atInfo()) + .addKeyValue(FULLY_QUALIFIED_NAMESPACE_KEY, getFullyQualifiedNamespace()) + .log("onConnectionShutdown. Shutting down."); if (!isDisposed.getAndSet(true)) { - logger.verbose("onConnectionShutdown connectionId[{}], hostName[{}]: disposing."); + logger.atVerbose() + .addKeyValue(FULLY_QUALIFIED_NAMESPACE_KEY, getFullyQualifiedNamespace()) + .log("onConnectionShutdown: disposing."); + closeAsync(shutdownSignal).subscribe(); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java index 3607803eba10..ac4ce4baefb3 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java @@ -6,6 +6,7 @@ import com.azure.core.amqp.AmqpShutdownSignal; import com.azure.core.amqp.implementation.handler.DispatchHandler; import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.reactor.Reactor; import org.apache.qpid.proton.reactor.Selectable; @@ -18,11 +19,14 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.Pipe; import java.time.Duration; +import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; + /** * The following utility class is used to generate an event to hook into {@link Reactor}'s event delegation pattern. It * uses a {@link Pipe} as the IO on which Reactor listens to. @@ -40,7 +44,7 @@ *

*/ public final class ReactorDispatcher { - private final ClientLogger logger = new ClientLogger(ReactorDispatcher.class); + private final ClientLogger logger; private final CloseHandler onClose; private final String connectionId; private final Reactor reactor; @@ -65,7 +69,7 @@ public ReactorDispatcher(final String connectionId, final Reactor reactor, final this.workQueue = new ConcurrentLinkedQueue<>(); this.onClose = new CloseHandler(); this.workScheduler = new WorkScheduler(); - + this.logger = new ClientLogger(ReactorDispatcher.class , Map.of(CONNECTION_ID_KEY, connectionId)); // The Proton-J reactor goes quiescent when there is no work to do, and it only wakes up when a Selectable (by // default, the network connection) signals that data is available. // @@ -125,7 +129,7 @@ private void throwIfSchedulerError() { final RejectedExecutionException rejectedException = this.reactor.attachments() .get(RejectedExecutionException.class, RejectedExecutionException.class); if (rejectedException != null) { - throw logger.logExceptionAsWarning(new RejectedExecutionException( + throw logger.atWarning().log(new RejectedExecutionException( "Underlying Reactor was already disposed. Should not continue dispatching work to this. " + rejectedException.getMessage(), rejectedException)); } @@ -133,7 +137,7 @@ private void throwIfSchedulerError() { // throw when the pipe is in closed state - in which case, // signalling the new event-dispatch will fail if (!this.ioSignal.sink().isOpen()) { - throw logger.logExceptionAsWarning(new RejectedExecutionException( + throw logger.atWarning().log(new RejectedExecutionException( "ReactorDispatcher instance is closed. Should not continue dispatching work to this reactor.")); } } @@ -146,14 +150,13 @@ private void signalWorkQueue() throws IOException { } } catch (ClosedChannelException ignorePipeClosedDuringReactorShutdown) { if (!isClosed.get()) { - logger.warning("connectionId[{}] signalWorkQueue failed before reactor closed.", - connectionId, ignorePipeClosedDuringReactorShutdown); + logger.atWarning() + .log("signalWorkQueue failed before reactor closed.", ignorePipeClosedDuringReactorShutdown); shutdownSignal.emitError(new RuntimeException(String.format( "connectionId[%s] IO Sink was interrupted before reactor closed.", connectionId), ignorePipeClosedDuringReactorShutdown), Sinks.EmitFailureHandler.FAIL_FAST); } else { - logger.verbose("connectionId[{}] signalWorkQueue failed with an error after closed. Can be ignored.", - connectionId, ignorePipeClosedDuringReactorShutdown); + logger.verbose("signalWorkQueue failed with an error after closed. Can be ignored.", ignorePipeClosedDuringReactorShutdown); } } } @@ -179,21 +182,20 @@ public void run(Selectable selectable) { } } catch (ClosedChannelException ignorePipeClosedDuringReactorShutdown) { if (!isClosed.get()) { - logger.warning("connectionId[{}] WorkScheduler.run() failed before reactor was closed.", - connectionId, ignorePipeClosedDuringReactorShutdown); + logger.atWarning() + .log("WorkScheduler.run() failed before reactor was closed.", ignorePipeClosedDuringReactorShutdown); shutdownSignal.emitError(new RuntimeException(String.format( "connectionId[%s] IO Source was interrupted before reactor closed.", connectionId), ignorePipeClosedDuringReactorShutdown), Sinks.EmitFailureHandler.FAIL_FAST); } else { - logger.verbose("connectionId[{}] WorkScheduler.run() failed with an error. Can be ignored.", - connectionId, ignorePipeClosedDuringReactorShutdown); + logger.atVerbose() + .log("WorkScheduler.run() failed with an error. Can be ignored.", ignorePipeClosedDuringReactorShutdown); } break; } catch (IOException ioException) { - shutdownSignal.emitError(logger.logExceptionAsError(new RuntimeException( - String.format("connectionId[%s] WorkScheduler.run() failed with an error.", connectionId), - ioException)), Sinks.EmitFailureHandler.FAIL_FAST); + shutdownSignal.emitError(logger.atError().log(new RuntimeException( + "WorkScheduler.run() failed with an error.", ioException)), Sinks.EmitFailureHandler.FAIL_FAST); break; } @@ -221,7 +223,7 @@ public void run(Selectable selectable) { return; } - logger.info("connectionId[{}] Reactor selectable is being disposed.", connectionId); + logger.info("Reactor selectable is being disposed."); shutdownSignal.emitValue(new AmqpShutdownSignal(false, false, String.format("connectionId[%s] Reactor selectable is disposed.", connectionId)), @@ -232,8 +234,7 @@ public void run(Selectable selectable) { ioSignal.sink().close(); } } catch (IOException ioException) { - logger.error("connectionId[{}] CloseHandler.sink().close() failed with an error.", - connectionId, ioException); + logger.atError().log("CloseHandler.sink().close() failed with an error.", ioException); } workScheduler.run(null); @@ -243,8 +244,7 @@ public void run(Selectable selectable) { ioSignal.source().close(); } } catch (IOException ioException) { - logger.error("connectionId[{}] CloseHandler.source().close() failed with an error.", - connectionId, ioException); + logger.atError().log("CloseHandler.source().close() failed with an error.", ioException); } } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java index 6a7d80476ea1..8aee5ae47dbc 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java @@ -18,18 +18,20 @@ import java.nio.channels.UnresolvedAddressException; import java.time.Duration; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; + /** * Schedules the proton-j reactor to continuously run work. */ class ReactorExecutor implements AsyncCloseable { - private static final String LOG_MESSAGE = "connectionId[{}] message[{}]"; - - private final ClientLogger logger = new ClientLogger(ReactorExecutor.class); + private final ClientLogger logger; private final AtomicBoolean hasStarted = new AtomicBoolean(); private final AtomicBoolean isDisposed = new AtomicBoolean(); private final Sinks.Empty isClosedMono = Sinks.empty(); @@ -37,7 +39,6 @@ class ReactorExecutor implements AsyncCloseable { private final Object lock = new Object(); private final Reactor reactor; private final Scheduler scheduler; - private final String connectionId; private final Duration timeout; private final AmqpExceptionHandler exceptionHandler; private final String hostname; @@ -46,10 +47,10 @@ class ReactorExecutor implements AsyncCloseable { Duration timeout, String hostname) { this.reactor = Objects.requireNonNull(reactor, "'reactor' cannot be null."); this.scheduler = Objects.requireNonNull(scheduler, "'scheduler' cannot be null."); - this.connectionId = Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); this.timeout = Objects.requireNonNull(timeout, "'timeout' cannot be null."); this.exceptionHandler = Objects.requireNonNull(exceptionHandler, "'exceptionHandler' cannot be null."); this.hostname = Objects.requireNonNull(hostname, "'hostname' cannot be null."); + this.logger = new ClientLogger(ReactorExecutor.class, Map.of(CONNECTION_ID_KEY, Objects.requireNonNull(connectionId, "'connectionId' cannot be null."))); } /** @@ -67,7 +68,7 @@ void start() { return; } - logger.info(LOG_MESSAGE, connectionId, "Starting reactor."); + logger.info("Starting reactor."); reactor.start(); scheduler.schedule(this::run); } @@ -79,8 +80,7 @@ void start() { private void run() { // If this hasn't been disposed of, and we're trying to run work items on it, log a warning and return. if (!isDisposed.get() && !hasStarted.get()) { - logger.warning(LOG_MESSAGE, connectionId, - "Cannot run work items on ReactorExecutor if ReactorExecutor.start() has not been invoked."); + logger.warning("Cannot run work items on ReactorExecutor if ReactorExecutor.start() has not been invoked."); return; } @@ -97,8 +97,7 @@ private void run() { scheduler.schedule(this::run); rescheduledReactor = true; } catch (RejectedExecutionException exception) { - logger.warning(LOG_MESSAGE, connectionId, - "Scheduling reactor failed because the scheduler has been shut down.", exception); + logger.warning("Scheduling reactor failed because the scheduler has been shut down.", exception); this.reactor.attachments() .set(RejectedExecutionException.class, RejectedExecutionException.class, exception); @@ -109,8 +108,7 @@ private void run() { ? handlerException : handlerException.getCause(); - logger.warning(LOG_MESSAGE, connectionId, - "Unhandled exception while processing events in reactor, report this error.", handlerException); + logger.warning("Unhandled exception while processing events in reactor, report this error.", handlerException); final String message = !CoreUtils.isNullOrEmpty(cause.getMessage()) ? cause.getMessage() @@ -137,14 +135,14 @@ private void run() { } finally { if (!rescheduledReactor) { if (hasStarted.getAndSet(false)) { - logger.verbose(LOG_MESSAGE, connectionId, "Scheduling reactor to complete pending tasks."); + logger.verbose("Scheduling reactor to complete pending tasks."); scheduleCompletePendingTasks(); } else { final String reason = "Stopping the reactor because thread was interrupted or the reactor has no more events to " + "process."; - logger.info(LOG_MESSAGE, connectionId, reason); + logger.info(reason); close(reason, true); } } @@ -156,18 +154,15 @@ private void run() { */ private void scheduleCompletePendingTasks() { final Runnable work = () -> { - logger.info(LOG_MESSAGE, connectionId, "Processing all pending tasks and closing old reactor."); + logger.info("Processing all pending tasks and closing old reactor."); try { if (reactor.process()) { - logger.verbose(LOG_MESSAGE, connectionId, - "Had more tasks to process on reactor but it is shutting down."); + logger.verbose("Had more tasks to process on reactor but it is shutting down."); } reactor.stop(); } catch (HandlerException e) { - logger.warning(LOG_MESSAGE, connectionId, - StringUtil.toStackTraceString(e, "scheduleCompletePendingTasks - exception occurred while " - + "processing events.")); + logger.atWarning().log(() -> StringUtil.toStackTraceString(e, "scheduleCompletePendingTasks - exception occurred while processing events.")); } finally { try { reactor.free(); @@ -183,17 +178,16 @@ private void scheduleCompletePendingTasks() { try { this.scheduler.schedule(work, timeout.toMillis(), TimeUnit.MILLISECONDS); } catch (RejectedExecutionException e) { - logger.warning(LOG_MESSAGE, connectionId, "Scheduler was already closed. Manually releasing reactor."); + logger.warning("Scheduler was already closed. Manually releasing reactor."); work.run(); } } private void close(String reason, boolean initiatedByClient) { - logger.verbose(LOG_MESSAGE, connectionId, "Completing close and disposing scheduler. {}", reason); + logger.verbose("Completing close and disposing scheduler. {}", reason); scheduler.dispose(); isClosedMono.emitEmpty((signalType, emitResult) -> { - logger.verbose("connectionId[{}] signalType[{}] emitResult[{}]: Unable to emit close event on reactor", - connectionId, signalType, emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult).log("Unable to emit close event on reactor"); return false; }); exceptionHandler.onConnectionShutdown(new AmqpShutdownSignal(false, initiatedByClient, reason)); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java index 086a395fc8e7..b34abf0d85d8 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java @@ -6,10 +6,13 @@ import com.azure.core.amqp.AmqpConnection; import com.azure.core.amqp.AmqpEndpointState; import com.azure.core.amqp.AmqpRetryOptions; +import com.azure.core.amqp.AmqpShutdownSignal; import com.azure.core.amqp.exception.AmqpErrorCondition; import com.azure.core.amqp.implementation.handler.ReceiveLinkHandler; import com.azure.core.util.AsyncCloseable; import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; @@ -19,6 +22,7 @@ import org.apache.qpid.proton.message.Message; import reactor.core.Disposable; import reactor.core.Disposables; +import reactor.core.Exceptions; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -26,12 +30,19 @@ import java.io.IOException; import java.io.UncheckedIOException; +import java.util.Map; import java.util.Objects; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; import static com.azure.core.util.FluxUtil.monoError; @@ -49,7 +60,7 @@ public class ReactorReceiver implements AmqpReceiveLink, AsyncCloseable, AutoClo private final Sinks.Empty isClosedMono = Sinks.empty(); private final Flux messagesProcessor; private final AmqpRetryOptions retryOptions; - private final ClientLogger logger = new ClientLogger(ReactorReceiver.class); + private final ClientLogger logger; private final Flux endpointStates; private final AtomicReference> creditSupplier = new AtomicReference<>(); @@ -62,6 +73,7 @@ protected ReactorReceiver(AmqpConnection amqpConnection, String entityPath, Rece this.handler = handler; this.tokenManager = tokenManager; this.dispatcher = dispatcher; + this.logger = new ClientLogger(ReactorReceiver.class, Map.of(CONNECTION_ID_KEY, handler.getConnectionId(), LINK_NAME_KEY, this.handler.getLinkName())); // Delivered messages are not published on another scheduler because we want the settlement method that happens // in decodeDelivery to take place and since proton-j is not thread safe, it could end up with hundreds of // backed up deliveries waiting to be settled. (Which, consequently, ends up in a FAIL_OVERFLOW error from @@ -83,12 +95,14 @@ protected ReactorReceiver(AmqpConnection amqpConnection, String entityPath, Rece final Integer credits = supplier.get(); if (credits != null && credits > 0) { - logger.info("connectionId[{}] linkName[{}] credits[{}] Adding credits.", - handler.getConnectionId(), getLinkName(), credits); + logger.atInfo() + .addKeyValue("credits", credits) + .log("Adding credits."); receiver.flow(credits); } else { - logger.verbose("connectionId[{}] linkName[{}] credits[{}] There are no credits to add.", - handler.getConnectionId(), getLinkName(), credits); + logger.atVerbose() + .addKeyValue("credits", credits) + .log("There are no credits to add."); } sink.success(message); @@ -102,16 +116,19 @@ protected ReactorReceiver(AmqpConnection amqpConnection, String entityPath, Rece this.retryOptions = retryOptions; this.endpointStates = this.handler.getEndpointStates() .map(state -> { - logger.verbose("connectionId[{}] entityPath[{}] linkName[{}] State {}", handler.getConnectionId(), - entityPath, getLinkName(), state); + logger.atVerbose() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log("State {}", state); return AmqpEndpointStateUtil.getConnectionState(state); }) .doOnError(error -> { final String message = isDisposed.getAndSet(true) ? "This was already disposed. Dropping error." : "Freeing resources due to error."; - logger.warning("connectionId[{}] entityPath[{}] linkName[{}] {}", - handler.getConnectionId(), entityPath, getLinkName(), message, error); + + logger.atInfo() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log(message); completeClose(); }) @@ -119,8 +136,10 @@ protected ReactorReceiver(AmqpConnection amqpConnection, String entityPath, Rece final String message = isDisposed.getAndSet(true) ? "This was already disposed." : "Freeing resources."; - logger.verbose("connectionId[{}] entityPath[{}] linkName[{}] {}", handler.getConnectionId(), - entityPath, getLinkName(), message); + + logger.atVerbose() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log(message); completeClose(); }) @@ -133,28 +152,27 @@ protected ReactorReceiver(AmqpConnection amqpConnection, String entityPath, Rece .onErrorResume(error -> { // When we encounter an error refreshing authorization results, close the receive link. final Mono operation = - closeAsync(String.format( - "connectionId[%s] linkName[%s] Token renewal failure. Disposing receive link.", - amqpConnection.getId(), getLinkName()), + closeAsync("Token renewal failure. Disposing receive link.", new ErrorCondition(Symbol.getSymbol(AmqpErrorCondition.NOT_ALLOWED.getErrorCondition()), error.getMessage())); return operation.then(Mono.empty()); - }).subscribe(response -> { - logger.verbose("connectionId[{}] linkName[{}] response[{}] Token refreshed.", - handler.getConnectionId(), getLinkName(), response); - }, error -> { + }).subscribe(response -> + logger.atVerbose() + .addKeyValue("response", response) + .log("Token refreshed.") + , error -> { }, () -> { - logger.verbose("connectionId[{}] entityPath[{}] linkName[{}] Authorization completed.", - handler.getConnectionId(), entityPath, getLinkName()); + logger.atVerbose() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log("Authorization completed."); closeAsync("Authorization completed. Disposing.", null).subscribe(); }), amqpConnection.getShutdownSignals().flatMap(signal -> { - logger.verbose("connectionId[{}] linkName[{}] Shutdown signal received.", handler.getConnectionId(), - getLinkName()); - + logger.atVerbose() + .log("Shutdown signal received."); return closeAsync("Connection shutdown.", null); }).subscribe()); //@formatter:on @@ -173,7 +191,7 @@ public Flux receive() { @Override public Mono addCredits(int credits) { if (isDisposed()) { - return monoError(logger, new IllegalStateException("Cannot add credits to closed link: " + getLinkName())); + return monoError(logger.atError(), Exceptions.propagate(new IllegalStateException("Cannot add credits to closed link: " + getLinkName()))); } return Mono.create(sink -> { @@ -262,10 +280,9 @@ Mono closeAsync(String message, ErrorCondition errorCondition) { return isClosedMono.asMono().publishOn(Schedulers.boundedElastic()); } - final String condition = errorCondition != null ? errorCondition.toString() : NOT_APPLICABLE; - logger.verbose("connectionId[{}], path[{}], linkName[{}] errorCondition[{}]: Setting error condition and " - + "disposing. {}", - handler.getConnectionId(), entityPath, getLinkName(), condition, message); + addErrorCondition(logger.atVerbose(), errorCondition) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log("Setting error condition and disposing. {}", message); final Runnable closeReceiver = () -> { if (receiver.getLocalState() != EndpointState.CLOSED) { @@ -281,16 +298,15 @@ Mono closeAsync(String message, ErrorCondition errorCondition) { try { dispatcher.invoke(closeReceiver); } catch (IOException e) { - logger.warning("connectionId[{}] linkName[{}] IO sink was closed when scheduling work. Manually " - + "invoking and completing close.", handler.getConnectionId(), getLinkName(), e); + logger.atWarning() + .log("IO sink was closed when scheduling work. Manually invoking and completing close.", e); closeReceiver.run(); completeClose(); } catch (RejectedExecutionException e) { // Not logging error here again because we have to log the exception when we throw it. - logger.info("connectionId[{}] linkName[{}] RejectedExecutionException when scheduling on " - + "ReactorDispatcher. Manually invoking and completing close.", handler.getConnectionId(), - getLinkName()); + logger.atInfo() + .log("RejectedExecutionException when scheduling on ReactorDispatcher. Manually invoking and completing close."); closeReceiver.run(); completeClose(); @@ -302,9 +318,11 @@ Mono closeAsync(String message, ErrorCondition errorCondition) { * Takes care of disposing of subscriptions, reactor resources after they've been closed. */ private void completeClose() { + isClosedMono.emitEmpty((signalType, result) -> { - logger.warning("connectionId[{}], signal[{}], result[{}]. Unable to emit shutdown signal.", - handler.getConnectionId(), signalType, result); + addSignalTypeAndResult(logger.atWarning(), signalType, result) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log("Unable to emit shutdown signal."); return false; }); @@ -318,6 +336,18 @@ private void completeClose() { receiver.free(); } + public LoggingEventBuilder addContext(LoggingEventBuilder logBuilder) { + return logBuilder + .addKeyValue(CONNECTION_ID_KEY, receiver.getName()) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, getLinkName()); + } + + /** + * Returns String representing this {@code ReactorReceiver} signal. + * + * To write logs, please use {@link ReactorReceiver#addContext} instead. + */ @Override public String toString() { return String.format("connectionId: [%s] entity path: [%s] linkName: [%s]", receiver.getName(), entityPath, diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java index ab3946443719..8048c1e0ab2f 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java @@ -46,6 +46,7 @@ import java.util.Comparator; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.PriorityQueue; import java.util.UUID; @@ -56,6 +57,10 @@ import java.util.concurrent.atomic.AtomicInteger; import static com.azure.core.amqp.exception.AmqpErrorCondition.NOT_ALLOWED; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.MAX_AMQP_HEADER_SIZE_BYTES; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; import static com.azure.core.amqp.implementation.ClientConstants.SERVER_BUSY_BASE_SLEEP_TIME_IN_SECS; @@ -65,6 +70,7 @@ * Handles scheduling and transmitting events through proton-j to Event Hubs service. */ class ReactorSender implements AmqpSendLink, AsyncCloseable, AutoCloseable { + private static final String DELIVERY_TAG_KEY = "deliveryTag"; private final String entityPath; private final Sender sender; private final SendLinkHandler handler; @@ -80,7 +86,7 @@ class ReactorSender implements AmqpSendLink, AsyncCloseable, AutoCloseable { private final ConcurrentHashMap pendingSendsMap = new ConcurrentHashMap<>(); private final PriorityQueue pendingSendsQueue = new PriorityQueue<>(1000, new DeliveryTagComparator()); - private final ClientLogger logger = new ClientLogger(ReactorSender.class); + private final ClientLogger logger; private final Flux endpointStates; private final TokenManager tokenManager; @@ -123,14 +129,16 @@ class ReactorSender implements AmqpSendLink, AsyncCloseable, AutoCloseable { this.retry = RetryUtil.getRetryPolicy(retryOptions); this.tokenManager = tokenManager; + String connectionId = handler.getConnectionId() == null ? NOT_APPLICABLE : handler.getConnectionId(); + String linkName = getLinkName() == null ? NOT_APPLICABLE : getLinkName(); + this.logger = new ClientLogger(ReactorSender.class, Map.of(CONNECTION_ID_KEY, connectionId, ENTITY_PATH_KEY, entityPath, LINK_NAME_KEY, linkName)); this.activeTimeoutMessage = String.format( "ReactorSender connectionId[%s] linkName[%s]: Waiting for send and receive handler to be ACTIVE", handler.getConnectionId(), handler.getLinkName()); this.endpointStates = this.handler.getEndpointStates() .map(state -> { - logger.verbose("connectionId[{}] entityPath[{}] linkName[{}]: State {}", handler.getConnectionId(), - entityPath, getLinkName(), state); + logger.verbose("State {}", state); this.hasConnected.set(state == EndpointState.ACTIVE); return AmqpEndpointStateUtil.getConnectionState(state); }) @@ -150,14 +158,13 @@ class ReactorSender implements AmqpSendLink, AsyncCloseable, AutoCloseable { this.handler.getDeliveredMessages().subscribe(this::processDeliveredMessage), this.handler.getLinkCredits().subscribe(credit -> { - logger.verbose("connectionId[{}] entityPath[{}] linkName[{}] credits[{}] Credits on link.", - handler.getConnectionId(), entityPath, getLinkName(), credit); + logger.atVerbose().addKeyValue("credits", credit) + .log("Credits on link."); this.scheduleWorkOnDispatcher(); }), amqpConnection.getShutdownSignals().flatMap(signal -> { - logger.verbose("connectionId[{}] linkName[{}]: Shutdown signal received.", handler.getConnectionId(), - getLinkName()); + logger.verbose("Shutdown signal received."); hasConnected.set(false); return closeAsync("Connection shutdown.", null); @@ -175,12 +182,11 @@ class ReactorSender implements AmqpSendLink, AsyncCloseable, AutoCloseable { return operation.then(Mono.empty()); }).subscribe(response -> { - logger.verbose("connectionId[{}] linkName[{}] response[{}] Token refreshed.", - handler.getConnectionId(), getLinkName(), response); + logger.atVerbose().addKeyValue("response", response) + .log("Token refreshed."); }, error -> { }, () -> { - logger.verbose("connectionId[{}] entityPath[{}] linkName[{}] Authorization completed. Disposing.", - handler.getConnectionId(), entityPath, getLinkName()); + logger.verbose(" Authorization completed. Disposing."); closeAsync("Authorization completed. Disposing.", null).subscribe(); })); @@ -332,9 +338,7 @@ public Mono getLinkSize() { if (remoteMaxMessageSize != null) { linkSize = remoteMaxMessageSize.intValue(); } else { - logger.warning("connectionId[{}], linkName[{}]: Could not get the getRemoteMaxMessageSize." - + " Returning current link size: {}", handler.getConnectionId(), handler.getLinkName(), - linkSize); + logger.warning("Could not get the getRemoteMaxMessageSize. Returning current link size: {}",linkSize); } return linkSize; @@ -385,10 +389,8 @@ Mono closeAsync(String message, ErrorCondition errorCondition) { return isClosedMono.asMono(); } - final String condition = errorCondition != null ? errorCondition.toString() : NOT_APPLICABLE; - logger.verbose("connectionId[{}], path[{}], linkName[{}] errorCondition[{}]. Setting error condition and " - + "disposing. {}", - handler.getConnectionId(), entityPath, getLinkName(), condition, message); + addErrorCondition(logger.atVerbose(), errorCondition) + .log("Setting error condition and disposing. {}", message); final Runnable closeWork = () -> { if (errorCondition != null && sender.getCondition() == null) { @@ -404,14 +406,12 @@ Mono closeAsync(String message, ErrorCondition errorCondition) { try { reactorProvider.getReactorDispatcher().invoke(closeWork); } catch (IOException e) { - logger.warning("connectionId[{}] entityPath[{}] linkName[{}]: Could not schedule close work. Running" - + " manually. And completing close.", handler.getConnectionId(), entityPath, getLinkName(), e); + logger.warning("Could not schedule close work. Running manually. And completing close.", e); closeWork.run(); handleClose(); } catch (RejectedExecutionException e) { - logger.info("connectionId[{}] entityPath[{}] linkName[{}]: RejectedExecutionException scheduling close" - + " work. And completing close.", handler.getConnectionId(), entityPath, getLinkName()); + logger.info("RejectedExecutionException scheduling close work. And completing close."); closeWork.run(); handleClose(); @@ -488,9 +488,9 @@ private void processSendWork() { if (workItem == null) { if (deliveryTag != null) { - logger.verbose( - "clientId[{}]. path[{}], linkName[{}], deliveryTag[{}]: sendData not found for this delivery.", - handler.getConnectionId(), entityPath, getLinkName(), deliveryTag); + logger.atVerbose() + .addKeyValue(DELIVERY_TAG_KEY, deliveryTag) + .log("sendData not found for this delivery."); } //TODO (conniey): Should we update to continue rather than break? @@ -519,18 +519,19 @@ private void processSendWork() { } if (linkAdvance) { - logger.verbose("entityPath[{}], linkName[{}], deliveryTag[{}]: Sent message", entityPath, - getLinkName(), deliveryTag); + logger.atVerbose() + .addKeyValue(DELIVERY_TAG_KEY, deliveryTag) + .log("Sent message."); workItem.setWaitingForAck(); scheduler.schedule(new SendTimeout(deliveryTag), retryOptions.getTryTimeout().toMillis(), TimeUnit.MILLISECONDS); } else { - logger.verbose( - "clientId[{}]. path[{}], linkName[{}], deliveryTag[{}], sentMessageSize[{}], " - + "payloadActualSize[{}]: sendlink advance failed", - handler.getConnectionId(), entityPath, getLinkName(), deliveryTag, sentMsgSize, - workItem.getEncodedMessageSize()); + logger.atVerbose() + .addKeyValue(DELIVERY_TAG_KEY, deliveryTag) + .addKeyValue("sentMessageSize", sentMsgSize) + .addKeyValue("payloadActualSize", workItem.getEncodedMessageSize()) + .log("Sendlink advance failed."); if (delivery != null) { delivery.free(); @@ -554,14 +555,17 @@ private void processDeliveredMessage(Delivery delivery) { final DeliveryState outcome = delivery.getRemoteState(); final String deliveryTag = new String(delivery.getTag(), UTF_8); - logger.verbose("entityPath[{}], linkName[{}], deliveryTag[{}]: process delivered message", - entityPath, getLinkName(), deliveryTag); + logger.atVerbose() + .addKeyValue(DELIVERY_TAG_KEY, deliveryTag) + .log("Process delivered message."); final RetriableWorkItem workItem = pendingSendsMap.remove(deliveryTag); if (workItem == null) { - logger.verbose("clientId[{}]. path[{}], linkName[{}], delivery[{}] - mismatch (or send timed out)", - handler.getConnectionId(), entityPath, getLinkName(), deliveryTag); + logger.atVerbose() + .addKeyValue(DELIVERY_TAG_KEY, deliveryTag) + .log("Mismatch (or send timed out."); + return; } else if (workItem.isDeliveryStateProvided()) { workItem.success(outcome); @@ -582,8 +586,10 @@ private void processDeliveredMessage(Delivery delivery) { final Exception exception = ExceptionUtil.toException(error.getCondition().toString(), error.getDescription(), handler.getErrorContext(sender)); - logger.warning("entityPath[{}], linkName[{}], deliveryTag[{}]: Delivery rejected. [{}]", - entityPath, getLinkName(), deliveryTag, rejected); + logger.atWarning() + .addKeyValue(DELIVERY_TAG_KEY, deliveryTag) + .addKeyValue("rejected", rejected) + .log("Delivery rejected."); final int retryAttempt; if (isGeneralSendError(error.getCondition())) { @@ -630,11 +636,10 @@ private void scheduleWorkOnDispatcher() { try { reactorProvider.getReactorDispatcher().invoke(this::processSendWork); } catch (IOException e) { - logger.warning("connectionId[{}] linkName[{}]: Error scheduling work on reactor.", - handler.getConnectionId(), getLinkName(), e); + logger.warning("Error scheduling work on reactor.", e); + } catch (RejectedExecutionException e) { - logger.info("connectionId[{}] linkName[{}]: Error scheduling work on reactor because of" - + " RejectedExecutionException.", handler.getConnectionId(), getLinkName()); + logger.info("Error scheduling work on reactor because of RejectedExecutionException."); } } @@ -645,8 +650,7 @@ private void cleanupFailedSend(final RetriableWorkItem workItem, final Exception private void completeClose() { isClosedMono.emitEmpty((signalType, result) -> { - logger.warning("connectionId[{}], signal[{}], result[{}]. Unable to emit shutdown signal.", - handler.getConnectionId(), signalType, result); + logger.warning("Unable to emit shutdown signal."); return false; }); @@ -664,11 +668,11 @@ private void completeClose() { */ private void handleError(Throwable error) { synchronized (pendingSendLock) { - final String logMessage = isDisposed.getAndSet(true) - ? "This was already disposed. Dropping error." - : String.format("Disposing of '%d' pending sends with error.", pendingSendsMap.size()); - logger.verbose("connectionId[{}] entityPath[{}] linkName[{}] {}", handler.getConnectionId(), entityPath, - getLinkName(), logMessage); + if (isDisposed.getAndSet(true)) { + logger.verbose("This was already disposed. Dropping error."); + } else { + logger.verbose("Disposing of '{}' pending sends with error.", pendingSendsMap.size()); + } pendingSendsMap.forEach((key, value) -> value.error(error)); pendingSendsMap.clear(); @@ -684,12 +688,11 @@ private void handleClose() { final AmqpErrorContext context = handler.getErrorContext(sender); synchronized (pendingSendLock) { - final String logMessage = isDisposed.getAndSet(true) - ? "This was already disposed." - : String.format("Disposing of '%d' pending sends.", pendingSendsMap.size()); - - logger.verbose("connectionId[{}] entityPath[{}] linkName[{}] {}", handler.getConnectionId(), entityPath, - getLinkName(), logMessage); + if (isDisposed.getAndSet(true)) { + logger.verbose("This was already disposed."); + } else { + logger.verbose("Disposing of '{}' pending sends.", pendingSendsMap.size()); + } pendingSendsMap.forEach((key, value) -> value.error(new AmqpException(true, message, context))); pendingSendsMap.clear(); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java index 3048850cd041..5e71fef941a6 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java @@ -18,6 +18,7 @@ import com.azure.core.amqp.implementation.handler.SendLinkHandler; import com.azure.core.amqp.implementation.handler.SessionHandler; import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.messaging.Source; import org.apache.qpid.proton.amqp.messaging.Target; @@ -50,7 +51,14 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; +import static com.azure.core.amqp.implementation.ClientConstants.SESSION_NAME_KEY; +import static com.azure.core.util.FluxUtil.monoError; /** * Represents an AMQP session using proton-j reactor. @@ -69,7 +77,7 @@ public class ReactorSession implements AmqpSession { */ private final Sinks.Empty isClosedMono = Sinks.empty(); - private final ClientLogger logger = new ClientLogger(ReactorSession.class); + private final ClientLogger logger; private final Flux endpointStates; private final AmqpConnection amqpConnection; @@ -120,10 +128,15 @@ public ReactorSession(AmqpConnection amqpConnection, Session session, SessionHan "ReactorSession connectionId[%s], session[%s]: Retries exhausted waiting for ACTIVE endpoint state.", sessionHandler.getConnectionId(), sessionName); + this.logger = new ClientLogger(ReactorSession.class, Map.of(CONNECTION_ID_KEY, this.sessionHandler.getConnectionId())); + this.endpointStates = sessionHandler.getEndpointStates() .map(state -> { - logger.verbose("connectionId[{}], sessionName[{}], state[{}]", sessionHandler.getConnectionId(), - sessionName, state); + logger.atVerbose() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .addKeyValue("state", state) + .log("Got endpoint state."); + return AmqpEndpointStateUtil.getConnectionState(state); }) .doOnError(error -> handleError(error)) @@ -210,9 +223,7 @@ public Mono rollbackTransaction(AmqpTransaction transaction) { @Override public Mono createProducer(String linkName, String entityPath, Duration timeout, AmqpRetryPolicy retry) { return createProducer(linkName, entityPath, timeout, retry, null) - .or(onClosedError(String.format( - "connectionId[%s] entityPath[%s] linkName[%s] Connection closed while waiting for new producer link.", - sessionHandler.getConnectionId(), entityPath, linkName))); + .or(onClosedError("Connection closed while waiting for new producer link.", entityPath, linkName)); } /** @@ -222,9 +233,7 @@ public Mono createProducer(String linkName, String entityPath, Duratio public Mono createConsumer(String linkName, String entityPath, Duration timeout, AmqpRetryPolicy retry) { return createConsumer(linkName, entityPath, timeout, retry, null, null, null, SenderSettleMode.UNSETTLED, ReceiverSettleMode.SECOND) - .or(onClosedError(String.format( - "connectionId[%s] entityPath[%s] linkName[%s] Connection closed while waiting for new receive link.", - sessionHandler.getConnectionId(), entityPath, linkName))) + .or(onClosedError("Connection closed while waiting for new receive link.", entityPath, linkName)) .cast(AmqpLink.class); } @@ -255,22 +264,23 @@ Mono closeAsync(String message, ErrorCondition errorCondition, boolean dis return isClosedMono.asMono(); } - final String condition = errorCondition != null ? errorCondition.toString() : NOT_APPLICABLE; - logger.verbose("connectionId[{}], sessionName[{}], errorCondition[{}]. Setting error condition and " - + "disposing session. {}", - sessionHandler.getConnectionId(), sessionName, condition, message != null ? message : ""); + addErrorCondition(logger.atVerbose(), errorCondition) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("Disposing session. {}", message != null ? message : ""); return Mono.fromRunnable(() -> { try { provider.getReactorDispatcher().invoke(() -> disposeWork(errorCondition, disposeLinks)); } catch (IOException e) { - logger.info("connectionId[{}] sessionName[{}] Error while scheduling work. Manually disposing.", - sessionHandler.getConnectionId(), sessionName, e); + addErrorCondition(logger.atInfo(), errorCondition) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("Error while scheduling work. Manually disposing.", e); disposeWork(errorCondition, disposeLinks); } catch (RejectedExecutionException e) { - logger.info("connectionId[{}] sessionName[{}] RejectedExecutionException when scheduling work.", - sessionHandler.getConnectionId(), sessionName); + logger.atInfo() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("RejectedExecutionException when scheduling work."); disposeWork(errorCondition, disposeLinks); } @@ -283,16 +293,18 @@ Mono closeAsync(String message, ErrorCondition errorCondition, boolean dis @Override public Mono getOrCreateTransactionCoordinator() { if (isDisposed()) { - return Mono.error(logger.logExceptionAsError(new AmqpException(true, String.format( - "connectionId[%s] sessionName[%s] Cannot create coordinator send link '%s' from a closed session.", - sessionHandler.getConnectionId(), sessionName, TRANSACTION_LINK_NAME), - sessionHandler.getErrorContext()))); + return Mono.error(logger.atError() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log(new AmqpException(true, + String.format("Cannot create coordinator send link %s from a closed session.", TRANSACTION_LINK_NAME), + sessionHandler.getErrorContext()))); } final TransactionCoordinator existing = transactionCoordinator.get(); if (existing != null) { - logger.verbose("connectionId[{}] coordinator[{}]: Returning existing transaction coordinator.", - sessionHandler.getConnectionId(), TRANSACTION_LINK_NAME); + logger.atVerbose() + .addKeyValue("coordinator", TRANSACTION_LINK_NAME) + .log("Returning existing transaction coordinator."); return Mono.just(existing); } @@ -306,9 +318,7 @@ public Mono getOrCreateTransactionCoordina return transactionCoordinator.get(); } }) - .or(onClosedError(String.format( - "connectionId[%s] Connection closed while waiting for transaction coordinator creation.", - sessionHandler.getConnectionId()))); + .or(onClosedError("Connection closed while waiting for transaction coordinator creation.", NOT_APPLICABLE, NOT_APPLICABLE)); } /** @@ -338,15 +348,20 @@ protected Mono createConsumer(String linkName, String entityPat ReceiverSettleMode receiverSettleMode) { if (isDisposed()) { - return Mono.error(logger.logExceptionAsError(new AmqpException(true, String.format( - "connectionId[%s] sessionName[%s] entityPath[%s] linkName[%s] Cannot create receive link from a closed" - + " session.", sessionHandler.getConnectionId(), sessionName, entityPath, linkName), - sessionHandler.getErrorContext()))); + LoggingEventBuilder logBuilder = logger.atError() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName); + + return monoError(logBuilder, new AmqpException(true, "Cannot create receive link from a closed session.", sessionHandler.getErrorContext())); } final LinkSubscription existingLink = openReceiveLinks.get(linkName); if (existingLink != null) { - logger.info("linkName[{}] entityPath[{}] Returning existing receive link.", linkName, entityPath); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log("Returning existing receive link."); return Mono.just(existingLink.getLink()); } @@ -360,15 +375,19 @@ protected Mono createConsumer(String linkName, String entityPat final LinkSubscription computed = openReceiveLinks.compute(linkName, (linkNameKey, existing) -> { if (existing != null) { - logger.info("linkName[{}]: Another receive link exists. Disposing of new one.", - linkName); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, linkName) + .log("Another receive link exists. Disposing of new one."); tokenManager.close(); return existing; } - logger.info("connectionId[{}] sessionId[{}] linkName[{}] Creating a new receiver link.", - sessionHandler.getConnectionId(), sessionName, linkName); + logger.atInfo() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .addKeyValue(LINK_NAME_KEY, linkName) + .log("Creating a new receiver link."); + return getSubscription(linkNameKey, entityPath, sourceFilters, receiverProperties, receiverDesiredCapabilities, senderSettleMode, receiverSettleMode, tokenManager); }); @@ -428,16 +447,18 @@ private Mono createProducer(String linkName, String entityPath, Map linkProperties, boolean requiresAuthorization) { if (isDisposed()) { - return Mono.error(logger.logExceptionAsError(new AmqpException(true, - String.format( - "connectionId[%s] sessionName[%s] entityPath[%s] linkName[%s] Cannot create send link from a closed" - + " session.", sessionHandler.getConnectionId(), sessionName, entityPath, linkName), - sessionHandler.getErrorContext()))); + LoggingEventBuilder logBuilder = logger.atError() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName); + return monoError(logBuilder, new AmqpException(true, "Cannot create send link from a closed session.", sessionHandler.getErrorContext())); } final LinkSubscription existing = openSendLinks.get(linkName); if (existing != null) { - logger.verbose("linkName[{}]: Returning existing send link.", linkName); + logger.atVerbose() + .addKeyValue(LINK_NAME_KEY, linkName) + .log("Returning existing send link."); return Mono.just(existing.getLink()); } @@ -459,8 +480,9 @@ private Mono createProducer(String linkName, String entityPath, final LinkSubscription computed = openSendLinks.compute(linkName, (linkNameKey, existingLink) -> { if (existingLink != null) { - logger.info("linkName[{}]: Another send link exists. Disposing of new one.", - linkName); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, linkName) + .log("Another send link exists. Disposing of new one."); if (tokenManager != null) { tokenManager.close(); @@ -468,8 +490,11 @@ private Mono createProducer(String linkName, String entityPath, return existingLink; } - logger.info("connectionId[{}] sessionId[{}] linkName[{}] Creating a new send link.", - sessionHandler.getConnectionId(), sessionName, linkName); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("Creating a new send link."); + return getSubscription(linkName, entityPath, target, linkProperties, options, tokenManager); }); @@ -517,7 +542,10 @@ private LinkSubscription getSubscription(String linkName, String e } }, () -> { if (!isDisposed.get()) { - logger.info("linkName[{}]: Complete. Removing and disposing send link.", linkName); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, linkName) + .log("Complete. Removing and disposing send link."); + removeLink(openSendLinks, linkName); } }); @@ -577,8 +605,10 @@ private LinkSubscription getSubscription(String linkName, Strin } }, () -> { if (!isDisposed.get()) { - logger.info("linkName[{}] entityPath[{}]: Complete. Removing receive link.", - linkName, entityPath); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log("Complete. Removing receive link."); removeLink(openReceiveLinks, linkName); } @@ -595,10 +625,10 @@ private LinkSubscription getSubscription(String linkName, Strin * * @return A Mono that completes when the shutdown signal is emitted. If it does, returns an error. */ - private Mono onClosedError(String message) { + private Mono onClosedError(String message, String linkName, String entityPath) { return Mono.firstWithSignal(isClosedMono.asMono(), shutdownSignals.next()) .then(Mono.error(new AmqpException(false, - String.format("connectionId[%s] Connection closed. %s", sessionHandler.getConnectionId(), message), + String.format("connectionId[%s] entityPath[%s] linkName[%s] Connection closed. %s", sessionHandler.getConnectionId(), entityPath, linkName, message), sessionHandler.getErrorContext()))); } @@ -614,16 +644,18 @@ private Mono onActiveEndpoint() { } private void handleClose() { - logger.verbose( - "connectionId[{}] sessionName[{}] Disposing of active send and receive links due to session close.", - sessionHandler.getConnectionId(), sessionName); + logger.atVerbose() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("Disposing of active send and receive links due to session close."); closeAsync().subscribe(); } private void handleError(Throwable error) { - logger.verbose("connectionId[{}] sessionName[{}] Disposing of active links due to error.", - sessionHandler.getConnectionId(), sessionName, error); + logger.atVerbose() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("Disposing of active links due to error."); + final ErrorCondition condition; if (error instanceof AmqpException) { final AmqpException exception = ((AmqpException) error); @@ -679,14 +711,17 @@ private void disposeWork(ErrorCondition errorCondition, boolean disposeLinks) { // We want to complete the session so that the parent connection isn't waiting. Mono closeLinksMono = Mono.when(closingLinks).timeout(retryOptions.getTryTimeout()) .onErrorResume(error -> { - logger.warning("connectionId[{}] sessionName[{}] Timed out waiting for all links to close.", - sessionHandler.getConnectionId(), sessionName, error); + logger.atWarning() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("Timed out waiting for all links to close.", error); return Mono.empty(); }) .then(Mono.fromRunnable(() -> { isClosedMono.emitEmpty((signalType, result) -> { - logger.warning("connectionId[{}] signal[{}] result[{}] Unable to emit shutdown signal.", - sessionHandler.getConnectionId(), signalType, result); + addSignalTypeAndResult(logger.atWarning(), signalType, result) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("Unable to emit shutdown signal."); + return false; }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java index be296b85d981..f49f12062efc 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java @@ -47,6 +47,9 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; import static com.azure.core.util.FluxUtil.monoError; import static java.nio.charset.StandardCharsets.UTF_8; @@ -56,7 +59,7 @@ * link and {@link Receiver} link. */ public class RequestResponseChannel implements AsyncCloseable { - private final ClientLogger logger = new ClientLogger(RequestResponseChannel.class); + private final ClientLogger logger; private final Sender sendLink; private final Receiver receiveLink; @@ -89,8 +92,6 @@ public class RequestResponseChannel implements AsyncCloseable { // those subscriptions should be disposed when the request-response-channel terminates. private final Disposable.Composite subscriptions; - private final String connectionId; - private final String linkName; private final AmqpRetryOptions retryOptions; private final String replyTo; private final String activeEndpointTimeoutMessage; @@ -122,8 +123,7 @@ protected RequestResponseChannel(AmqpConnection amqpConnection, String connectio AmqpRetryOptions retryOptions, ReactorHandlerProvider handlerProvider, ReactorProvider provider, MessageSerializer messageSerializer, SenderSettleMode senderSettleMode, ReceiverSettleMode receiverSettleMode) { - this.connectionId = connectionId; - this.linkName = linkName; + this.logger = new ClientLogger(RequestResponseChannel.class, Map.of(CONNECTION_ID_KEY, connectionId, LINK_NAME_KEY, linkName)); this.retryOptions = retryOptions; this.provider = provider; this.senderSettleMode = senderSettleMode; @@ -169,8 +169,9 @@ protected RequestResponseChannel(AmqpConnection amqpConnection, String connectio receiveLinkHandler.getDeliveredMessages() .map(this::decodeDelivery) .subscribe(message -> { - logger.verbose("connectionId[{}], linkName[{}] messageId[{}]: Settling message.", connectionId, - linkName, message.getCorrelationId()); + logger.atVerbose() + .addKeyValue("messageId", message.getCorrelationId()) + .log("Settling message."); settleMessage(message); }), @@ -198,7 +199,7 @@ protected RequestResponseChannel(AmqpConnection amqpConnection, String connectio //TODO (conniey): Do we need this if we already close the request response nodes when the // connection.closeWork is executed? It would be preferred to get rid of this circular dependency. amqpConnection.getShutdownSignals().next().flatMap(signal -> { - logger.verbose("connectionId[{}] linkName[{}]: Shutdown signal received.", connectionId, linkName); + logger.verbose("Shutdown signal received."); return closeAsync(); }).subscribe() ); @@ -213,8 +214,7 @@ protected RequestResponseChannel(AmqpConnection amqpConnection, String connectio this.receiveLink.open(); }); } catch (IOException | RejectedExecutionException e) { - throw logger.logExceptionAsError(new RuntimeException(String.format( - "connectionId[%s] linkName[%s]: Unable to open send and receive link.", connectionId, linkName), e)); + throw logger.atError().log(new RuntimeException("Unable to open send and receive link.", e)); } } @@ -233,9 +233,8 @@ public Mono closeAsync() { .timeout(retryOptions.getTryTimeout()) .onErrorResume(TimeoutException.class, error -> { return Mono.fromRunnable(() -> { - logger.info("connectionId[{}] linkName[{}] Timed out waiting for RequestResponseChannel to complete" - + " closing. Manually closing.", - connectionId, linkName); + logger.atInfo() + .log("Timed out waiting for RequestResponseChannel to complete closing. Manually closing."); onTerminalState("SendLinkHandler"); onTerminalState("ReceiveLinkHandler"); @@ -244,25 +243,24 @@ public Mono closeAsync() { .subscribeOn(Schedulers.boundedElastic()); if (isDisposed.getAndSet(true)) { - logger.verbose("connectionId[{}] linkName[{}] Channel already closed.", connectionId, linkName); + logger.verbose("Channel already closed."); return closeOperationWithTimeout; } - logger.verbose("connectionId[{}] linkName[{}] Closing request/response channel.", connectionId, linkName); + logger.verbose("Closing request/response channel."); return Mono.fromRunnable(() -> { try { // Schedule API calls on proton-j entities on the ReactorThread associated with the connection. provider.getReactorDispatcher().invoke(() -> { - logger.verbose("connectionId[{}] linkName[{}] Closing send link and receive link.", - connectionId, linkName); + logger.verbose("Closing send link and receive link."); sendLink.close(); receiveLink.close(); }); } catch (IOException | RejectedExecutionException e) { - logger.info("connectionId[{}] linkName[{}] Unable to schedule close work. Closing manually.", - connectionId, linkName); + logger.info("Unable to schedule close work. Closing manually."); + sendLink.close(); receiveLink.close(); } @@ -294,18 +292,17 @@ public Mono sendWithAck(final Message message) { */ public Mono sendWithAck(final Message message, DeliveryState deliveryState) { if (isDisposed()) { - return monoError(logger, new IllegalStateException( - "Cannot send a message when request response channel is disposed.")); + return monoError(logger.atError(), new IllegalStateException("Cannot send a message when request response channel is disposed.")); } if (message == null) { - return monoError(logger, new NullPointerException("message cannot be null")); + return monoError(logger.atError(), new NullPointerException("message cannot be null")); } if (message.getMessageId() != null) { - return monoError(logger, new IllegalArgumentException("message.getMessageId() should be null")); + return monoError(logger.atError(), new IllegalArgumentException("message.getMessageId() should be null")); } if (message.getReplyTo() != null) { - return monoError(logger, new IllegalArgumentException("message.getReplyTo() should be null")); + return monoError(logger.atError(), new IllegalArgumentException("message.getReplyTo() should be null")); } final UnsignedLong messageId = UnsignedLong.valueOf(requestId.incrementAndGet()); @@ -319,8 +316,9 @@ public Mono sendWithAck(final Message message, DeliveryState deliverySt return RetryUtil.withRetry(onActiveEndpoints, retryOptions, activeEndpointTimeoutMessage) .then(Mono.create(sink -> { try { - logger.verbose("connectionId[{}], linkName[{}] messageId[{}]: Scheduling on dispatcher. ", - connectionId, linkName, messageId); + logger.atVerbose() + .addKeyValue("messageId", message.getCorrelationId()) + .log("Scheduling on dispatcher."); unconfirmedSends.putIfAbsent(messageId, sink); // Schedule API calls on proton-j entities on the ReactorThread associated with the connection. @@ -329,8 +327,9 @@ public Mono sendWithAck(final Message message, DeliveryState deliverySt .replace("-", "").getBytes(UTF_8)); if (deliveryState != null) { - logger.verbose("connectionId[{}], linkName[{}]: Setting delivery state as [{}].", - connectionId, linkName, deliveryState); + logger.atVerbose() + .addKeyValue("state", deliveryState) + .log("Setting delivery state."); delivery.setMessageFormat(DeliveryImpl.DEFAULT_MESSAGE_FORMAT); delivery.disposition(deliveryState); } @@ -381,9 +380,9 @@ private void settleMessage(Message message) { final MonoSink sink = unconfirmedSends.remove(correlationId); if (sink == null) { - logger.warning( - "connectionId[{}] linkName[{}] messageId[{}] Received delivery without pending message.", - connectionId, linkName, id); + logger.atWarning() + .addKeyValue("messageId", id) + .log("Received delivery without pending message."); return; } @@ -395,12 +394,13 @@ private void handleError(Throwable error, String message) { return; } - logger.error("connectionId[{}] linkName[{}] {} Disposing unconfirmed sends.", connectionId, linkName, message, - error); + logger.atError() + .log("{} Disposing unconfirmed sends.", message, error); endpointStates.emitError(error, (signalType, emitResult) -> { - logger.warning("connectionId[{}] linkName[{}] signal[{}] result[{}] Could not emit error to sink.", - connectionId, linkName, signalType, emitResult); + addSignalTypeAndResult(logger.atWarning(), signalType, emitResult) + .log("Could not emit error to sink."); + return false; }); @@ -411,13 +411,15 @@ private void handleError(Throwable error, String message) { private void onTerminalState(String handlerName) { if (pendingLinkTerminations.get() <= 0) { - logger.verbose("connectionId[{}] linkName[{}] Already disposed send/receive links."); + logger.atVerbose() + .log("Already disposed send/receive links."); + return; } final int remaining = pendingLinkTerminations.decrementAndGet(); - logger.verbose("connectionId[{}] linkName[{}] {} disposed. Remaining: {}", - connectionId, linkName, handlerName, remaining); + logger.atVerbose() + .log("{} disposed. Remaining: {}", handlerName, remaining); if (remaining == 0) { subscriptions.dispose(); @@ -435,9 +437,8 @@ private void onTerminalState(String handlerName) { } private boolean onEmitSinkFailure(SignalType signalType, Sinks.EmitResult emitResult, String message) { - logger.verbose("connectionId[{}] linkName[{}] signal[{}] result[{}] {}", - connectionId, linkName, signalType, emitResult, message); - + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .log(message); return false; } @@ -450,8 +451,10 @@ private synchronized void updateEndpointState(AmqpEndpointState sendLinkState, A this.receiveLinkState = receiveLinkState; } - logger.verbose("connectionId[{}] linkName[{}] sendState[{}] receiveState[{}] Updating endpoint states.", - connectionId, linkName, this.sendLinkState, this.receiveLinkState); + logger.atVerbose() + .addKeyValue("sendState", this.sendLinkState) + .addKeyValue("receiveState", this.receiveLinkState) + .log("Updating endpoint states."); if (this.sendLinkState == this.receiveLinkState) { this.endpointStates.emitNext(this.sendLinkState, Sinks.EmitFailureHandler.FAIL_FAST); @@ -460,8 +463,8 @@ private synchronized void updateEndpointState(AmqpEndpointState sendLinkState, A // Terminate the unconfirmed MonoSinks by notifying the given error. private void terminateUnconfirmedSends(Throwable error) { - logger.verbose("connectionId[{}] linkName[{}] terminating {} unconfirmed sends (reason: {}).", - connectionId, linkName, unconfirmedSends.size(), error.getMessage()); + logger.atVerbose() + .log("Terminating {} unconfirmed sends (reason: {}).", unconfirmedSends.size(), error.getMessage()); Map.Entry> next; int count = 0; while ((next = unconfirmedSends.pollFirstEntry()) != null) { @@ -469,8 +472,9 @@ private void terminateUnconfirmedSends(Throwable error) { next.getValue().error(error); count++; } + // The below log can also help debug if the external code that error() calls into never return. - logger.verbose("connectionId[{}] linkName[{}] completed the termination of {} unconfirmed sends (reason: {}).", - connectionId, linkName, count, error.getMessage()); + logger.atVerbose() + .log("completed the termination of {} unconfirmed sends (reason: {}).", count, error.getMessage()); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java index 3e529e475a7d..daa074bac0e1 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java @@ -29,6 +29,10 @@ import java.util.Map; import java.util.Objects; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.ClientConstants.FULLY_QUALIFIED_NAMESPACE_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; + /** * Creates an AMQP connection using sockets. */ @@ -57,8 +61,7 @@ public class ConnectionHandler extends Handler { public ConnectionHandler(final String connectionId, final ConnectionOptions connectionOptions, SslPeerDetails peerDetails) { super(connectionId, - Objects.requireNonNull(connectionOptions, "'connectionOptions' cannot be null.").getHostname(), - new ClientLogger(ConnectionHandler.class)); + Objects.requireNonNull(connectionOptions, "'connectionOptions' cannot be null.").getHostname(), ConnectionHandler.class.getName()); add(new Handshaker()); this.connectionOptions = connectionOptions; @@ -131,8 +134,7 @@ protected void addTransportLayers(Event event, TransportInternal transport) { try { defaultSslContext = SSLContext.getDefault(); } catch (NoSuchAlgorithmException e) { - throw logger.logExceptionAsError(new RuntimeException( - "Default SSL algorithm not found in JRE. Please check your JRE setup.", e)); + throw logger.atError().log(new RuntimeException("Default SSL algorithm not found in JRE. Please check your JRE setup.", e)); } } @@ -150,10 +152,10 @@ protected void addTransportLayers(Event event, TransportInternal transport) { sslDomain.setSslContext(defaultSslContext); sslDomain.setPeerAuthentication(SslDomain.VerifyMode.VERIFY_PEER); } else if (verifyMode == SslDomain.VerifyMode.ANONYMOUS_PEER) { - logger.warning("connectionId[{}] '{}' is not secure.", getConnectionId(), verifyMode); + logger.warning("'{}' is not secure.", verifyMode); sslDomain.setPeerAuthentication(SslDomain.VerifyMode.ANONYMOUS_PEER); } else { - throw logger.logExceptionAsError(new UnsupportedOperationException( + throw logger.atError().log(new UnsupportedOperationException( "verifyMode is not supported: " + verifyMode)); } @@ -162,12 +164,14 @@ protected void addTransportLayers(Event event, TransportInternal transport) { @Override public void onConnectionInit(Event event) { - logger.info("onConnectionInit connectionId[{}] hostname[{}] amqpHostname[{}]", - getConnectionId(), getHostname(), connectionOptions.getFullyQualifiedNamespace()); + logger.atInfo() + .addKeyValue(HOSTNAME_KEY, getHostname()) + .addKeyValue(FULLY_QUALIFIED_NAMESPACE_KEY, connectionOptions.getFullyQualifiedNamespace()) + .log("onConnectionInit"); final Connection connection = event.getConnection(); if (connection == null) { - logger.warning("connectionId[{}] Underlying connection is null. Should not be possible."); + logger.warning("Underlying connection is null. Should not be possible."); close(); return; } @@ -188,8 +192,10 @@ public void onConnectionInit(Event event) { public void onConnectionBound(Event event) { final Transport transport = event.getTransport(); - logger.info("onConnectionBound connectionId[{}] hostname[{}] peerDetails[{}:{}]", getConnectionId(), - getHostname(), peerDetails.getHostname(), peerDetails.getPort()); + logger.atInfo() + .addKeyValue(HOSTNAME_KEY, getHostname()) + .addKeyValue("peerDetails", () -> peerDetails.getHostname() + ":" + peerDetails.getPort()) + .log("onConnectionBound"); this.addTransportLayers(event, (TransportInternal) transport); @@ -202,8 +208,11 @@ public void onConnectionBound(Event event) { @Override public void onConnectionUnbound(Event event) { final Connection connection = event.getConnection(); - logger.info("onConnectionUnbound hostname[{}], connectionId[{}], state[{}], remoteState[{}]", - connection.getHostname(), getConnectionId(), connection.getLocalState(), connection.getRemoteState()); + logger.atInfo() + .addKeyValue(HOSTNAME_KEY, connection.getHostname()) + .addKeyValue("localState", connection.getLocalState()) + .addKeyValue("remoteState", connection.getRemoteState()) + .log("onConnectionUnbound"); // if failure happened while establishing transport - nothing to free up. if (connection.getRemoteState() != EndpointState.UNINITIALIZED) { @@ -219,10 +228,9 @@ public void onTransportError(Event event) { final Transport transport = event.getTransport(); final ErrorCondition condition = transport.getCondition(); - logger.warning("onTransportError hostname[{}], connectionId[{}], error[{}]", - connection != null ? connection.getHostname() : ClientConstants.NOT_APPLICABLE, - getConnectionId(), - condition != null ? condition.getDescription() : ClientConstants.NOT_APPLICABLE); + addErrorCondition(logger.atWarning(), condition) + .addKeyValue(HOSTNAME_KEY, connection.getHostname()) + .log("onTransportError"); if (connection != null) { notifyErrorContext(connection, condition); @@ -238,10 +246,9 @@ public void onTransportClosed(Event event) { final Transport transport = event.getTransport(); final ErrorCondition condition = transport.getCondition(); - logger.info("onTransportClosed hostname[{}], connectionId[{}], error[{}]", - connection != null ? connection.getHostname() : ClientConstants.NOT_APPLICABLE, - getConnectionId(), - condition != null ? condition.getDescription() : ClientConstants.NOT_APPLICABLE); + addErrorCondition(logger.atInfo(), condition) + .addKeyValue(HOSTNAME_KEY, connection.getHostname()) + .log("onTransportClosed"); if (connection != null) { notifyErrorContext(connection, condition); @@ -260,8 +267,10 @@ public void onConnectionLocalOpen(Event event) { public void onConnectionRemoteOpen(Event event) { final Connection connection = event.getConnection(); - logger.info("onConnectionRemoteOpen hostname[{}], connectionId[{}], remoteContainer[{}]", - connection.getHostname(), getConnectionId(), connection.getRemoteContainer()); + logger.atInfo() + .addKeyValue(HOSTNAME_KEY, connection.getHostname()) + .addKeyValue("remoteContainer", connection.getRemoteContainer()) + .log("onConnectionRemoteOpen"); onNext(connection.getRemoteState()); } @@ -317,8 +326,7 @@ private void notifyErrorContext(Connection connection, ErrorCondition condition) } if (condition == null) { - throw logger.logExceptionAsError(new IllegalStateException(String.format( - "connectionId[%s]: notifyErrorContext does not have an ErrorCondition.", getConnectionId()))); + throw logger.atError().log(new IllegalStateException("notifyErrorContext does not have an ErrorCondition.")); } // if the remote-peer abruptly closes the connection without issuing close frame issue one @@ -329,11 +337,8 @@ private void notifyErrorContext(Connection connection, ErrorCondition condition) } private void logErrorCondition(String eventName, Connection connection, ErrorCondition error) { - logger.info("{} connectionId[{}] hostname[{}] errorCondition[{}] errorDescription[{}]", - eventName, - getConnectionId(), - connection.getHostname(), - error != null ? error.getCondition() : ClientConstants.NOT_APPLICABLE, - error != null ? error.getDescription() : ClientConstants.NOT_APPLICABLE); + addErrorCondition(logger.atInfo(), error) + .addKeyValue(HOSTNAME_KEY, connection.getHostname()) + .log(eventName); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java index 1258d7698783..4b102e3954b7 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java @@ -9,14 +9,17 @@ import org.apache.qpid.proton.engine.Transport; import org.apache.qpid.proton.reactor.impl.IOHandler; +import java.util.Map; + +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; public class CustomIOHandler extends IOHandler { - private final ClientLogger logger = new ClientLogger(CustomIOHandler.class); - private final String connectionId; + private final ClientLogger logger; public CustomIOHandler(final String connectionId) { - this.connectionId = connectionId; + this.logger = new ClientLogger(CustomIOHandler.class, Map.of(CONNECTION_ID_KEY, connectionId)); } @Override @@ -24,8 +27,9 @@ public void onTransportClosed(Event event) { final Transport transport = event.getTransport(); final Connection connection = event.getConnection(); - logger.info("onTransportClosed connectionId[{}], hostname[{}]", - connectionId, (connection != null ? connection.getHostname() : NOT_APPLICABLE)); + logger.atInfo() + .addKeyValue(HOSTNAME_KEY, connection != null ? connection.getHostname() : NOT_APPLICABLE) + .log("onTransportClosed"); if (transport != null && connection != null && connection.getTransport() != null) { transport.unbind(); @@ -40,7 +44,7 @@ public void onUnhandled(Event event) { try { super.onUnhandled(event); } catch (NullPointerException e) { - logger.error("Exception occurred when handling event in super.", e); + logger.atError().log("Exception occurred when handling event in super.", e); } } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java index 4079b7a0892e..1282f0c2d9cd 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java @@ -10,9 +10,13 @@ import reactor.core.publisher.Sinks; import java.io.Closeable; +import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; + /** * Base class for all proton-j handlers. */ @@ -32,14 +36,15 @@ public abstract class Handler extends BaseHandler implements Closeable { * @param hostname Hostname of the connection. This could be the DNS hostname or the IP address of the * connection. Usually of the form {@literal ".service.windows.net"} but can change if the * messages are brokered through an intermediary. - * @param logger Logger to use for messages. + * @param loggerName loggerName to use. * * @throws NullPointerException if {@code connectionId}, {@code hostname}, or {@code logger} is null. */ - Handler(final String connectionId, final String hostname, ClientLogger logger) { + Handler(final String connectionId, final String hostname, final String loggerName) { this.connectionId = Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); this.hostname = Objects.requireNonNull(hostname, "'hostname' cannot be null."); - this.logger = Objects.requireNonNull(logger, "'logger' cannot be null."); + this.logger = new ClientLogger(Objects.requireNonNull(loggerName, "'loggerName' cannot be null."), + Map.of(CONNECTION_ID_KEY, connectionId)); } /** @@ -85,8 +90,8 @@ void onNext(EndpointState state) { } endpointStates.emitNext(state, (signalType, emitResult) -> { - logger.verbose("connectionId[{}] signal[{}] result[{}] could not emit endpoint state.", connectionId, - signalType, emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .log("could not emit endpoint state."); return false; }); @@ -103,8 +108,8 @@ void onError(Throwable error) { } endpointStates.emitError(error, (signalType, emitResult) -> { - logger.warning("connectionId[{}] signal[{}] result[{}] Could not emit error.", connectionId, - signalType, emitResult, error); + addSignalTypeAndResult(logger.atWarning(), signalType, emitResult) + .log("could not emit error.", error); return false; }); @@ -123,14 +128,15 @@ public void close() { // This is fine in the case that someone called onNext(EndpointState.CLOSED) and then called handler.close(). // We want to ensure that the next endpoint subscriber does not believe the handler is alive still. endpointStates.emitNext(EndpointState.CLOSED, (signalType, emitResult) -> { - logger.info("connectionId[{}] signal[{}] result[{}] Could not emit closed endpoint state.", connectionId, - signalType, emitResult); + addSignalTypeAndResult(logger.atInfo(), signalType, emitResult) + .log("Could not emit closed endpoint state."); return false; }); endpointStates.emitComplete((signalType, emitResult) -> { - logger.verbose("connectionId[{}] result[{}] Could not emit complete.", connectionId, emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .log("Could not emit complete."); return false; }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java index 9727d822580f..9c584aff7ea8 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java @@ -15,6 +15,8 @@ import java.util.Objects; import static com.azure.core.amqp.implementation.AmqpErrorCode.TRACKING_ID_PROPERTY; +import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; /** @@ -34,13 +36,13 @@ abstract class LinkHandler extends Handler { * connection. Usually of the form {@literal ".service.windows.net"} but can change if the * messages are brokered through an intermediary. * @param entityPath The address within the message broker for this link. - * @param logger Logger to use for messages. + * @param loggerName Logger name to use for messages. * * @throws NullPointerException if {@code connectionId}, {@code hostname}, {@code entityPath}, or {@code logger} is * null. */ - LinkHandler(String connectionId, String hostname, String entityPath, ClientLogger logger) { - super(connectionId, hostname, logger); + LinkHandler(String connectionId, String hostname, String entityPath, String loggerName) { + super(connectionId, hostname, loggerName); this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); } @@ -49,11 +51,9 @@ public void onLinkLocalClose(Event event) { final Link link = event.getLink(); final ErrorCondition condition = link.getCondition(); - logger.verbose("onLinkLocalClose connectionId[{}], linkName[{}], errorCondition[{}], errorDescription[{}]", - getConnectionId(), - link.getName(), - condition != null ? condition.getCondition() : NOT_APPLICABLE, - condition != null ? condition.getDescription() : NOT_APPLICABLE); + addErrorCondition(logger.atVerbose(), condition) + .addKeyValue(LINK_NAME_KEY, link.getName()) + .log("onLinkLocalClose"); } @Override @@ -71,7 +71,9 @@ public void onLinkFinal(Event event) { final String linkName = event != null && event.getLink() != null ? event.getLink().getName() : NOT_APPLICABLE; - logger.info("onLinkFinal connectionId[{}], linkName[{}]", getConnectionId(), linkName); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, linkName) + .log("onLinkFinal"); // Be explicit about wanting to call Handler.close(). When we receive onLinkFinal, the service and proton-j are // releasing this link. So we want to complete the endpoint states. @@ -93,14 +95,14 @@ private void handleRemoteLinkClosed(final String eventName, final Event event) { final Link link = event.getLink(); final ErrorCondition condition = link.getRemoteCondition(); - logger.info("{} connectionId[{}] linkName[{}], errorCondition[{}] errorDescription[{}]", - eventName, getConnectionId(), link.getName(), - condition != null ? condition.getCondition() : NOT_APPLICABLE, - condition != null ? condition.getDescription() : NOT_APPLICABLE); + addErrorCondition(logger.atInfo(), condition) + .addKeyValue(LINK_NAME_KEY, link.getName()) + .log(eventName); if (link.getLocalState() != EndpointState.CLOSED) { - logger.info("connectionId[{}] linkName[{}] state[{}] Local link state is not closed.", getConnectionId(), - link.getName(), link.getLocalState()); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, link.getName()) + .addKeyValue("state", link.getLocalState()); link.setCondition(condition); link.close(); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReactorHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReactorHandler.java index 5d8c9d9de74a..35d7774a3385 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReactorHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReactorHandler.java @@ -8,8 +8,11 @@ import org.apache.qpid.proton.engine.Event; import org.apache.qpid.proton.reactor.Reactor; +import java.util.Map; import java.util.Objects; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; + /** * Handler that sets the timeout period for waiting for Selectables. */ @@ -20,17 +23,16 @@ public class ReactorHandler extends BaseHandler { */ private static final int REACTOR_IO_POLL_TIMEOUT = 20; - private final ClientLogger logger = new ClientLogger(ReactorHandler.class); - private final String connectionId; + private final ClientLogger logger; public ReactorHandler(final String connectionId) { Objects.requireNonNull(connectionId); - this.connectionId = connectionId; + this.logger = new ClientLogger(ReactorHandler.class, Map.of(CONNECTION_ID_KEY, connectionId)); } @Override public void onReactorInit(Event e) { - logger.info("connectionId[{}] reactor.onReactorInit", connectionId); + logger.info("reactor.onReactorInit"); final Reactor reactor = e.getReactor(); reactor.setTimeout(REACTOR_IO_POLL_TIMEOUT); @@ -38,6 +40,6 @@ public void onReactorInit(Event e) { @Override public void onReactorFinal(Event e) { - logger.info("connectionId[{}] reactor.onReactorFinal. event: {}", connectionId, e); + logger.info("reactor.onReactorFinal. event: {}", e); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java index d9e83ed9d3d6..ac3b4b8582df 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java @@ -3,7 +3,7 @@ package com.azure.core.amqp.implementation.handler; -import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.messaging.Modified; import org.apache.qpid.proton.amqp.transport.ErrorCondition; @@ -24,6 +24,11 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; + + /** * Handler that receives events from its corresponding {@link Receiver}. Handlers must be associated to a * {@link Receiver} to receive its events. @@ -45,9 +50,9 @@ public class ReceiveLinkHandler extends LinkHandler { private final String entityPath; public ReceiveLinkHandler(String connectionId, String hostname, String linkName, String entityPath) { - super(connectionId, hostname, entityPath, new ClientLogger(ReceiveLinkHandler.class)); + super(connectionId, hostname, entityPath, ReceiveLinkHandler.class.getName()); this.linkName = Objects.requireNonNull(linkName, "'linkName' cannot be null."); - this.entityPath = entityPath; + this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); } public String getLinkName() { @@ -78,8 +83,11 @@ public void close() { public void onLinkLocalOpen(Event event) { final Link link = event.getLink(); if (link instanceof Receiver) { - logger.verbose("onLinkLocalOpen connectionId[{}], entityPath[{}], linkName[{}], localSource[{}]", - getConnectionId(), entityPath, link.getName(), link.getSource()); + logger.atVerbose() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, link.getName()) + .addKeyValue("localSource", link.getSource()) + .log("onLinkLocalOpen"); } } @@ -90,17 +98,22 @@ public void onLinkRemoteOpen(Event event) { return; } + LoggingEventBuilder logBuilder = logger.atInfo() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, link.getName()); + if (link.getRemoteSource() != null) { - logger.info("onLinkRemoteOpen connectionId[{}], entityPath[{}], linkName[{}], remoteSource[{}]", - getConnectionId(), entityPath, link.getName(), link.getRemoteSource()); + logBuilder.addKeyValue("remoteSource", link.getRemoteSource()); if (!isRemoteActive.getAndSet(true)) { onNext(EndpointState.ACTIVE); } } else { - logger.info("onLinkRemoteOpen connectionId[{}], entityPath[{}], linkName[{}], action[waitingForError]", - getConnectionId(), entityPath, link.getName()); + logBuilder + .addKeyValue("action", "waitingForError"); } + + logBuilder.log("onLinkRemoteOpen"); } @Override @@ -123,14 +136,18 @@ public void onDelivery(Event event) { // before we fix proton-j - this work around ensures that we ignore the duplicate Delivery event if (wasSettled) { if (link != null) { - logger.info("onDelivery connectionId[{}], entityPath[{}], linkName[{}], updatedLinkCredit[{}]," - + " remoteCredit[{}], remoteCondition[{}], delivery.isSettled[{}] Was already settled.", - getConnectionId(), entityPath, link.getName(), link.getCredit(), link.getRemoteCredit(), - link.getRemoteCondition(), delivery.isSettled()); + addErrorCondition(logger.atInfo(), link.getRemoteCondition()) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue("updatedLinkCredit", link.getCredit()) + .addKeyValue("remoteCredit",link.getRemoteCredit()) + .addKeyValue("delivery.isSettled", delivery.isSettled()) + .log("Was already settled."); } else { - logger.warning("connectionId[{}], entityPath[{}] delivery.isSettled[{}] Settled delivery with no " - + " link.", - getConnectionId(), entityPath, delivery.isSettled()); + logger.atWarning() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue("delivery.isSettled", delivery.isSettled()) + .log("Settled delivery with no delivery"); } } else { if (link.getLocalState() == EndpointState.CLOSED) { @@ -144,9 +161,12 @@ public void onDelivery(Event event) { } else { queuedDeliveries.add(delivery); deliveries.emitNext(delivery, (signalType, emitResult) -> { - logger.warning("connectionId[{}], entityPath[{}], linkName[{}], emitResult[{}] " - + "Could not emit delivery. {}", - getConnectionId(), entityPath, linkName, emitResult, delivery); + logger.atWarning() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue("emitResult", emitResult) + .log("Could not emit delivery. {}", delivery); + if (emitResult == Sinks.EmitResult.FAIL_OVERFLOW && link.getLocalState() != EndpointState.CLOSED) { link.setCondition(new ErrorCondition(Symbol.getSymbol("delivery-buffer-overflow"), @@ -164,11 +184,14 @@ public void onDelivery(Event event) { if (link != null) { final ErrorCondition condition = link.getRemoteCondition(); - logger.verbose("onDelivery connectionId[{}], linkName[{}], updatedLinkCredit[{}]," - + "remoteCredit[{}], remoteCondition[{}], delivery.isPartial[{}], delivery.isSettled[{}]", - getConnectionId(), link.getName(), link.getCredit(), link.getRemoteCredit(), - condition != null && condition.getCondition() != null ? condition : "N/A", - delivery.isPartial(), wasSettled); + addErrorCondition(logger.atVerbose(), condition) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue("updatedLinkCredit", link.getCredit()) + .addKeyValue("remoteCredit", link.getRemoteCredit()) + .addKeyValue("delivery.isPartial", delivery.isPartial()) + .addKeyValue("delivery.isSettled", wasSettled) + .log("onDelivery."); } } @@ -179,8 +202,10 @@ public void onLinkLocalClose(Event event) { // Someone called receiver.close() to set the local link state to close. Since the link was never remotely // active, we complete getEndpointStates() ourselves. if (!isRemoteActive.get()) { - logger.info("connectionId[{}] linkName[{}] entityPath[{}] Receiver link was never active. Closing endpoint " - + "states.", getConnectionId(), getLinkName(), entityPath); + logger.atInfo() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName) + .log("Receiver link was never active. Closing endpoint states"); super.close(); } @@ -209,8 +234,10 @@ public void onLinkFinal(Event event) { */ private void clearAndCompleteDeliveries(String errorMessage) { deliveries.emitComplete((signalType, emitResult) -> { - logger.verbose("connectionId[{}], entityPath[{}], linkName[{}] {}", getConnectionId(), entityPath, - linkName, errorMessage); + logger.atVerbose() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue(LINK_NAME_KEY, linkName) + .log(errorMessage); return false; }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java index cd11ef50e163..af73ef5f951d 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java @@ -3,7 +3,7 @@ package com.azure.core.amqp.implementation.handler; -import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.engine.BaseHandler; import org.apache.qpid.proton.engine.Delivery; import org.apache.qpid.proton.engine.EndpointState; @@ -19,6 +19,11 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; + /** * Handler that receives events from its corresponding {@link Sender}. Handlers must be associated to a {@link Sender} * to receive its events. @@ -39,7 +44,7 @@ public class SendLinkHandler extends LinkHandler { private final Sinks.Many deliveryProcessor = Sinks.many().multicast().onBackpressureBuffer(); public SendLinkHandler(String connectionId, String hostname, String linkName, String entityPath) { - super(connectionId, hostname, entityPath, new ClientLogger(SendLinkHandler.class)); + super(connectionId, hostname, entityPath, SendLinkHandler.class.getName()); this.linkName = Objects.requireNonNull(linkName, "'linkName' cannot be null."); this.entityPath = entityPath; } @@ -69,8 +74,9 @@ public void close() { creditProcessor.emitComplete(Sinks.EmitFailureHandler.FAIL_FAST); deliveryProcessor.emitComplete((signalType, emitResult) -> { - logger.verbose("connectionId[{}] linkName[{}] result[{}] Unable to emit complete on deliverySink.", - getConnectionId(), linkName, emitResult); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .addKeyValue(LINK_NAME_KEY, linkName) + .log("Unable to emit complete on deliverySink."); return false; }); @@ -81,8 +87,11 @@ public void close() { public void onLinkLocalOpen(Event event) { final Link link = event.getLink(); if (link instanceof Sender) { - logger.verbose("onLinkLocalOpen connectionId[{}], entityPath[{}], linkName[{}], localTarget[{}]", - getConnectionId(), entityPath, link.getName(), link.getTarget()); + logger.atVerbose() + .addKeyValue(LINK_NAME_KEY, link.getName()) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .addKeyValue("localTarget", link.getTarget()) + .log("onLinkLocalOpen"); } } @@ -93,18 +102,22 @@ public void onLinkRemoteOpen(Event event) { return; } + LoggingEventBuilder logBuilder = logger.atInfo() + .addKeyValue(LINK_NAME_KEY, link.getName()) + .addKeyValue(ENTITY_PATH_KEY, entityPath); + if (link.getRemoteTarget() != null) { - logger.info("onLinkRemoteOpen connectionId[{}], entityPath[{}], linkName[{}], remoteTarget[{}]", - getConnectionId(), entityPath, link.getName(), link.getRemoteTarget()); + // TODO escape + logBuilder.addKeyValue("remoteTarget", link.getRemoteTarget()); if (!isRemoteActive.getAndSet(true)) { onNext(EndpointState.ACTIVE); } } else { - logger.info("onLinkRemoteOpen connectionId[{}], entityPath[{}], linkName[{}], remoteTarget[null]," - + " remoteSource[null], action[waitingForError]", - getConnectionId(), entityPath, link.getName()); + logBuilder.addKeyValue("remoteTarget", (String)null) + .addKeyValue("action", "waitingForError"); } + logBuilder.log("onLinkRemoteOpen"); } @Override @@ -116,13 +129,19 @@ public void onLinkFlow(Event event) { final Sender sender = event.getSender(); final int credits = sender.getRemoteCredit(); creditProcessor.emitNext(credits, (signalType, emitResult) -> { - logger.verbose("connectionId[{}] linkName[{}] result[{}] Unable to emit credits [{}].", - getConnectionId(), linkName, emitResult, credits); + logger.atVerbose() + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue(EMIT_RESULT_KEY, emitResult) + .addKeyValue("credits", credits) + .log("Unable to emit credits."); return false; }); - logger.verbose("onLinkFlow connectionId[{}] linkName[{}] unsettled[{}] credit[{}]", - getConnectionId(), sender.getName(), sender.getUnsettled(), sender.getCredit()); + logger.atVerbose() + .addKeyValue(LINK_NAME_KEY, linkName) + .addKeyValue("unsettled", sender.getUnsettled()) + .addKeyValue("credits", credits) + .log("onLinkFlow."); } @Override @@ -132,8 +151,10 @@ public void onLinkLocalClose(Event event) { // Someone called sender.close() to set the local link state to close. Since the link was never remotely // active, we complete getEndpointStates() ourselves. if (!isRemoteActive.get()) { - logger.info("connectionId[{}] linkName[{}] entityPath[{}] Sender link was never active. Closing endpoint " - + "states.", getConnectionId(), getLinkName(), entityPath); + logger.atInfo() + .addKeyValue(LINK_NAME_KEY, getLinkName()) + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log("Sender link was never active. Closing endpoint states."); super.close(); } @@ -147,14 +168,21 @@ public void onDelivery(Event event) { final Sender sender = (Sender) delivery.getLink(); final String deliveryTag = new String(delivery.getTag(), StandardCharsets.UTF_8); - logger.verbose("onDelivery connectionId[{}] linkName[{}] unsettled[{}] credit[{}]," - + " deliveryState[{}] delivery.isBuffered[{}] delivery.id[{}]", - getConnectionId(), sender.getName(), sender.getUnsettled(), sender.getRemoteCredit(), - delivery.getRemoteState(), delivery.isBuffered(), deliveryTag); + logger.atVerbose() + .addKeyValue(LINK_NAME_KEY, getLinkName()) + .addKeyValue("unsettled", sender.getUnsettled()) + .addKeyValue("credit", sender.getRemoteCredit()) + .addKeyValue("deliveryState", delivery.getRemoteState()) + .addKeyValue("delivery.isBuffered", delivery.isBuffered()) + .addKeyValue("delivery.id", deliveryTag) + .log("onDelivery"); deliveryProcessor.emitNext(delivery, (signalType, emitResult) -> { - logger.warning("connectionId[{}] linkName[{}] result[{}] Unable to emit delivery [{}].", - getConnectionId(), linkName, emitResult, deliveryTag); + logger.atWarning() + .addKeyValue(LINK_NAME_KEY, getLinkName()) + .addKeyValue(EMIT_RESULT_KEY, emitResult) + .addKeyValue("delivery.id", deliveryTag) + .log("Unable to emit delivery."); return emitResult == Sinks.EmitResult.FAIL_OVERFLOW; }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java index f8a633d98d7d..aa6d92ad6898 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java @@ -6,10 +6,9 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; import com.azure.core.amqp.exception.SessionErrorContext; -import com.azure.core.amqp.implementation.ClientConstants; import com.azure.core.amqp.implementation.ExceptionUtil; import com.azure.core.amqp.implementation.ReactorDispatcher; -import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; import org.apache.qpid.proton.engine.Event; @@ -20,46 +19,48 @@ import java.util.Locale; import java.util.concurrent.RejectedExecutionException; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.ClientConstants.SESSION_NAME_KEY; + public class SessionHandler extends Handler { - private final String entityName; + private final String sessionName; private final Duration openTimeout; private final ReactorDispatcher reactorDispatcher; - public SessionHandler(String connectionId, String hostname, String entityName, ReactorDispatcher reactorDispatcher, + public SessionHandler(String connectionId, String hostname, String sessionName, ReactorDispatcher reactorDispatcher, Duration openTimeout) { - super(connectionId, hostname, new ClientLogger(SessionHandler.class)); - this.entityName = entityName; + super(connectionId, hostname, SessionHandler.class.getName()); + this.sessionName = sessionName; this.openTimeout = openTimeout; this.reactorDispatcher = reactorDispatcher; } public AmqpErrorContext getErrorContext() { - return new SessionErrorContext(getHostname(), entityName); + return new SessionErrorContext(getHostname(), sessionName); } @Override public void onSessionLocalOpen(Event e) { - logger.verbose("onSessionLocalOpen connectionId[{}], entityName[{}], condition[{}]", - getConnectionId(), this.entityName, - e.getSession().getCondition() == null - ? ClientConstants.NOT_APPLICABLE - : e.getSession().getCondition().toString()); + addErrorCondition(logger.atVerbose(), e.getSession().getCondition()) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("onSessionLocalOpen"); final Session session = e.getSession(); try { reactorDispatcher.invoke(this::onSessionTimeout, this.openTimeout); } catch (IOException | RejectedExecutionException ioException) { - logger.info("onSessionLocalOpen connectionId[{}], entityName[{}], reactorDispatcherError[{}]", - getConnectionId(), this.entityName, - ioException.getMessage()); + logger.atInfo() + .addKeyValue(SESSION_NAME_KEY, sessionName) + .addKeyValue("reactorDispatcherError", ioException.getMessage()) + .log("onSessionLocalOpen"); session.close(); final String message = String.format(Locale.US, "onSessionLocalOpen connectionId[%s], entityName[%s], underlying IO of" + " reactorDispatcher faulted with error: %s", - getConnectionId(), this.entityName, ioException.getMessage()); + getConnectionId(), sessionName, ioException.getMessage()); final Throwable exception = new AmqpException(false, message, ioException, getErrorContext()); onError(exception); @@ -69,18 +70,20 @@ public void onSessionLocalOpen(Event e) { @Override public void onSessionRemoteOpen(Event e) { final Session session = e.getSession(); + LoggingEventBuilder logBuilder; if (session.getLocalState() == EndpointState.UNINITIALIZED) { - logger.warning("onSessionRemoteOpen connectionId[{}], entityName[{}], sessionIncCapacity[{}]," - + " sessionOutgoingWindow[{}] endpoint was uninitialised.", - getConnectionId(), entityName, session.getIncomingCapacity(), session.getOutgoingWindow()); - + logBuilder = logger.atWarning(); session.open(); } else { - logger.info("onSessionRemoteOpen connectionId[{}], entityName[{}], sessionIncCapacity[{}]," - + " sessionOutgoingWindow[{}]", - getConnectionId(), entityName, session.getIncomingCapacity(), session.getOutgoingWindow()); + logBuilder = logger.atInfo(); } + + logBuilder.addKeyValue(SESSION_NAME_KEY, sessionName) + .addKeyValue("sessionIncCapacity", session.getIncomingCapacity()) + .addKeyValue("sessionOutgoingWindow", session.getOutgoingWindow()) + .log("onSessionRemoteOpen"); + onNext(EndpointState.ACTIVE); } @@ -90,30 +93,25 @@ public void onSessionLocalClose(Event e) { ? e.getSession().getCondition() : null; - logger.verbose("onSessionLocalClose connectionId[{}], entityName[{}], condition[{}]", - entityName, getConnectionId(), - condition == null ? ClientConstants.NOT_APPLICABLE : condition.toString()); + addErrorCondition(logger.atVerbose(), condition) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("onSessionLocalClose"); } @Override public void onSessionRemoteClose(Event e) { final Session session = e.getSession(); - logger.info("onSessionRemoteClose connectionId[{}], entityName[{}], condition[{}]", - entityName, - getConnectionId(), - session == null || session.getRemoteCondition() == null - ? ClientConstants.NOT_APPLICABLE - : session.getRemoteCondition().toString()); + addErrorCondition(logger.atInfo(), session.getCondition()) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("onSessionRemoteClose"); ErrorCondition condition = session != null ? session.getRemoteCondition() : null; if (session != null && session.getLocalState() != EndpointState.CLOSED) { - logger.info("onSessionRemoteClose closing a local session for connectionId[{}], entityName[{}], " - + "condition[{}], description[{}]", - getConnectionId(), entityName, - condition != null ? condition.getCondition() : ClientConstants.NOT_APPLICABLE, - condition != null ? condition.getDescription() : ClientConstants.NOT_APPLICABLE); + addErrorCondition(logger.atInfo(), condition) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("onSessionRemoteClose closing a local session."); session.setCondition(session.getRemoteCondition()); session.close(); @@ -127,7 +125,7 @@ public void onSessionRemoteClose(Event e) { final Exception exception = ExceptionUtil.toException(condition.getCondition().toString(), String.format(Locale.US, "onSessionRemoteClose connectionId[%s], entityName[%s] condition[%s]", - id, entityName, condition), context); + id, sessionName, condition), context); onError(exception); } @@ -138,11 +136,9 @@ public void onSessionFinal(Event e) { final Session session = e.getSession(); final ErrorCondition condition = session != null ? session.getCondition() : null; - logger.info("onSessionFinal connectionId[{}], entityName[{}], condition[{}], description[{}]", - getConnectionId(), entityName, - condition != null ? condition.getCondition() : ClientConstants.NOT_APPLICABLE, - condition != null ? condition.getDescription() : ClientConstants.NOT_APPLICABLE); - + addErrorCondition(logger.atInfo(), condition) + .addKeyValue(SESSION_NAME_KEY, sessionName) + .log("onSessionFinal."); close(); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java index 192e222aa00f..37d4255c9572 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java @@ -10,6 +10,11 @@ import org.apache.qpid.proton.engine.SslPeerDetails; import org.apache.qpid.proton.engine.impl.TransportInternal; +import java.util.Map; + +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; + /** * Creates an AMQP connection using web sockets (port 443). */ @@ -22,7 +27,7 @@ public class WebSocketsConnectionHandler extends ConnectionHandler { private static final String SOCKET_PATH = "/$servicebus/websocket"; private static final String PROTOCOL = "AMQPWSB10"; - private final ClientLogger logger = new ClientLogger(WebSocketsConnectionHandler.class); + private final ClientLogger logger; /** * Creates a handler that handles proton-j's connection events using web sockets. @@ -33,6 +38,7 @@ public class WebSocketsConnectionHandler extends ConnectionHandler { public WebSocketsConnectionHandler(String connectionId, ConnectionOptions connectionOptions, SslPeerDetails peerDetails) { super(connectionId, connectionOptions, peerDetails); + logger = new ClientLogger(WebSocketsConnectionHandler.class, Map.of(CONNECTION_ID_KEY, connectionId)); } /** @@ -44,7 +50,6 @@ public WebSocketsConnectionHandler(String connectionId, ConnectionOptions connec @Override protected void addTransportLayers(final Event event, final TransportInternal transport) { final String hostName = event.getConnection().getHostname(); - logger.info("Adding web socket layer"); final WebSocketImpl webSocket = new WebSocketImpl(); webSocket.configure( @@ -58,8 +63,9 @@ protected void addTransportLayers(final Event event, final TransportInternal tra transport.addTransportLayer(webSocket); - logger.verbose("connectionId[{}] Adding web sockets transport layer for hostname[{}]", - getConnectionId(), hostName); + logger.atVerbose() + .addKeyValue(HOSTNAME_KEY, hostName) + .log("Adding web sockets transport layer."); super.addTransportLayers(event, transport); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java index a2ace78afa76..fadb892ca552 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java @@ -27,16 +27,21 @@ import java.net.URI; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; + /** * Creates an AMQP connection using web sockets and connects through a proxy. */ public class WebSocketsProxyConnectionHandler extends WebSocketsConnectionHandler { private static final String HTTPS_URI_FORMAT = "https://%s:%s"; - private final ClientLogger logger = new ClientLogger(WebSocketsProxyConnectionHandler.class); + private final ClientLogger logger; private final InetSocketAddress connectionHostname; private final ProxyOptions proxyOptions; private final String fullyQualifiedNamespace; @@ -60,14 +65,14 @@ public WebSocketsProxyConnectionHandler(String connectionId, ConnectionOptions c this.proxyOptions = Objects.requireNonNull(proxyOptions, "'proxyConfiguration' cannot be null."); this.fullyQualifiedNamespace = connectionOptions.getFullyQualifiedNamespace(); this.amqpBrokerHostname = connectionOptions.getFullyQualifiedNamespace() + ":" + connectionOptions.getPort(); - + this.logger = new ClientLogger(WebSocketsProxyConnectionHandler.class, Map.of(CONNECTION_ID_KEY, connectionId)); if (proxyOptions.isProxyAddressConfigured()) { this.connectionHostname = (InetSocketAddress) proxyOptions.getProxyAddress().address(); } else { final URI serviceUri = createURI(connectionOptions.getHostname(), connectionOptions.getPort()); final ProxySelector proxySelector = ProxySelector.getDefault(); if (proxySelector == null) { - throw logger.logExceptionAsError(new IllegalStateException("ProxySelector should not be null.")); + throw logger.atError().log(new IllegalStateException("ProxySelector should not be null.")); } final List proxies = proxySelector.select(serviceUri); @@ -75,7 +80,7 @@ public WebSocketsProxyConnectionHandler(String connectionId, ConnectionOptions c final String formatted = String.format("No proxy address found for: '%s'. Available: %s.", serviceUri, proxies.stream().map(Proxy::toString).collect(Collectors.joining(", "))); - throw logger.logExceptionAsError(new IllegalStateException(formatted)); + throw logger.atError().log(new IllegalStateException(formatted)); } final Proxy proxy = proxies.get(0); @@ -131,16 +136,15 @@ public void onTransportError(Event event) { final Transport transport = event.getTransport(); final Connection connection = event.getConnection(); if (connection == null || transport == null) { - logger.verbose("connectionId[{}] There is no connection or transport associated with error. Event: {}", - event); + logger.verbose("There is no connection or transport associated with error. Event: {}", event); return; } final ErrorCondition errorCondition = transport.getCondition(); if (errorCondition == null || !(errorCondition.getCondition().equals(ConnectionError.FRAMING_ERROR) || errorCondition.getCondition().equals(AmqpErrorCode.PROTON_IO_ERROR))) { - logger.verbose("connectionId[{}] There is no error condition and these are not framing errors. Error: {}", - errorCondition); + addErrorCondition(logger.atVerbose(), errorCondition) + .log("There is no error condition and these are not framing errors."); return; } @@ -148,14 +152,14 @@ public void onTransportError(Event event) { // If the proxy is not configured, or we are not connected to a host yet. if (proxyOptions == null || CoreUtils.isNullOrEmpty(hostname)) { - logger.verbose("connectionId[{}] Proxy is not configured and there is no host connected. Error: {}", - errorCondition); + addErrorCondition(logger.atVerbose(), errorCondition) + .log("Proxy is not configured and there is no host connected."); return; } final String[] hostNameParts = hostname.split(":"); if (hostNameParts.length != 2) { - logger.warning("connectionId[{}] Invalid hostname: {}", getConnectionId(), hostname); + logger.warning("Invalid hostname: {}", hostname); return; } @@ -163,7 +167,7 @@ public void onTransportError(Event event) { try { port = Integer.parseInt(hostNameParts[1]); } catch (NumberFormatException ignore) { - logger.warning("connectionId[{}] Invalid port number: {}", getConnectionId(), hostNameParts[1]); + logger.warning("Invalid port number: {}", hostNameParts[1]); return; } @@ -174,8 +178,7 @@ public void onTransportError(Event event) { final URI url = createURI(fullyQualifiedNamespace, port); final InetSocketAddress address = new InetSocketAddress(hostNameParts[0], port); - logger.error("connectionId[{}] Failed to connect to url: '{}', proxy host: '{}'", - getConnectionId(), url, address.getHostString(), ioException); + logger.atError().log("Failed to connect to url: '{}', proxy host: '{}'", url, address.getHostString(), ioException); final ProxySelector proxySelector = ProxySelector.getDefault(); if (proxySelector != null) { @@ -200,7 +203,9 @@ protected void addTransportLayers(final Event event, final TransportInternal tra transport.addTransportLayer(proxy); - logger.info("connectionId[{}] addProxyHandshake: hostname[{}]", getConnectionId(), amqpBrokerHostname); + logger.atInfo() + .addKeyValue(HOSTNAME_KEY, amqpBrokerHostname) + .log("addProxyHandshake"); } private com.microsoft.azure.proton.transport.proxy.ProxyConfiguration getProtonConfiguration() { @@ -227,8 +232,7 @@ private com.microsoft.azure.proton.transport.proxy.ProxyAuthenticationType getPr case NONE: return com.microsoft.azure.proton.transport.proxy.ProxyAuthenticationType.NONE; default: - throw logger.logExceptionAsError(new IllegalArgumentException(String.format( - "connectionId[%s]: This authentication type is unknown: %s", getConnectionId(), type.name()))); + throw logger.atError().log(new IllegalArgumentException(String.format("This authentication type is unknown: %s", type.name()))); } } diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java index 8c53f8a25d0b..815420f16cdf 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java @@ -58,8 +58,7 @@ void setup() { mocksCloseable = MockitoAnnotations.openMocks(this); channelProcessor = new AmqpChannelProcessor<>("connection-test", "test-path", - TestObject::getStates, retryPolicy, - new ClientLogger(AmqpChannelProcessor.class.getName() + "")); + TestObject::getStates, retryPolicy, "TestLogger"); } @AfterEach diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java index ef442a7b45d8..3626f5558b7f 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java @@ -95,8 +95,7 @@ public void setup(TestInfo testInfo) { final AmqpChannelProcessor requestResponseMono = Mono.defer(() -> Mono.just(requestResponseChannel)).subscribeWith(new AmqpChannelProcessor<>( - "foo", "bar", RequestResponseChannel::getEndpointStates, - retryPolicy, logger)); + "foo", "bar", RequestResponseChannel::getEndpointStates, retryPolicy, "TestLogger")); when(tokenManager.authorize()).thenReturn(Mono.just(1000L)); when(tokenManager.getAuthorizationResults()).thenReturn(tokenProviderResults.flux()); diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ReactorSenderTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ReactorSenderTest.java index c46cc3059a8f..b60bbb6474f5 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ReactorSenderTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ReactorSenderTest.java @@ -128,6 +128,7 @@ public void setup() throws IOException { when(handler.getLinkCredits()).thenReturn(Flux.just(100)); when(handler.getEndpointStates()).thenReturn(endpointStatePublisher.flux()); + when(handler.getConnectionId()).thenReturn("connectionId"); endpointStatePublisher.next(EndpointState.ACTIVE); when(tokenManager.getAuthorizationResults()).thenReturn(authorizationResults.flux()); diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java index 15235a1a2e51..b23b9ad76069 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java @@ -39,13 +39,12 @@ public void teardown() { @Test public void constructor() { // Arrange - final ClientLogger logger = new ClientLogger(TestHandler.class); final String connectionId = "id"; final String hostname = "hostname"; // Act - assertThrows(NullPointerException.class, () -> new TestHandler(null, hostname, logger)); - assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, null, logger)); + assertThrows(NullPointerException.class, () -> new TestHandler(null, hostname, TestHandler.class.getName())); + assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, null, TestHandler.class.getName())); assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, hostname, null)); } @@ -158,11 +157,11 @@ private static class TestHandler extends Handler { static final String HOSTNAME = "test-hostname"; TestHandler() { - super(CONNECTION_ID, HOSTNAME, new ClientLogger(TestHandler.class)); + super(CONNECTION_ID, HOSTNAME, TestHandler.class.getName()); } - TestHandler(String connectionId, String hostname, ClientLogger logger) { - super(connectionId, hostname, logger); + TestHandler(String connectionId, String hostname, String loggerName) { + super(connectionId, hostname, loggerName); } } } diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java index 18b2e8081aa3..a34cca0521cf 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java @@ -59,11 +59,11 @@ public class LinkHandlerTest { @Mock private Session session; - private final ClientLogger logger = new ClientLogger(LinkHandlerTest.class); + private final String loggerName = LinkHandlerTest.class.getName(); private final AmqpErrorCondition linkStolen = LINK_STOLEN; private final Symbol symbol = Symbol.getSymbol(linkStolen.getErrorCondition()); private final String description = "test-description"; - private final LinkHandler handler = new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH, logger); + private final LinkHandler handler = new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH, loggerName); private AutoCloseable mocksCloseable; @BeforeEach @@ -333,11 +333,11 @@ public void onLinkFinal() { public void constructor() { // Act assertThrows(NullPointerException.class, - () -> new MockLinkHandler(null, HOSTNAME, ENTITY_PATH, logger)); + () -> new MockLinkHandler(null, HOSTNAME, ENTITY_PATH, loggerName)); assertThrows(NullPointerException.class, - () -> new MockLinkHandler(CONNECTION_ID, null, ENTITY_PATH, logger)); + () -> new MockLinkHandler(CONNECTION_ID, null, ENTITY_PATH, loggerName)); assertThrows(NullPointerException.class, - () -> new MockLinkHandler(CONNECTION_ID, HOSTNAME, null, logger)); + () -> new MockLinkHandler(CONNECTION_ID, HOSTNAME, null, loggerName)); assertThrows(NullPointerException.class, () -> new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH, null)); } @@ -409,8 +409,8 @@ public void errorContextNoReferenceId(Map properties) { } private static final class MockLinkHandler extends LinkHandler { - MockLinkHandler(String connectionId, String hostname, String entityPath, ClientLogger logger) { - super(connectionId, hostname, entityPath, logger); + MockLinkHandler(String connectionId, String hostname, String entityPath, String loggerName) { + super(connectionId, hostname, entityPath, loggerName); } } } diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/util/FluxUtil.java b/sdk/core/azure-core/src/main/java/com/azure/core/util/FluxUtil.java index b4d7c586fada..f3f64860df45 100644 --- a/sdk/core/azure-core/src/main/java/com/azure/core/util/FluxUtil.java +++ b/sdk/core/azure-core/src/main/java/com/azure/core/util/FluxUtil.java @@ -11,6 +11,7 @@ import com.azure.core.implementation.RetriableDownloadFlux; import com.azure.core.implementation.TypeUtil; import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.logging.LoggingEventBuilder; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import reactor.core.CoreSubscriber; @@ -313,6 +314,18 @@ public static Mono monoError(ClientLogger logger, RuntimeException ex) { return Mono.error(logger.logExceptionAsError(Exceptions.propagate(ex))); } + /** + * Propagates a {@link RuntimeException} through the error channel of {@link Mono}. + * + * @param logBuilder The {@link LoggingEventBuilder} with context to log the exception. + * @param ex The {@link RuntimeException}. + * @param The return type. + * @return A {@link Mono} that terminates with error wrapping the {@link RuntimeException}. + */ + public static Mono monoError(LoggingEventBuilder logBuilder, RuntimeException ex) { + return Mono.error(logBuilder.log(Exceptions.propagate(ex))); + } + /** * Propagates a {@link RuntimeException} through the error channel of {@link Flux}. * From 2f86731082639376fc04cf2afd863f4594b47eb8 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Tue, 30 Nov 2021 13:57:29 -0800 Subject: [PATCH 02/17] clean up --- .../java/com/azure/core/amqp/AmqpShutdownSignal.java | 12 ++---------- .../amqp/implementation/AmqpExceptionHandler.java | 5 +++-- .../core/amqp/implementation/AmqpLoggingUtils.java | 9 +++++++++ .../core/amqp/implementation/ReactorConnection.java | 7 ++++--- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java index e9e3c9665b2a..cca9f7d4c895 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java @@ -48,21 +48,13 @@ public boolean isInitiatedByClient() { return isInitiatedByClient; } - public LoggingEventBuilder addContext(LoggingEventBuilder logBuilder) { - return logBuilder - .addKeyValue("isTransient", isTransient) - .addKeyValue("isInitiatedByClient", isInitiatedByClient) - .addKeyValue("shutdownMessage", message); - } - /** * Returns String representing this {@code AmqpShutdownSignal} signal. * - * To write logs, please use {@link AmqpShutdownSignal#addContext} instead. + * To write logs, please use {@link com.azure.core.amqp.implementation.AmqpLoggingUtils#addShutdownSignal(LoggingEventBuilder, AmqpShutdownSignal)}. */ @Override public String toString() { - return String.format(Locale.US, "%s, isTransient[%s], initiatedByClient[%s]", message, isTransient, - isInitiatedByClient); + return message; } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java index fa02f50883bd..9e88e86eaaa8 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java @@ -7,6 +7,8 @@ import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.ClientLogger; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addShutdownSignal; + /** * Handles exceptions generated by AMQP connections, sessions, and/or links. */ @@ -34,7 +36,6 @@ void onConnectionError(Throwable exception) { * @param shutdownSignal The shutdown signal that was received. */ void onConnectionShutdown(AmqpShutdownSignal shutdownSignal) { - shutdownSignal - .addContext(logger.atInfo()).log("Shutdown received"); + addShutdownSignal(logger.atInfo(), shutdownSignal).log("Shutdown received"); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java index d00d778047b7..a34faa72a2c7 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java @@ -1,5 +1,6 @@ package com.azure.core.amqp.implementation; +import com.azure.core.amqp.AmqpShutdownSignal; import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import reactor.core.publisher.SignalType; @@ -29,5 +30,13 @@ public static LoggingEventBuilder addErrorCondition(LoggingEventBuilder logBuild .addKeyValue(ERROR_CONDITION_KEY, errorCondition.getCondition()) .addKeyValue(ERROR_DESCRIPTION_KEY, errorCondition.getDescription()); } + + public static LoggingEventBuilder addShutdownSignal(LoggingEventBuilder logBuilder, AmqpShutdownSignal shutdownSignal) { + return logBuilder + .addKeyValue("isTransient", shutdownSignal.isTransient()) + .addKeyValue("isInitiatedByClient", shutdownSignal.isInitiatedByClient()) + // will call toString() if logging is enabled + .addKeyValue("shutdownMessage", shutdownSignal); + } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java index 851eb1bbdd4d..9d27d166874d 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java @@ -41,6 +41,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addShutdownSignal; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; @@ -423,12 +424,12 @@ public Mono closeAsync() { } Mono closeAsync(AmqpShutdownSignal shutdownSignal) { - shutdownSignal.addContext(logger.atInfo()).log("Disposing of ReactorConnection."); + addShutdownSignal(logger.atInfo(), shutdownSignal).log("Disposing of ReactorConnection."); final Sinks.EmitResult result = shutdownSignalSink.tryEmitValue(shutdownSignal); if (result.isFailure()) { // It's possible that another one was already emitted, so it's all good. - shutdownSignal.addContext(logger.atInfo()) + addShutdownSignal(logger.atInfo(), shutdownSignal) .addKeyValue(EMIT_RESULT_KEY, result) .log("Unable to emit shutdown signal."); } @@ -618,7 +619,7 @@ public void onConnectionError(Throwable exception) { @Override void onConnectionShutdown(AmqpShutdownSignal shutdownSignal) { - shutdownSignal.addContext(logger.atInfo()) + addShutdownSignal(logger.atInfo(), shutdownSignal) .addKeyValue(FULLY_QUALIFIED_NAMESPACE_KEY, getFullyQualifiedNamespace()) .log("onConnectionShutdown. Shutting down."); From adeb0d07446534390a1b142d72c72202d07599db Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Tue, 30 Nov 2021 14:08:46 -0800 Subject: [PATCH 03/17] clean up --- .../amqp/implementation/ActiveClientTokenManager.java | 3 +-- .../azure/core/amqp/implementation/ReactorReceiver.java | 2 +- .../core/amqp/implementation/RequestResponseChannel.java | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java index e9e3dce79ee8..bdadf85d4485 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java @@ -26,7 +26,7 @@ * Manages the re-authorization of the client to the token audience against the CBS node. */ public class ActiveClientTokenManager implements TokenManager { - private final ClientLogger logger; + private final ClientLogger logger = new ClientLogger(ActiveClientTokenManager.class); private final AtomicBoolean hasScheduled = new AtomicBoolean(); private final AtomicBoolean hasDisposed = new AtomicBoolean(); private final Mono cbsNode; @@ -42,7 +42,6 @@ public ActiveClientTokenManager(Mono cbsNode, String to this.cbsNode = cbsNode; this.tokenAudience = tokenAudience; this.scopes = scopes; - this.logger = new ClientLogger(ActiveClientTokenManager.class); } /** diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java index b34abf0d85d8..536135097655 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java @@ -191,7 +191,7 @@ public Flux receive() { @Override public Mono addCredits(int credits) { if (isDisposed()) { - return monoError(logger.atError(), Exceptions.propagate(new IllegalStateException("Cannot add credits to closed link: " + getLinkName()))); + return monoError(logger, Exceptions.propagate(new IllegalStateException("Cannot add credits to closed link: " + getLinkName()))); } return Mono.create(sink -> { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java index f49f12062efc..beb1cd4e4daf 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java @@ -292,17 +292,17 @@ public Mono sendWithAck(final Message message) { */ public Mono sendWithAck(final Message message, DeliveryState deliveryState) { if (isDisposed()) { - return monoError(logger.atError(), new IllegalStateException("Cannot send a message when request response channel is disposed.")); + return monoError(logger, new IllegalStateException("Cannot send a message when request response channel is disposed.")); } if (message == null) { - return monoError(logger.atError(), new NullPointerException("message cannot be null")); + return monoError(logger, new NullPointerException("message cannot be null")); } if (message.getMessageId() != null) { - return monoError(logger.atError(), new IllegalArgumentException("message.getMessageId() should be null")); + return monoError(logger, new IllegalArgumentException("message.getMessageId() should be null")); } if (message.getReplyTo() != null) { - return monoError(logger.atError(), new IllegalArgumentException("message.getReplyTo() should be null")); + return monoError(logger, new IllegalArgumentException("message.getReplyTo() should be null")); } final UnsignedLong messageId = UnsignedLong.valueOf(requestId.incrementAndGet()); From e689306fe2168740162a46ed405fbb2c2c64473d Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Wed, 1 Dec 2021 15:42:01 -0800 Subject: [PATCH 04/17] clean up --- .../checks/ThrowFromClientLoggerCheck.java | 36 +++++++++++++-- .../ThrowFromClientLoggerCheckTest.java | 4 +- .../DirectThrowExceptionTestData.java | 17 +++++++ .../azure/core/amqp/AmqpShutdownSignal.java | 2 - .../ActiveClientTokenManager.java | 37 ++++++++-------- .../implementation/AmqpChannelProcessor.java | 10 +++-- .../implementation/AmqpExceptionHandler.java | 1 - .../amqp/implementation/AmqpLoggingUtils.java | 44 +++++++++++++++++++ .../amqp/implementation/ClientConstants.java | 20 ++++----- .../implementation/ManagementChannel.java | 9 ++-- .../implementation/ReactorConnection.java | 9 ++-- .../implementation/ReactorDispatcher.java | 6 +-- .../amqp/implementation/ReactorExecutor.java | 6 +-- .../amqp/implementation/ReactorReceiver.java | 25 ++++++----- .../amqp/implementation/ReactorSender.java | 11 +++-- .../amqp/implementation/ReactorSession.java | 4 +- .../RequestResponseChannel.java | 8 +++- .../handler/ConnectionHandler.java | 5 +-- .../handler/CustomIOHandler.java | 6 +-- .../amqp/implementation/handler/Handler.java | 6 +-- .../implementation/handler/LinkHandler.java | 1 - .../handler/ReactorHandler.java | 5 +-- .../handler/ReceiveLinkHandler.java | 2 +- .../handler/SendLinkHandler.java | 4 +- .../handler/SessionHandler.java | 5 +-- .../handler/WebSocketsConnectionHandler.java | 7 +-- .../WebSocketsProxyConnectionHandler.java | 5 +-- .../AmqpChannelProcessorTest.java | 1 - .../implementation/handler/HandlerTest.java | 1 - .../handler/LinkHandlerTest.java | 1 - 30 files changed, 191 insertions(+), 107 deletions(-) diff --git a/eng/code-quality-reports/src/main/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck.java b/eng/code-quality-reports/src/main/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck.java index 1a9729f10848..08caf8094ddc 100644 --- a/eng/code-quality-reports/src/main/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck.java +++ b/eng/code-quality-reports/src/main/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck.java @@ -7,6 +7,7 @@ import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.FullIdent; import com.puppycrawl.tools.checkstyle.api.TokenTypes; + import java.util.ArrayDeque; import java.util.Collections; import java.util.Queue; @@ -25,12 +26,15 @@ public class ThrowFromClientLoggerCheck extends AbstractCheck { private static final String LOGGER_LOG_EXCEPTION_AS_ERROR = "logger.logExceptionAsError"; private static final String LOGGER_LOG_THROWABLE_AS_ERROR = "logger.logThrowableAsError"; + private static final String LOGGING_BUILDER_LOG_THROWABLE_AS_ERROR = "logger.atError().log"; private static final String LOGGER_LOG_EXCEPTION_AS_WARNING = "logger.logExceptionAsWarning"; - private static final String LOGGER_LOG_THROWABLE_AS_WARNING = "logger.logThrowableAsWarning"; + private static final String LOGGER_LOG_THROWABLE_AS_WARNING = "logger.logThrowableAsWarning"; + private static final String LOGGING_BUILDER_LOG_THROWABLE_AS_WARNING = "logger.atWarning().log"; + static final String THROW_LOGGER_EXCEPTION_MESSAGE = String.format("Directly throwing an exception is disallowed. " - + "Must throw through \"ClientLogger\" API, either of \"%s\", \"%s\", \"%s\", or \"%s\" where \"logger\" is " + + "Must throw through \"ClientLogger\" API, either of \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\" where \"logger\" is " + "type of ClientLogger from Azure Core package.", LOGGER_LOG_EXCEPTION_AS_ERROR, - LOGGER_LOG_THROWABLE_AS_ERROR, LOGGER_LOG_EXCEPTION_AS_WARNING, LOGGER_LOG_THROWABLE_AS_WARNING); + LOGGER_LOG_THROWABLE_AS_ERROR, LOGGING_BUILDER_LOG_THROWABLE_AS_ERROR, LOGGER_LOG_EXCEPTION_AS_WARNING, LOGGER_LOG_THROWABLE_AS_WARNING, LOGGING_BUILDER_LOG_THROWABLE_AS_WARNING); // A LIFO queue stores the static status of class, skip this ThrowFromClientLoggerCheck if the class is static private final Queue classStaticDeque = Collections.asLifoQueue(new ArrayDeque<>()); @@ -94,9 +98,11 @@ public void visitToken(DetailAST token) { case TokenTypes.LITERAL_THROW: // Skip check if the throw exception from static class, constructor or static method if (classStaticDeque.isEmpty() || classStaticDeque.peek() || isInConstructor - || methodStaticDeque.isEmpty() || methodStaticDeque.peek()) { + || methodStaticDeque.isEmpty() || methodStaticDeque.peek() + || findLogMethodIdentifier(token)) { return; } + DetailAST methodCallToken = token.findFirstToken(TokenTypes.EXPR).findFirstToken(TokenTypes.METHOD_CALL); if (methodCallToken == null) { @@ -118,4 +124,26 @@ public void visitToken(DetailAST token) { break; } } + + /** + * + **/ + private static boolean findLogMethodIdentifier(DetailAST root) { + for (DetailAST ast = root.getFirstChild(); ast != null; ast = ast.getNextSibling()) { + if (ast.getType() == TokenTypes.METHOD_CALL) { + DetailAST dot = ast.findFirstToken(TokenTypes.DOT); + if (dot != null) { + DetailAST ident = dot.findFirstToken(TokenTypes.IDENT); + if ("log".equals(ident.getText())) { + return true; + } + } + } + if (findLogMethodIdentifier(ast)) { + return true; + } + } + + return false; + } } diff --git a/eng/code-quality-reports/src/test/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheckTest.java b/eng/code-quality-reports/src/test/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheckTest.java index fba2d2215799..d2cb7f377a36 100644 --- a/eng/code-quality-reports/src/test/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheckTest.java +++ b/eng/code-quality-reports/src/test/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheckTest.java @@ -9,6 +9,7 @@ import org.junit.Before; import org.junit.Test; + import static com.azure.tools.checkstyle.checks.ThrowFromClientLoggerCheck.THROW_LOGGER_EXCEPTION_MESSAGE; public class ThrowFromClientLoggerCheckTest extends AbstractModuleTestSupport { @@ -32,7 +33,8 @@ protected String getPackageLocation() { @Test public void directThrowExceptionTestData() throws Exception { String[] expected = { - expectedErrorMessage(12, 9) + expectedErrorMessage(12, 9), + expectedErrorMessage(60, 9) }; verify(checker, getPath("DirectThrowExceptionTestData.java"), expected); } diff --git a/eng/code-quality-reports/src/test/resources/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck/DirectThrowExceptionTestData.java b/eng/code-quality-reports/src/test/resources/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck/DirectThrowExceptionTestData.java index 7b00762ac3a8..7fb461af0880 100644 --- a/eng/code-quality-reports/src/test/resources/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck/DirectThrowExceptionTestData.java +++ b/eng/code-quality-reports/src/test/resources/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck/DirectThrowExceptionTestData.java @@ -42,4 +42,21 @@ public void validLogExceptionAsWarning() { public void validLogThrowableAsWarning() { throw logger.logThrowableAsWarning(new RuntimeException("Error message.")); } + + public void validThrowExceptionWithBuilder() { + throw logger.atError().log(Exceptions.propagate(new IllegalStateException("Error Messages"))); + } + + public void validThrowExceptionWithBuilderAndContext() { + throw logger.atError().addKeyValuePair("foo", "bar").log(new RuntimeException("Error message.")); + } + + public void validThrowExceptionWithBuilderAndContextAdvanced() { + LoggingEventBuilder builder = logger.atError(); + throw builder.addKeyValuePair("foo", "bar").log(new RuntimeException("Error message.")); + } + + public void invalidLoggingBuilderNoLogCall() { + throw logger.atError(); + } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java index cca9f7d4c895..24e3a9a72db4 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java @@ -5,8 +5,6 @@ import com.azure.core.util.logging.LoggingEventBuilder; -import java.util.Locale; - /** * Represents a signal that caused the AMQP connection to shutdown. */ diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java index bdadf85d4485..fb39f6e7fd20 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java @@ -19,7 +19,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; /** @@ -176,29 +175,29 @@ private Disposable scheduleRefreshTokenTask(Duration initialRefresh) { return false; }); }, error -> { - logger.atError() - .addKeyValue("scopes", scopes) - .addKeyValue("audience", tokenAudience) - .log("Error occurred while refreshing token that is not retriable. Not scheduling" + logger.atError() + .addKeyValue("scopes", scopes) + .addKeyValue("audience", tokenAudience) + .log("Error occurred while refreshing token that is not retriable. Not scheduling" + " refresh task. Use ActiveClientTokenManager.authorize() to schedule task again.", error); - // This hasn't been disposed yet. - if (!hasDisposed.getAndSet(true)) { - hasScheduled.set(false); - durationSource.emitComplete((signalType, emitResult) -> { - addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) - .log("Could not close durationSource."); + // This hasn't been disposed yet. + if (!hasDisposed.getAndSet(true)) { + hasScheduled.set(false); + durationSource.emitComplete((signalType, emitResult) -> { + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + .log("Could not close durationSource."); - return false; - }); + return false; + }); - authorizationResults.emitError(error, (signalType, emitResult) -> { - addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) + authorizationResults.emitError(error, (signalType, emitResult) -> { + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) .log("Could not emit authorization error.", error); - return false; - }); - } - }); + return false; + }); + } + }); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index 0f95feaec32a..2a6640992646 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -9,7 +9,6 @@ import com.azure.core.amqp.exception.AmqpException; import com.azure.core.util.AsyncCloseable; import com.azure.core.util.logging.ClientLogger; -import com.azure.core.util.logging.ClientLogger; import org.reactivestreams.Processor; import org.reactivestreams.Subscription; import reactor.core.CoreSubscriber; @@ -19,6 +18,7 @@ import reactor.core.publisher.Operators; import java.time.Duration; +import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentLinkedDeque; @@ -60,9 +60,11 @@ public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, this.endpointStatesFunction = Objects.requireNonNull(endpointStatesFunction, "'endpointStates' cannot be null."); this.retryPolicy = Objects.requireNonNull(retryPolicy, "'retryPolicy' cannot be null."); - this.logger = new ClientLogger(loggerName, Map.of( - FULLY_QUALIFIED_NAMESPACE_KEY, Objects.requireNonNull(fullyQualifiedNamespace, "'fullyQualifiedNamespace' cannot be null."), - ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null."))); + + Map loggingContext = new HashMap<>(); + loggingContext.put(FULLY_QUALIFIED_NAMESPACE_KEY, Objects.requireNonNull(fullyQualifiedNamespace, "'fullyQualifiedNamespace' cannot be null.")); + loggingContext.put(ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null.")); + this.logger = new ClientLogger(loggerName, loggingContext); this.errorContext = new AmqpErrorContext(fullyQualifiedNamespace); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java index 9e88e86eaaa8..a8c61303cb1f 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpExceptionHandler.java @@ -5,7 +5,6 @@ import com.azure.core.amqp.AmqpShutdownSignal; import com.azure.core.util.logging.ClientLogger; -import com.azure.core.util.logging.ClientLogger; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addShutdownSignal; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java index a34faa72a2c7..030ac2568deb 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.core.amqp.implementation; import com.azure.core.amqp.AmqpShutdownSignal; @@ -6,19 +9,51 @@ import reactor.core.publisher.SignalType; import reactor.core.publisher.Sinks; +import java.util.HashMap; +import java.util.Map; + +import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ERROR_CONDITION_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ERROR_DESCRIPTION_KEY; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; import static com.azure.core.amqp.implementation.ClientConstants.SIGNAL_TYPE_KEY; +/** + * Utils for contextual logging. + */ public class AmqpLoggingUtils { + + /** + * Creates logging context with connectionId. + */ + public static Map createContextWithConnectionId(String connectionId) { + Map globalLoggingContext = new HashMap<>(); + globalLoggingContext.put(CONNECTION_ID_KEY, connectionId); + + return globalLoggingContext; + } + + /** + * Adds {@link SignalType} under {@code signalType} key and {@link reactor.core.publisher.Sinks.EmitResult} + * under {@code emitResult} key to the {@link LoggingEventBuilder} + + * @return updated {@link LoggingEventBuilder} for chaining. + */ public static LoggingEventBuilder addSignalTypeAndResult(LoggingEventBuilder logBuilder, SignalType signalType, Sinks.EmitResult result) { return logBuilder .addKeyValue(SIGNAL_TYPE_KEY, signalType) .addKeyValue(EMIT_RESULT_KEY, result); } + /** + * Adds {@link ErrorCondition} to the {@link LoggingEventBuilder}. Writes the {@code getCondition()} under {@code errorCondition} key + * and {@code getDescription()} under {@code errorDescription} keys. + * + * If errorCondition is {@code null} writes {@code n/a}. + + * @return updated {@link LoggingEventBuilder} for chaining. + */ public static LoggingEventBuilder addErrorCondition(LoggingEventBuilder logBuilder, ErrorCondition errorCondition) { if (errorCondition == null) { return logBuilder @@ -31,6 +66,15 @@ public static LoggingEventBuilder addErrorCondition(LoggingEventBuilder logBuild .addKeyValue(ERROR_DESCRIPTION_KEY, errorCondition.getDescription()); } + /** + * Adds {@link AmqpShutdownSignal} to the {@link LoggingEventBuilder}. Writes + *
    + *
  • {@code isTransient()} under {@code isTransient}
  • + *
  • {@code isInitiatedByClient()} under {@code isInitiatedByClient}
  • + *
  • {@code toString()} under {@code shutdownMessage}
  • + *
+ * @return updated {@link LoggingEventBuilder} for chaining. + */ public static LoggingEventBuilder addShutdownSignal(LoggingEventBuilder logBuilder, AmqpShutdownSignal shutdownSignal) { return logBuilder .addKeyValue("isTransient", shutdownSignal.isTransient()) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java index 04210a669653..6dad9c4b002f 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java @@ -13,16 +13,16 @@ public final class ClientConstants { public static final Duration SERVER_BUSY_WAIT_TIME = Duration.ofSeconds(4); // Logging context keys - public final static String CONNECTION_ID_KEY = "connectionId"; - public final static String LINK_NAME_KEY = "linkName"; - public final static String ENTITY_PATH_KEY = "entityPath"; - public final static String SESSION_NAME_KEY = "sessionName"; - public final static String FULLY_QUALIFIED_NAMESPACE_KEY = "namespace"; - public final static String ERROR_CONDITION_KEY = "errorCondition"; - public final static String ERROR_DESCRIPTION_KEY = "errorDescription"; - public final static String EMIT_RESULT_KEY = "emitResult"; - public final static String SIGNAL_TYPE_KEY = "signalType"; - public final static String HOSTNAME_KEY = "hostName"; + public static final String CONNECTION_ID_KEY = "connectionId"; + public static final String LINK_NAME_KEY = "linkName"; + public static final String ENTITY_PATH_KEY = "entityPath"; + public static final String SESSION_NAME_KEY = "sessionName"; + public static final String FULLY_QUALIFIED_NAMESPACE_KEY = "namespace"; + public static final String ERROR_CONDITION_KEY = "errorCondition"; + public static final String ERROR_DESCRIPTION_KEY = "errorDescription"; + public static final String EMIT_RESULT_KEY = "emitResult"; + public static final String SIGNAL_TYPE_KEY = "signalType"; + public static final String HOSTNAME_KEY = "hostName"; /** * The default maximum allowable size, in bytes, for a batch to be sent. diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ManagementChannel.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ManagementChannel.java index 853a99384519..f2c15f34cdb1 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ManagementChannel.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ManagementChannel.java @@ -12,13 +12,12 @@ import com.azure.core.amqp.models.AmqpAnnotatedMessage; import com.azure.core.amqp.models.DeliveryOutcome; import com.azure.core.util.logging.ClientLogger; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.amqp.transport.DeliveryState; import org.apache.qpid.proton.message.Message; -import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; import reactor.core.publisher.SynchronousSink; +import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -42,7 +41,11 @@ public ManagementChannel(AmqpChannelProcessor createChan this.fullyQualifiedNamespace = Objects.requireNonNull(fullyQualifiedNamespace, "'fullyQualifiedNamespace' cannot be null."); this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); - this.logger = new ClientLogger(ManagementChannel.class, Map.of(ENTITY_PATH_KEY, entityPath)); + + Map globalLoggingContext = new HashMap<>(); + globalLoggingContext.put(ENTITY_PATH_KEY, entityPath); + this.logger = new ClientLogger(ManagementChannel.class, globalLoggingContext); + this.tokenManager = Objects.requireNonNull(tokenManager, "'tokenManager' cannot be null."); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java index 9d27d166874d..f1a537155cbe 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java @@ -15,7 +15,6 @@ import com.azure.core.amqp.implementation.handler.ConnectionHandler; import com.azure.core.amqp.implementation.handler.SessionHandler; import com.azure.core.util.logging.ClientLogger; -import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.transport.ReceiverSettleMode; import org.apache.qpid.proton.amqp.transport.SenderSettleMode; import org.apache.qpid.proton.engine.BaseHandler; @@ -43,12 +42,12 @@ import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addShutdownSignal; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.FULLY_QUALIFIED_NAMESPACE_KEY; import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; -import static com.azure.core.amqp.implementation.ClientConstants.FULLY_QUALIFIED_NAMESPACE_KEY; import static com.azure.core.amqp.implementation.ClientConstants.SESSION_NAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.SIGNAL_TYPE_KEY; import static com.azure.core.util.FluxUtil.monoError; @@ -114,7 +113,7 @@ public ReactorConnection(String connectionId, ConnectionOptions connectionOption this.connectionOptions = connectionOptions; this.reactorProvider = reactorProvider; this.connectionId = connectionId; - this.logger = new ClientLogger(ReactorConnection.class, Map.of(CONNECTION_ID_KEY, connectionId)); + this.logger = new ClientLogger(ReactorConnection.class, createContextWithConnectionId(connectionId)); this.handlerProvider = handlerProvider; this.tokenManagerProvider = Objects.requireNonNull(tokenManagerProvider, "'tokenManagerProvider' cannot be null."); @@ -484,7 +483,7 @@ Mono closeAsync(AmqpShutdownSignal shutdownSignal) { private synchronized void closeConnectionWork() { if (connection == null) { isClosedMono.emitEmpty((signalType, emitResult) -> { - addSignalTypeAndResult(logger.atInfo(), signalType, emitResult) + addSignalTypeAndResult(logger.atInfo(), signalType, emitResult) .log("Unable to complete closeMono."); return false; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java index ac4ce4baefb3..5bfee1bfc13d 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java @@ -6,7 +6,6 @@ import com.azure.core.amqp.AmqpShutdownSignal; import com.azure.core.amqp.implementation.handler.DispatchHandler; import com.azure.core.util.logging.ClientLogger; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.reactor.Reactor; import org.apache.qpid.proton.reactor.Selectable; @@ -19,13 +18,12 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.Pipe; import java.time.Duration; -import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; /** * The following utility class is used to generate an event to hook into {@link Reactor}'s event delegation pattern. It @@ -69,7 +67,7 @@ public ReactorDispatcher(final String connectionId, final Reactor reactor, final this.workQueue = new ConcurrentLinkedQueue<>(); this.onClose = new CloseHandler(); this.workScheduler = new WorkScheduler(); - this.logger = new ClientLogger(ReactorDispatcher.class , Map.of(CONNECTION_ID_KEY, connectionId)); + this.logger = new ClientLogger(ReactorDispatcher.class, createContextWithConnectionId(connectionId)); // The Proton-J reactor goes quiescent when there is no work to do, and it only wakes up when a Selectable (by // default, the network connection) signals that data is available. // diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java index 8aee5ae47dbc..98e168be3724 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java @@ -18,14 +18,13 @@ import java.nio.channels.UnresolvedAddressException; import java.time.Duration; import java.util.Locale; -import java.util.Map; import java.util.Objects; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; /** * Schedules the proton-j reactor to continuously run work. @@ -50,7 +49,8 @@ class ReactorExecutor implements AsyncCloseable { this.timeout = Objects.requireNonNull(timeout, "'timeout' cannot be null."); this.exceptionHandler = Objects.requireNonNull(exceptionHandler, "'exceptionHandler' cannot be null."); this.hostname = Objects.requireNonNull(hostname, "'hostname' cannot be null."); - this.logger = new ClientLogger(ReactorExecutor.class, Map.of(CONNECTION_ID_KEY, Objects.requireNonNull(connectionId, "'connectionId' cannot be null."))); + Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); + this.logger = new ClientLogger(ReactorExecutor.class, createContextWithConnectionId(connectionId)); } /** diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java index 536135097655..e4d6c45272b4 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java @@ -6,12 +6,10 @@ import com.azure.core.amqp.AmqpConnection; import com.azure.core.amqp.AmqpEndpointState; import com.azure.core.amqp.AmqpRetryOptions; -import com.azure.core.amqp.AmqpShutdownSignal; import com.azure.core.amqp.exception.AmqpErrorCondition; import com.azure.core.amqp.implementation.handler.ReceiveLinkHandler; import com.azure.core.util.AsyncCloseable; import com.azure.core.util.logging.ClientLogger; -import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.amqp.Symbol; @@ -37,13 +35,12 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; -import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; import static com.azure.core.util.FluxUtil.monoError; /** @@ -73,7 +70,11 @@ protected ReactorReceiver(AmqpConnection amqpConnection, String entityPath, Rece this.handler = handler; this.tokenManager = tokenManager; this.dispatcher = dispatcher; - this.logger = new ClientLogger(ReactorReceiver.class, Map.of(CONNECTION_ID_KEY, handler.getConnectionId(), LINK_NAME_KEY, this.handler.getLinkName())); + + Map loggingContext = createContextWithConnectionId(handler.getConnectionId()); + loggingContext.put(LINK_NAME_KEY, this.handler.getLinkName()); + this.logger = new ClientLogger(ReactorReceiver.class, loggingContext); + // Delivered messages are not published on another scheduler because we want the settlement method that happens // in decodeDelivery to take place and since proton-j is not thread safe, it could end up with hundreds of // backed up deliveries waiting to be settled. (Which, consequently, ends up in a FAIL_OVERFLOW error from @@ -160,12 +161,12 @@ protected ReactorReceiver(AmqpConnection amqpConnection, String entityPath, Rece }).subscribe(response -> logger.atVerbose() .addKeyValue("response", response) - .log("Token refreshed.") - , error -> { - }, () -> { - logger.atVerbose() - .addKeyValue(ENTITY_PATH_KEY, entityPath) - .log("Authorization completed."); + .log("Token refreshed."), + error -> { }, + () -> { + logger.atVerbose() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log("Authorization completed."); closeAsync("Authorization completed. Disposing.", null).subscribe(); }), diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java index 8048c1e0ab2f..398c023237c7 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java @@ -58,7 +58,7 @@ import static com.azure.core.amqp.exception.AmqpErrorCondition.NOT_ALLOWED; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.MAX_AMQP_HEADER_SIZE_BYTES; @@ -131,7 +131,12 @@ class ReactorSender implements AmqpSendLink, AsyncCloseable, AutoCloseable { String connectionId = handler.getConnectionId() == null ? NOT_APPLICABLE : handler.getConnectionId(); String linkName = getLinkName() == null ? NOT_APPLICABLE : getLinkName(); - this.logger = new ClientLogger(ReactorSender.class, Map.of(CONNECTION_ID_KEY, connectionId, ENTITY_PATH_KEY, entityPath, LINK_NAME_KEY, linkName)); + + Map loggingContext = createContextWithConnectionId(connectionId); + loggingContext.put(LINK_NAME_KEY, linkName); + loggingContext.put(ENTITY_PATH_KEY, entityPath); + this.logger = new ClientLogger(ReactorSender.class, loggingContext); + this.activeTimeoutMessage = String.format( "ReactorSender connectionId[%s] linkName[%s]: Waiting for send and receive handler to be ACTIVE", handler.getConnectionId(), handler.getLinkName()); @@ -338,7 +343,7 @@ public Mono getLinkSize() { if (remoteMaxMessageSize != null) { linkSize = remoteMaxMessageSize.intValue(); } else { - logger.warning("Could not get the getRemoteMaxMessageSize. Returning current link size: {}",linkSize); + logger.warning("Could not get the getRemoteMaxMessageSize. Returning current link size: {}", linkSize); } return linkSize; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java index 5e71fef941a6..a5e7e10df09a 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java @@ -52,7 +52,7 @@ import java.util.function.Consumer; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; @@ -128,7 +128,7 @@ public ReactorSession(AmqpConnection amqpConnection, Session session, SessionHan "ReactorSession connectionId[%s], session[%s]: Retries exhausted waiting for ACTIVE endpoint state.", sessionHandler.getConnectionId(), sessionName); - this.logger = new ClientLogger(ReactorSession.class, Map.of(CONNECTION_ID_KEY, this.sessionHandler.getConnectionId())); + this.logger = new ClientLogger(ReactorSession.class, createContextWithConnectionId(this.sessionHandler.getConnectionId())); this.endpointStates = sessionHandler.getEndpointStates() .map(state -> { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java index beb1cd4e4daf..fba17564fde9 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java @@ -47,7 +47,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; import static com.azure.core.util.FluxUtil.monoError; @@ -123,7 +123,11 @@ protected RequestResponseChannel(AmqpConnection amqpConnection, String connectio AmqpRetryOptions retryOptions, ReactorHandlerProvider handlerProvider, ReactorProvider provider, MessageSerializer messageSerializer, SenderSettleMode senderSettleMode, ReceiverSettleMode receiverSettleMode) { - this.logger = new ClientLogger(RequestResponseChannel.class, Map.of(CONNECTION_ID_KEY, connectionId, LINK_NAME_KEY, linkName)); + + Map loggingContext = createContextWithConnectionId(connectionId); + loggingContext.put(LINK_NAME_KEY, linkName); + this.logger = new ClientLogger(RequestResponseChannel.class, loggingContext); + this.retryOptions = retryOptions; this.provider = provider; this.senderSettleMode = senderSettleMode; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java index daa074bac0e1..b99ce0492867 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java @@ -10,7 +10,6 @@ import com.azure.core.util.ClientOptions; import com.azure.core.util.CoreUtils; import com.azure.core.util.UserAgentUtil; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; @@ -229,7 +228,7 @@ public void onTransportError(Event event) { final ErrorCondition condition = transport.getCondition(); addErrorCondition(logger.atWarning(), condition) - .addKeyValue(HOSTNAME_KEY, connection.getHostname()) + .addKeyValue(HOSTNAME_KEY, connection != null ? connection.getHostname() : ClientConstants.NOT_APPLICABLE) .log("onTransportError"); if (connection != null) { @@ -247,7 +246,7 @@ public void onTransportClosed(Event event) { final ErrorCondition condition = transport.getCondition(); addErrorCondition(logger.atInfo(), condition) - .addKeyValue(HOSTNAME_KEY, connection.getHostname()) + .addKeyValue(HOSTNAME_KEY, connection != null ? connection.getHostname() : ClientConstants.NOT_APPLICABLE) .log("onTransportClosed"); if (connection != null) { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java index 4b102e3954b7..f39bb9e70cb0 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java @@ -9,9 +9,7 @@ import org.apache.qpid.proton.engine.Transport; import org.apache.qpid.proton.reactor.impl.IOHandler; -import java.util.Map; - -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; @@ -19,7 +17,7 @@ public class CustomIOHandler extends IOHandler { private final ClientLogger logger; public CustomIOHandler(final String connectionId) { - this.logger = new ClientLogger(CustomIOHandler.class, Map.of(CONNECTION_ID_KEY, connectionId)); + this.logger = new ClientLogger(CustomIOHandler.class, createContextWithConnectionId(connectionId)); } @Override diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java index 1282f0c2d9cd..0e308e9e1122 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java @@ -10,12 +10,11 @@ import reactor.core.publisher.Sinks; import java.io.Closeable; -import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; /** * Base class for all proton-j handlers. @@ -43,8 +42,7 @@ public abstract class Handler extends BaseHandler implements Closeable { Handler(final String connectionId, final String hostname, final String loggerName) { this.connectionId = Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); this.hostname = Objects.requireNonNull(hostname, "'hostname' cannot be null."); - this.logger = new ClientLogger(Objects.requireNonNull(loggerName, "'loggerName' cannot be null."), - Map.of(CONNECTION_ID_KEY, connectionId)); + this.logger = new ClientLogger(Objects.requireNonNull(loggerName, "'loggerName' cannot be null."), createContextWithConnectionId(connectionId)); } /** diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java index 9c584aff7ea8..7ee02b2d973b 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java @@ -6,7 +6,6 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.LinkErrorContext; import com.azure.core.amqp.implementation.ExceptionUtil; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; import org.apache.qpid.proton.engine.Event; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReactorHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReactorHandler.java index 35d7774a3385..0ad459c6431e 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReactorHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReactorHandler.java @@ -8,10 +8,9 @@ import org.apache.qpid.proton.engine.Event; import org.apache.qpid.proton.reactor.Reactor; -import java.util.Map; import java.util.Objects; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; /** * Handler that sets the timeout period for waiting for Selectables. @@ -27,7 +26,7 @@ public class ReactorHandler extends BaseHandler { public ReactorHandler(final String connectionId) { Objects.requireNonNull(connectionId); - this.logger = new ClientLogger(ReactorHandler.class, Map.of(CONNECTION_ID_KEY, connectionId)); + this.logger = new ClientLogger(ReactorHandler.class, createContextWithConnectionId(connectionId)); } @Override diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java index ac3b4b8582df..08146c596425 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java @@ -140,7 +140,7 @@ public void onDelivery(Event event) { .addKeyValue(ENTITY_PATH_KEY, entityPath) .addKeyValue(LINK_NAME_KEY, linkName) .addKeyValue("updatedLinkCredit", link.getCredit()) - .addKeyValue("remoteCredit",link.getRemoteCredit()) + .addKeyValue("remoteCredit", link.getRemoteCredit()) .addKeyValue("delivery.isSettled", delivery.isSettled()) .log("Was already settled."); } else { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java index af73ef5f951d..0ee4cafd4a17 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java @@ -23,6 +23,7 @@ import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; +import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; /** * Handler that receives events from its corresponding {@link Sender}. Handlers must be associated to a {@link Sender} @@ -107,14 +108,13 @@ public void onLinkRemoteOpen(Event event) { .addKeyValue(ENTITY_PATH_KEY, entityPath); if (link.getRemoteTarget() != null) { - // TODO escape logBuilder.addKeyValue("remoteTarget", link.getRemoteTarget()); if (!isRemoteActive.getAndSet(true)) { onNext(EndpointState.ACTIVE); } } else { - logBuilder.addKeyValue("remoteTarget", (String)null) + logBuilder.addKeyValue("remoteTarget", NOT_APPLICABLE) .addKeyValue("action", "waitingForError"); } logBuilder.log("onLinkRemoteOpen"); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java index aa6d92ad6898..b6ba8e3c429a 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java @@ -101,13 +101,12 @@ public void onSessionLocalClose(Event e) { @Override public void onSessionRemoteClose(Event e) { final Session session = e.getSession(); + final ErrorCondition condition = session != null ? session.getRemoteCondition() : null; - addErrorCondition(logger.atInfo(), session.getCondition()) + addErrorCondition(logger.atInfo(), condition) .addKeyValue(SESSION_NAME_KEY, sessionName) .log("onSessionRemoteClose"); - ErrorCondition condition = session != null ? session.getRemoteCondition() : null; - if (session != null && session.getLocalState() != EndpointState.CLOSED) { addErrorCondition(logger.atInfo(), condition) .addKeyValue(SESSION_NAME_KEY, sessionName) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java index 37d4255c9572..9e4d59541a32 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java @@ -9,10 +9,7 @@ import org.apache.qpid.proton.engine.Event; import org.apache.qpid.proton.engine.SslPeerDetails; import org.apache.qpid.proton.engine.impl.TransportInternal; - -import java.util.Map; - -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; /** @@ -38,7 +35,7 @@ public class WebSocketsConnectionHandler extends ConnectionHandler { public WebSocketsConnectionHandler(String connectionId, ConnectionOptions connectionOptions, SslPeerDetails peerDetails) { super(connectionId, connectionOptions, peerDetails); - logger = new ClientLogger(WebSocketsConnectionHandler.class, Map.of(CONNECTION_ID_KEY, connectionId)); + logger = new ClientLogger(WebSocketsConnectionHandler.class, createContextWithConnectionId(connectionId)); } /** diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java index fadb892ca552..3c919d8ccffc 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java @@ -27,11 +27,10 @@ import java.net.URI; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; @@ -65,7 +64,7 @@ public WebSocketsProxyConnectionHandler(String connectionId, ConnectionOptions c this.proxyOptions = Objects.requireNonNull(proxyOptions, "'proxyConfiguration' cannot be null."); this.fullyQualifiedNamespace = connectionOptions.getFullyQualifiedNamespace(); this.amqpBrokerHostname = connectionOptions.getFullyQualifiedNamespace() + ":" + connectionOptions.getPort(); - this.logger = new ClientLogger(WebSocketsProxyConnectionHandler.class, Map.of(CONNECTION_ID_KEY, connectionId)); + this.logger = new ClientLogger(WebSocketsProxyConnectionHandler.class, createContextWithConnectionId(connectionId)); if (proxyOptions.isProxyAddressConfigured()) { this.connectionHostname = (InetSocketAddress) proxyOptions.getProxyAddress().address(); } else { diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java index 815420f16cdf..53c3b1b4ed69 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java @@ -8,7 +8,6 @@ import com.azure.core.amqp.exception.AmqpErrorCondition; import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; -import com.azure.core.util.logging.ClientLogger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java index b23b9ad76069..546e2c2326b8 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java @@ -5,7 +5,6 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.engine.EndpointState; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java index a34cca0521cf..6d8cf3470164 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java @@ -7,7 +7,6 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; import com.azure.core.amqp.exception.LinkErrorContext; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; From dece26bcbdb02b7350c81efebb2dfebb10057252 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Wed, 1 Dec 2021 16:42:10 -0800 Subject: [PATCH 05/17] mroe cleanup --- .../azure/core/amqp/AmqpShutdownSignal.java | 2 +- .../ActiveClientTokenManager.java | 2 +- .../implementation/AmqpChannelProcessor.java | 2 +- .../AzureTokenManagerProvider.java | 2 +- .../implementation/ReactorConnection.java | 7 +++-- .../implementation/ReactorDispatcher.java | 19 ++++++------- .../amqp/implementation/ReactorReceiver.java | 27 +++---------------- .../amqp/implementation/ReactorSender.java | 5 ++-- .../amqp/implementation/ReactorSession.java | 4 +-- .../RequestResponseChannel.java | 8 +++--- .../handler/ConnectionHandler.java | 8 +++--- .../handler/CustomIOHandler.java | 2 +- .../implementation/handler/LinkHandler.java | 3 ++- .../handler/ReceiveLinkHandler.java | 7 ++--- .../WebSocketsProxyConnectionHandler.java | 6 ++--- 15 files changed, 41 insertions(+), 63 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java index 24e3a9a72db4..88bd392265fd 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/AmqpShutdownSignal.java @@ -47,7 +47,7 @@ public boolean isInitiatedByClient() { } /** - * Returns String representing this {@code AmqpShutdownSignal} signal. + * Returns String representing the message of this {@code AmqpShutdownSignal} signal. * * To write logs, please use {@link com.azure.core.amqp.implementation.AmqpLoggingUtils#addShutdownSignal(LoggingEventBuilder, AmqpShutdownSignal)}. */ diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java index fb39f6e7fd20..2a91370a2488 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java @@ -148,7 +148,7 @@ private Disposable scheduleRefreshTokenTask(Duration initialRefresh) { durationSource.emitNext(lastRefresh, (signalType, emitResult) -> { addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) .addKeyValue("lastRefresh", lastRefresh) - .log("Could not emit."); + .log("Could not emit lastRefresh."); return false; }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index 2a6640992646..ee8340730601 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -231,7 +231,7 @@ public void subscribe(CoreSubscriber actual) { actual.onSubscribe(Operators.emptySubscription()); actual.onError(lastError); } else { - Operators.error(actual, logger.atError().log(new IllegalStateException("Cannot subscribe. Processor is already terminated."))); + Operators.error(actual, logger.logExceptionAsError(new IllegalStateException("Cannot subscribe. Processor is already terminated."))); } return; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AzureTokenManagerProvider.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AzureTokenManagerProvider.java index fe4488f92fc1..9a015af447f8 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AzureTokenManagerProvider.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AzureTokenManagerProvider.java @@ -66,7 +66,7 @@ public String getScopesFromResource(String resource) { } else if (CbsAuthorizationType.SHARED_ACCESS_SIGNATURE.equals(authorizationType)) { return String.format(Locale.US, TOKEN_AUDIENCE_FORMAT, fullyQualifiedNamespace, resource); } else { - throw logger.atError().log(new IllegalArgumentException(String.format(Locale.US, + throw logger.logExceptionAsError(new IllegalArgumentException(String.format(Locale.US, "'%s' is not supported authorization type for token audience.", authorizationType))); } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java index f1a537155cbe..bf0ee0f18d03 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java @@ -137,8 +137,7 @@ public ReactorConnection(String connectionId, ConnectionOptions connectionOption }) .doOnError(error -> { if (isDisposed.getAndSet(true)) { - logger.atVerbose() - .log("Connection was already disposed: Error occurred while connection was starting.", error); + logger.verbose("Connection was already disposed: Error occurred while connection was starting.", error); } else { closeAsync(new AmqpShutdownSignal(false, false, String.format( "Error occurred while connection was starting. Error: %s", error))).subscribe(); @@ -207,7 +206,7 @@ public Mono getManagementNode(String entityPath) { return tokenManager.authorize().thenReturn(managementNodes.compute(entityPath, (key, current) -> { if (current != null) { - logger.atInfo().addKeyValue(ENTITY_PATH_KEY, entityPath).log("A management node exists already, returning it."); + logger.info("A management node exists already, returning it."); // Close the token manager we had created during this because it is unneeded now. tokenManager.close(); @@ -302,7 +301,7 @@ public Mono createSession(String sessionName) { logger.atVerbose() .addKeyValue(SESSION_NAME_KEY, sessionName) - .log("connectionId[{}] sessionName[{}]: Complete. Removing and disposing session."); + .log("Complete. Removing and disposing session."); removeSession(key); }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java index 5bfee1bfc13d..77bdd2338f47 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorDispatcher.java @@ -127,7 +127,7 @@ private void throwIfSchedulerError() { final RejectedExecutionException rejectedException = this.reactor.attachments() .get(RejectedExecutionException.class, RejectedExecutionException.class); if (rejectedException != null) { - throw logger.atWarning().log(new RejectedExecutionException( + throw logger.logExceptionAsWarning(new RejectedExecutionException( "Underlying Reactor was already disposed. Should not continue dispatching work to this. " + rejectedException.getMessage(), rejectedException)); } @@ -135,7 +135,7 @@ private void throwIfSchedulerError() { // throw when the pipe is in closed state - in which case, // signalling the new event-dispatch will fail if (!this.ioSignal.sink().isOpen()) { - throw logger.atWarning().log(new RejectedExecutionException( + throw logger.logExceptionAsWarning(new RejectedExecutionException( "ReactorDispatcher instance is closed. Should not continue dispatching work to this reactor.")); } } @@ -148,8 +148,7 @@ private void signalWorkQueue() throws IOException { } } catch (ClosedChannelException ignorePipeClosedDuringReactorShutdown) { if (!isClosed.get()) { - logger.atWarning() - .log("signalWorkQueue failed before reactor closed.", ignorePipeClosedDuringReactorShutdown); + logger.warning("signalWorkQueue failed before reactor closed.", ignorePipeClosedDuringReactorShutdown); shutdownSignal.emitError(new RuntimeException(String.format( "connectionId[%s] IO Sink was interrupted before reactor closed.", connectionId), ignorePipeClosedDuringReactorShutdown), Sinks.EmitFailureHandler.FAIL_FAST); @@ -180,19 +179,17 @@ public void run(Selectable selectable) { } } catch (ClosedChannelException ignorePipeClosedDuringReactorShutdown) { if (!isClosed.get()) { - logger.atWarning() - .log("WorkScheduler.run() failed before reactor was closed.", ignorePipeClosedDuringReactorShutdown); + logger.warning("WorkScheduler.run() failed before reactor was closed.", ignorePipeClosedDuringReactorShutdown); shutdownSignal.emitError(new RuntimeException(String.format( "connectionId[%s] IO Source was interrupted before reactor closed.", connectionId), ignorePipeClosedDuringReactorShutdown), Sinks.EmitFailureHandler.FAIL_FAST); } else { - logger.atVerbose() - .log("WorkScheduler.run() failed with an error. Can be ignored.", ignorePipeClosedDuringReactorShutdown); + logger.verbose("WorkScheduler.run() failed with an error. Can be ignored.", ignorePipeClosedDuringReactorShutdown); } break; } catch (IOException ioException) { - shutdownSignal.emitError(logger.atError().log(new RuntimeException( + shutdownSignal.emitError(logger.logExceptionAsError(new RuntimeException( "WorkScheduler.run() failed with an error.", ioException)), Sinks.EmitFailureHandler.FAIL_FAST); break; } @@ -232,7 +229,7 @@ public void run(Selectable selectable) { ioSignal.sink().close(); } } catch (IOException ioException) { - logger.atError().log("CloseHandler.sink().close() failed with an error.", ioException); + logger.error("CloseHandler.sink().close() failed with an error.", ioException); } workScheduler.run(null); @@ -242,7 +239,7 @@ public void run(Selectable selectable) { ioSignal.source().close(); } } catch (IOException ioException) { - logger.atError().log("CloseHandler.source().close() failed with an error.", ioException); + logger.error("CloseHandler.source().close() failed with an error.", ioException); } } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java index e4d6c45272b4..130cf68df478 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorReceiver.java @@ -10,7 +10,6 @@ import com.azure.core.amqp.implementation.handler.ReceiveLinkHandler; import com.azure.core.util.AsyncCloseable; import com.azure.core.util.logging.ClientLogger; -import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; @@ -20,7 +19,6 @@ import org.apache.qpid.proton.message.Message; import reactor.core.Disposable; import reactor.core.Disposables; -import reactor.core.Exceptions; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -38,7 +36,6 @@ import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; -import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; import static com.azure.core.util.FluxUtil.monoError; @@ -172,8 +169,7 @@ protected ReactorReceiver(AmqpConnection amqpConnection, String entityPath, Rece }), amqpConnection.getShutdownSignals().flatMap(signal -> { - logger.atVerbose() - .log("Shutdown signal received."); + logger.verbose("Shutdown signal received."); return closeAsync("Connection shutdown.", null); }).subscribe()); //@formatter:on @@ -192,7 +188,7 @@ public Flux receive() { @Override public Mono addCredits(int credits) { if (isDisposed()) { - return monoError(logger, Exceptions.propagate(new IllegalStateException("Cannot add credits to closed link: " + getLinkName()))); + return monoError(logger, new IllegalStateException("Cannot add credits to closed link: " + getLinkName())); } return Mono.create(sink -> { @@ -299,15 +295,13 @@ Mono closeAsync(String message, ErrorCondition errorCondition) { try { dispatcher.invoke(closeReceiver); } catch (IOException e) { - logger.atWarning() - .log("IO sink was closed when scheduling work. Manually invoking and completing close.", e); + logger.warning("IO sink was closed when scheduling work. Manually invoking and completing close.", e); closeReceiver.run(); completeClose(); } catch (RejectedExecutionException e) { // Not logging error here again because we have to log the exception when we throw it. - logger.atInfo() - .log("RejectedExecutionException when scheduling on ReactorDispatcher. Manually invoking and completing close."); + logger.info("RejectedExecutionException when scheduling on ReactorDispatcher. Manually invoking and completing close."); closeReceiver.run(); completeClose(); @@ -322,7 +316,6 @@ private void completeClose() { isClosedMono.emitEmpty((signalType, result) -> { addSignalTypeAndResult(logger.atWarning(), signalType, result) - .addKeyValue(ENTITY_PATH_KEY, entityPath) .log("Unable to emit shutdown signal."); return false; }); @@ -337,18 +330,6 @@ private void completeClose() { receiver.free(); } - public LoggingEventBuilder addContext(LoggingEventBuilder logBuilder) { - return logBuilder - .addKeyValue(CONNECTION_ID_KEY, receiver.getName()) - .addKeyValue(ENTITY_PATH_KEY, entityPath) - .addKeyValue(LINK_NAME_KEY, getLinkName()); - } - - /** - * Returns String representing this {@code ReactorReceiver} signal. - * - * To write logs, please use {@link ReactorReceiver#addContext} instead. - */ @Override public String toString() { return String.format("connectionId: [%s] entity path: [%s] linkName: [%s]", receiver.getName(), entityPath, diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java index 398c023237c7..90a16e855a95 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java @@ -58,6 +58,7 @@ import static com.azure.core.amqp.exception.AmqpErrorCondition.NOT_ALLOWED; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; @@ -569,7 +570,7 @@ private void processDeliveredMessage(Delivery delivery) { if (workItem == null) { logger.atVerbose() .addKeyValue(DELIVERY_TAG_KEY, deliveryTag) - .log("Mismatch (or send timed out."); + .log("Mismatch (or send timed out)."); return; } else if (workItem.isDeliveryStateProvided()) { @@ -655,7 +656,7 @@ private void cleanupFailedSend(final RetriableWorkItem workItem, final Exception private void completeClose() { isClosedMono.emitEmpty((signalType, result) -> { - logger.warning("Unable to emit shutdown signal."); + addSignalTypeAndResult(logger.atWarning(), signalType, result).log("Unable to emit shutdown signal."); return false; }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java index a5e7e10df09a..ca8dcf47d4fd 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java @@ -266,13 +266,13 @@ Mono closeAsync(String message, ErrorCondition errorCondition, boolean dis addErrorCondition(logger.atVerbose(), errorCondition) .addKeyValue(SESSION_NAME_KEY, sessionName) - .log("Disposing session. {}", message != null ? message : ""); + .log("Setting error condition and disposing session. {}", message); return Mono.fromRunnable(() -> { try { provider.getReactorDispatcher().invoke(() -> disposeWork(errorCondition, disposeLinks)); } catch (IOException e) { - addErrorCondition(logger.atInfo(), errorCondition) + logger.atInfo() .addKeyValue(SESSION_NAME_KEY, sessionName) .log("Error while scheduling work. Manually disposing.", e); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java index fba17564fde9..7743937df527 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java @@ -218,7 +218,7 @@ protected RequestResponseChannel(AmqpConnection amqpConnection, String connectio this.receiveLink.open(); }); } catch (IOException | RejectedExecutionException e) { - throw logger.atError().log(new RuntimeException("Unable to open send and receive link.", e)); + throw logger.logExceptionAsError(new RuntimeException("Unable to open send and receive link.", e)); } } @@ -422,8 +422,7 @@ private void onTerminalState(String handlerName) { } final int remaining = pendingLinkTerminations.decrementAndGet(); - logger.atVerbose() - .log("{} disposed. Remaining: {}", handlerName, remaining); + logger.verbose("{} disposed. Remaining: {}", handlerName, remaining); if (remaining == 0) { subscriptions.dispose(); @@ -467,8 +466,7 @@ private synchronized void updateEndpointState(AmqpEndpointState sendLinkState, A // Terminate the unconfirmed MonoSinks by notifying the given error. private void terminateUnconfirmedSends(Throwable error) { - logger.atVerbose() - .log("Terminating {} unconfirmed sends (reason: {}).", unconfirmedSends.size(), error.getMessage()); + logger.verbose("Terminating {} unconfirmed sends (reason: {}).", unconfirmedSends.size(), error.getMessage()); Map.Entry> next; int count = 0; while ((next = unconfirmedSends.pollFirstEntry()) != null) { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java index b99ce0492867..e3855805430f 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java @@ -133,7 +133,7 @@ protected void addTransportLayers(Event event, TransportInternal transport) { try { defaultSslContext = SSLContext.getDefault(); } catch (NoSuchAlgorithmException e) { - throw logger.atError().log(new RuntimeException("Default SSL algorithm not found in JRE. Please check your JRE setup.", e)); + throw logger.logExceptionAsError(new RuntimeException("Default SSL algorithm not found in JRE. Please check your JRE setup.", e)); } } @@ -154,7 +154,7 @@ protected void addTransportLayers(Event event, TransportInternal transport) { logger.warning("'{}' is not secure.", verifyMode); sslDomain.setPeerAuthentication(SslDomain.VerifyMode.ANONYMOUS_PEER); } else { - throw logger.atError().log(new UnsupportedOperationException( + throw logger.logExceptionAsError(new UnsupportedOperationException( "verifyMode is not supported: " + verifyMode)); } @@ -209,7 +209,7 @@ public void onConnectionUnbound(Event event) { final Connection connection = event.getConnection(); logger.atInfo() .addKeyValue(HOSTNAME_KEY, connection.getHostname()) - .addKeyValue("localState", connection.getLocalState()) + .addKeyValue("state", connection.getLocalState()) .addKeyValue("remoteState", connection.getRemoteState()) .log("onConnectionUnbound"); @@ -325,7 +325,7 @@ private void notifyErrorContext(Connection connection, ErrorCondition condition) } if (condition == null) { - throw logger.atError().log(new IllegalStateException("notifyErrorContext does not have an ErrorCondition.")); + throw logger.logExceptionAsError(new IllegalStateException("notifyErrorContext does not have an ErrorCondition.")); } // if the remote-peer abruptly closes the connection without issuing close frame issue one diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java index f39bb9e70cb0..a83c8bb6c2fb 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/CustomIOHandler.java @@ -42,7 +42,7 @@ public void onUnhandled(Event event) { try { super.onUnhandled(event); } catch (NullPointerException e) { - logger.atError().log("Exception occurred when handling event in super.", e); + logger.error("Exception occurred when handling event in super.", e); } } } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java index 7ee02b2d973b..9bc36927b9f2 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java @@ -101,7 +101,8 @@ private void handleRemoteLinkClosed(final String eventName, final Event event) { if (link.getLocalState() != EndpointState.CLOSED) { logger.atInfo() .addKeyValue(LINK_NAME_KEY, link.getName()) - .addKeyValue("state", link.getLocalState()); + .addKeyValue("state", link.getLocalState()) + .log("Local link state is not closed."); link.setCondition(condition); link.close(); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java index 08146c596425..308a0d7db020 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java @@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; @@ -142,12 +143,12 @@ public void onDelivery(Event event) { .addKeyValue("updatedLinkCredit", link.getCredit()) .addKeyValue("remoteCredit", link.getRemoteCredit()) .addKeyValue("delivery.isSettled", delivery.isSettled()) - .log("Was already settled."); + .log("onDelivery. Was already settled."); } else { logger.atWarning() .addKeyValue(ENTITY_PATH_KEY, entityPath) .addKeyValue("delivery.isSettled", delivery.isSettled()) - .log("Settled delivery with no delivery"); + .log("Settled delivery with no link."); } } else { if (link.getLocalState() == EndpointState.CLOSED) { @@ -164,7 +165,7 @@ public void onDelivery(Event event) { logger.atWarning() .addKeyValue(ENTITY_PATH_KEY, entityPath) .addKeyValue(LINK_NAME_KEY, linkName) - .addKeyValue("emitResult", emitResult) + .addKeyValue(EMIT_RESULT_KEY, emitResult) .log("Could not emit delivery. {}", delivery); if (emitResult == Sinks.EmitResult.FAIL_OVERFLOW diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java index 3c919d8ccffc..38ecbdd66f87 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java @@ -71,7 +71,7 @@ public WebSocketsProxyConnectionHandler(String connectionId, ConnectionOptions c final URI serviceUri = createURI(connectionOptions.getHostname(), connectionOptions.getPort()); final ProxySelector proxySelector = ProxySelector.getDefault(); if (proxySelector == null) { - throw logger.atError().log(new IllegalStateException("ProxySelector should not be null.")); + throw logger.logExceptionAsError(new IllegalStateException("ProxySelector should not be null.")); } final List proxies = proxySelector.select(serviceUri); @@ -79,7 +79,7 @@ public WebSocketsProxyConnectionHandler(String connectionId, ConnectionOptions c final String formatted = String.format("No proxy address found for: '%s'. Available: %s.", serviceUri, proxies.stream().map(Proxy::toString).collect(Collectors.joining(", "))); - throw logger.atError().log(new IllegalStateException(formatted)); + throw logger.logExceptionAsError(new IllegalStateException(formatted)); } final Proxy proxy = proxies.get(0); @@ -231,7 +231,7 @@ private com.microsoft.azure.proton.transport.proxy.ProxyAuthenticationType getPr case NONE: return com.microsoft.azure.proton.transport.proxy.ProxyAuthenticationType.NONE; default: - throw logger.atError().log(new IllegalArgumentException(String.format("This authentication type is unknown: %s", type.name()))); + throw logger.logExceptionAsError(new IllegalArgumentException(String.format("This authentication type is unknown: %s", type.name()))); } } From d631a91e5214bae770adb6a3e67e03fc8b1d4184 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Fri, 3 Dec 2021 11:44:07 -0800 Subject: [PATCH 06/17] create logger in handler implementations --- .../amqp/implementation/AmqpLoggingUtils.java | 2 + .../handler/ConnectionHandler.java | 5 +- .../amqp/implementation/handler/Handler.java | 7 ++- .../implementation/handler/LinkHandler.java | 7 +-- .../handler/ReceiveLinkHandler.java | 4 +- .../handler/SendLinkHandler.java | 4 +- .../handler/SessionHandler.java | 4 +- .../implementation/handler/HandlerTest.java | 12 +++-- .../handler/LinkHandlerTest.java | 15 +++--- .../azure-core-tracing-opentelemetry/pom.xml | 2 + .../OpenTelemetryHttpPolicy.java | 10 ++-- .../OpenTelemetrySpanSuppressionHelper.java | 52 ++++--------------- 12 files changed, 52 insertions(+), 72 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java index 030ac2568deb..44bebb6072fc 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Objects; import static com.azure.core.amqp.implementation.ClientConstants.CONNECTION_ID_KEY; import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; @@ -28,6 +29,7 @@ public class AmqpLoggingUtils { * Creates logging context with connectionId. */ public static Map createContextWithConnectionId(String connectionId) { + Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); Map globalLoggingContext = new HashMap<>(); globalLoggingContext.put(CONNECTION_ID_KEY, connectionId); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java index e3855805430f..c3d96ed58dad 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java @@ -10,6 +10,7 @@ import com.azure.core.util.ClientOptions; import com.azure.core.util.CoreUtils; import com.azure.core.util.UserAgentUtil; +import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; @@ -29,6 +30,7 @@ import java.util.Objects; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.FULLY_QUALIFIED_NAMESPACE_KEY; import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; @@ -60,7 +62,8 @@ public class ConnectionHandler extends Handler { public ConnectionHandler(final String connectionId, final ConnectionOptions connectionOptions, SslPeerDetails peerDetails) { super(connectionId, - Objects.requireNonNull(connectionOptions, "'connectionOptions' cannot be null.").getHostname(), ConnectionHandler.class.getName()); + Objects.requireNonNull(connectionOptions, "'connectionOptions' cannot be null.").getHostname(), + new ClientLogger(ConnectionHandler.class, createContextWithConnectionId(connectionId))); add(new Handshaker()); this.connectionOptions = connectionOptions; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java index 0e308e9e1122..95d389965fbb 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java @@ -14,7 +14,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; /** * Base class for all proton-j handlers. @@ -35,14 +34,14 @@ public abstract class Handler extends BaseHandler implements Closeable { * @param hostname Hostname of the connection. This could be the DNS hostname or the IP address of the * connection. Usually of the form {@literal ".service.windows.net"} but can change if the * messages are brokered through an intermediary. - * @param loggerName loggerName to use. + * @param logger Logger to use for messages. * * @throws NullPointerException if {@code connectionId}, {@code hostname}, or {@code logger} is null. */ - Handler(final String connectionId, final String hostname, final String loggerName) { + Handler(final String connectionId, final String hostname, final ClientLogger logger) { this.connectionId = Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); this.hostname = Objects.requireNonNull(hostname, "'hostname' cannot be null."); - this.logger = new ClientLogger(Objects.requireNonNull(loggerName, "'loggerName' cannot be null."), createContextWithConnectionId(connectionId)); + this.logger = Objects.requireNonNull(logger, "'logger' cannot be null."); } /** diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java index 9bc36927b9f2..df5842bad7d6 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java @@ -6,6 +6,7 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.LinkErrorContext; import com.azure.core.amqp.implementation.ExceptionUtil; +import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; import org.apache.qpid.proton.engine.Event; @@ -35,13 +36,13 @@ abstract class LinkHandler extends Handler { * connection. Usually of the form {@literal ".service.windows.net"} but can change if the * messages are brokered through an intermediary. * @param entityPath The address within the message broker for this link. - * @param loggerName Logger name to use for messages. + * @param logger Logger to use for messages. * * @throws NullPointerException if {@code connectionId}, {@code hostname}, {@code entityPath}, or {@code logger} is * null. */ - LinkHandler(String connectionId, String hostname, String entityPath, String loggerName) { - super(connectionId, hostname, loggerName); + LinkHandler(String connectionId, String hostname, String entityPath, ClientLogger logger) { + super(connectionId, hostname, logger); this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java index 308a0d7db020..e0af148f3bd4 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java @@ -3,6 +3,7 @@ package com.azure.core.amqp.implementation.handler; +import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.messaging.Modified; @@ -25,6 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; @@ -51,7 +53,7 @@ public class ReceiveLinkHandler extends LinkHandler { private final String entityPath; public ReceiveLinkHandler(String connectionId, String hostname, String linkName, String entityPath) { - super(connectionId, hostname, entityPath, ReceiveLinkHandler.class.getName()); + super(connectionId, hostname, entityPath, new ClientLogger(ReceiveLinkHandler.class, createContextWithConnectionId(connectionId))); this.linkName = Objects.requireNonNull(linkName, "'linkName' cannot be null."); this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java index 0ee4cafd4a17..81b2a24c25cd 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java @@ -3,6 +3,7 @@ package com.azure.core.amqp.implementation.handler; +import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.engine.BaseHandler; import org.apache.qpid.proton.engine.Delivery; @@ -20,6 +21,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; @@ -45,7 +47,7 @@ public class SendLinkHandler extends LinkHandler { private final Sinks.Many deliveryProcessor = Sinks.many().multicast().onBackpressureBuffer(); public SendLinkHandler(String connectionId, String hostname, String linkName, String entityPath) { - super(connectionId, hostname, entityPath, SendLinkHandler.class.getName()); + super(connectionId, hostname, entityPath, new ClientLogger(SendLinkHandler.class, createContextWithConnectionId(connectionId))); this.linkName = Objects.requireNonNull(linkName, "'linkName' cannot be null."); this.entityPath = entityPath; } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java index b6ba8e3c429a..7cad0c3b2991 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java @@ -8,6 +8,7 @@ import com.azure.core.amqp.exception.SessionErrorContext; import com.azure.core.amqp.implementation.ExceptionUtil; import com.azure.core.amqp.implementation.ReactorDispatcher; +import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; @@ -20,6 +21,7 @@ import java.util.concurrent.RejectedExecutionException; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.SESSION_NAME_KEY; public class SessionHandler extends Handler { @@ -29,7 +31,7 @@ public class SessionHandler extends Handler { public SessionHandler(String connectionId, String hostname, String sessionName, ReactorDispatcher reactorDispatcher, Duration openTimeout) { - super(connectionId, hostname, SessionHandler.class.getName()); + super(connectionId, hostname, new ClientLogger(SessionHandler.class, createContextWithConnectionId(connectionId))); this.sessionName = sessionName; this.openTimeout = openTimeout; this.reactorDispatcher = reactorDispatcher; diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java index 546e2c2326b8..0ee6b6c9fb59 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java @@ -5,6 +5,7 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; +import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.engine.EndpointState; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -12,6 +13,7 @@ import org.mockito.Mockito; import reactor.test.StepVerifier; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -42,8 +44,8 @@ public void constructor() { final String hostname = "hostname"; // Act - assertThrows(NullPointerException.class, () -> new TestHandler(null, hostname, TestHandler.class.getName())); - assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, null, TestHandler.class.getName())); + assertThrows(NullPointerException.class, () -> new TestHandler(null, hostname, new ClientLogger(TestHandler.class))); + assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, null, new ClientLogger(TestHandler.class))); assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, hostname, null)); } @@ -156,11 +158,11 @@ private static class TestHandler extends Handler { static final String HOSTNAME = "test-hostname"; TestHandler() { - super(CONNECTION_ID, HOSTNAME, TestHandler.class.getName()); + super(CONNECTION_ID, HOSTNAME, new ClientLogger(TestHandler.class, createContextWithConnectionId(CONNECTION_ID))); } - TestHandler(String connectionId, String hostname, String loggerName) { - super(connectionId, hostname, loggerName); + TestHandler(String connectionId, String hostname, ClientLogger logger) { + super(connectionId, hostname, logger); } } } diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java index 6d8cf3470164..7649fe7bc1ab 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java @@ -7,6 +7,7 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; import com.azure.core.amqp.exception.LinkErrorContext; +import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; @@ -58,11 +59,11 @@ public class LinkHandlerTest { @Mock private Session session; - private final String loggerName = LinkHandlerTest.class.getName(); private final AmqpErrorCondition linkStolen = LINK_STOLEN; private final Symbol symbol = Symbol.getSymbol(linkStolen.getErrorCondition()); private final String description = "test-description"; - private final LinkHandler handler = new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH, loggerName); + private final ClientLogger logger = new ClientLogger(LinkHandlerTest.class); + private final LinkHandler handler = new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH, logger); private AutoCloseable mocksCloseable; @BeforeEach @@ -332,11 +333,11 @@ public void onLinkFinal() { public void constructor() { // Act assertThrows(NullPointerException.class, - () -> new MockLinkHandler(null, HOSTNAME, ENTITY_PATH, loggerName)); + () -> new MockLinkHandler(null, HOSTNAME, ENTITY_PATH, logger)); assertThrows(NullPointerException.class, - () -> new MockLinkHandler(CONNECTION_ID, null, ENTITY_PATH, loggerName)); + () -> new MockLinkHandler(CONNECTION_ID, null, ENTITY_PATH, logger)); assertThrows(NullPointerException.class, - () -> new MockLinkHandler(CONNECTION_ID, HOSTNAME, null, loggerName)); + () -> new MockLinkHandler(CONNECTION_ID, HOSTNAME, null, logger)); assertThrows(NullPointerException.class, () -> new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH, null)); } @@ -408,8 +409,8 @@ public void errorContextNoReferenceId(Map properties) { } private static final class MockLinkHandler extends LinkHandler { - MockLinkHandler(String connectionId, String hostname, String entityPath, String loggerName) { - super(connectionId, hostname, entityPath, loggerName); + MockLinkHandler(String connectionId, String hostname, String entityPath, ClientLogger logger) { + super(connectionId, hostname, entityPath, logger); } } } diff --git a/sdk/core/azure-core-tracing-opentelemetry/pom.xml b/sdk/core/azure-core-tracing-opentelemetry/pom.xml index 642fe28930c4..aac8a1033288 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/pom.xml +++ b/sdk/core/azure-core-tracing-opentelemetry/pom.xml @@ -46,6 +46,7 @@ opentelemetry-api 1.0.0 + com.azure azure-core @@ -149,6 +150,7 @@ io.opentelemetry:opentelemetry-sdk:[1.0.0] io.opentelemetry:opentelemetry-exporter-logging:[1.0.0] io.opentelemetry:opentelemetry-exporter-jaeger:[1.0.0] + io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:[1.9.0-alpha] diff --git a/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/OpenTelemetryHttpPolicy.java b/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/OpenTelemetryHttpPolicy.java index 65bcf0b74b2c..fc5399914ce0 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/OpenTelemetryHttpPolicy.java +++ b/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/OpenTelemetryHttpPolicy.java @@ -20,6 +20,7 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; import reactor.core.CoreSubscriber; @@ -226,6 +227,7 @@ private static io.opentelemetry.context.Context getTraceContextOrCurrent(HttpPip * * OpenTelemetry reactor auto-instrumentation will take care of the cold path. */ + @SuppressWarnings("try") static final class ScalarPropagatingMono extends Mono { public static final Mono INSTANCE = new ScalarPropagatingMono(); @@ -238,12 +240,8 @@ private ScalarPropagatingMono() { public void subscribe(CoreSubscriber actual) { Context traceContext = actual.currentContext().getOrDefault(REACTOR_PARENT_TRACE_CONTEXT_KEY, null); if (traceContext != null) { - Object agentContext = OpenTelemetrySpanSuppressionHelper.registerClientSpan(traceContext); - AutoCloseable closeable = OpenTelemetrySpanSuppressionHelper.makeCurrent(agentContext, traceContext); - actual.onSubscribe(Operators.scalarSubscription(actual, value)); - try { - closeable.close(); - } catch (Throwable ignored) { + try (Scope s = OpenTelemetrySpanSuppressionHelper.registerClientSpan(traceContext).makeCurrent()) { + actual.onSubscribe(Operators.scalarSubscription(actual, value)); } } else { actual.onSubscribe(Operators.scalarSubscription(actual, value)); diff --git a/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/implementation/OpenTelemetrySpanSuppressionHelper.java b/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/implementation/OpenTelemetrySpanSuppressionHelper.java index 170004e76472..888b0822744f 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/implementation/OpenTelemetrySpanSuppressionHelper.java +++ b/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/implementation/OpenTelemetrySpanSuppressionHelper.java @@ -19,31 +19,19 @@ public class OpenTelemetrySpanSuppressionHelper { private static boolean agentDiscovered; private static final ClientLogger LOGGER = new ClientLogger(OpenTelemetrySpanSuppressionHelper.class); - private static Method getAgentContextMethod; + private static Method setSpanKeyMethod; private static Object clientSpanKey; - private static Method getAgentSpanMethod; - private static Method agentContextMakeCurrentMethod; static { agentDiscovered = true; try { - Class agentContextStorageClass = Class.forName("io.opentelemetry.javaagent.instrumentation.opentelemetryapi.context.AgentContextStorage"); - Class agentContextClass = Class.forName("io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Context"); - Class spanKeyClass = Class.forName("io.opentelemetry.javaagent.shaded.instrumentation.api.instrumenter.SpanKey"); - Class bridgingClass = Class.forName("io.opentelemetry.javaagent.instrumentation.opentelemetryapi.trace.Bridging"); - Class agentSpanClass = Class.forName("io.opentelemetry.javaagent.shaded.io.opentelemetry.api.trace.Span"); + Class spanKeyClass = Class.forName("io.opentelemetry.instrumentation.api.instrumenter.SpanKey"); - getAgentContextMethod = agentContextStorageClass.getDeclaredMethod("getAgentContext", io.opentelemetry.context.Context.class); - getAgentSpanMethod = bridgingClass.getDeclaredMethod("toAgentOrNull", io.opentelemetry.api.trace.Span.class); clientSpanKey = spanKeyClass.getDeclaredField("ALL_CLIENTS").get(null); - setSpanKeyMethod = spanKeyClass.getDeclaredMethod("storeInContext", agentContextClass, agentSpanClass); - agentContextMakeCurrentMethod = agentContextClass.getMethod("makeCurrent"); + setSpanKeyMethod = spanKeyClass.getDeclaredMethod("storeInContext", Context.class, Span.class); - if (getAgentContextMethod.getReturnType() != agentContextClass - || getAgentSpanMethod.getReturnType() != agentSpanClass - || setSpanKeyMethod.getReturnType() != agentContextClass - || !AutoCloseable.class.isAssignableFrom(agentContextMakeCurrentMethod.getReturnType())) { + if (setSpanKeyMethod.getReturnType() != Context.class) { agentDiscovered = false; } @@ -58,42 +46,20 @@ public class OpenTelemetrySpanSuppressionHelper { * @param traceContext OpenTelemetry context with client span * @return Agent context or null when agent is not running or doesn't behave as expected. */ - public static Object registerClientSpan(Context traceContext) { + public static Context registerClientSpan(Context traceContext) { Objects.requireNonNull(traceContext, "'traceContext' cannot be null"); if (agentDiscovered) { try { - return setSpanKeyMethod.invoke( + return (Context) setSpanKeyMethod.invoke( clientSpanKey, - getAgentContextMethod.invoke(null, traceContext), - getAgentSpanMethod.invoke(null, Span.fromContext(traceContext))); + traceContext, + Span.fromContext(traceContext)); } catch (Throwable t) { // should not happen, If it does, we'll log it once. LOGGER.warning("Failed to register client span on OpenTelemetry agent"); agentDiscovered = false; } } - return null; - } - - /** - * Makes passed agent context current. Falls back to passed trace context when agent is not running - * or doesn't behave as expected. - * @param agentContext agent context instance obtained from {@link OpenTelemetrySpanSuppressionHelper#registerClientSpan} - * @param traceContext Regular OpenTelemetry context to fallback to. - * @return scope to be closed in the same thread as it was started. - */ - public static AutoCloseable makeCurrent(Object agentContext, Context traceContext) { - Objects.requireNonNull(traceContext, "'traceContext' cannot be null"); - if (agentDiscovered && agentContext != null) { - try { - return (AutoCloseable) agentContextMakeCurrentMethod.invoke(agentContext); - } catch (Throwable t) { - // should not happen, If it does, we'll log it once. - LOGGER.warning("Failed to make OpenTelemetry agent context current"); - agentDiscovered = false; - } - } - - return traceContext.makeCurrent(); + return traceContext; } } From 4a264df48783d5986cfaa5367206e97d0e094735 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Fri, 3 Dec 2021 12:08:24 -0800 Subject: [PATCH 07/17] oops --- .../azure-core-tracing-opentelemetry/pom.xml | 2 - .../OpenTelemetryHttpPolicy.java | 10 ++-- .../OpenTelemetrySpanSuppressionHelper.java | 52 +++++++++++++++---- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/sdk/core/azure-core-tracing-opentelemetry/pom.xml b/sdk/core/azure-core-tracing-opentelemetry/pom.xml index aac8a1033288..642fe28930c4 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/pom.xml +++ b/sdk/core/azure-core-tracing-opentelemetry/pom.xml @@ -46,7 +46,6 @@ opentelemetry-api 1.0.0 - com.azure azure-core @@ -150,7 +149,6 @@ io.opentelemetry:opentelemetry-sdk:[1.0.0] io.opentelemetry:opentelemetry-exporter-logging:[1.0.0] io.opentelemetry:opentelemetry-exporter-jaeger:[1.0.0] - io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:[1.9.0-alpha] diff --git a/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/OpenTelemetryHttpPolicy.java b/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/OpenTelemetryHttpPolicy.java index fc5399914ce0..65bcf0b74b2c 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/OpenTelemetryHttpPolicy.java +++ b/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/OpenTelemetryHttpPolicy.java @@ -20,7 +20,6 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; import reactor.core.CoreSubscriber; @@ -227,7 +226,6 @@ private static io.opentelemetry.context.Context getTraceContextOrCurrent(HttpPip * * OpenTelemetry reactor auto-instrumentation will take care of the cold path. */ - @SuppressWarnings("try") static final class ScalarPropagatingMono extends Mono { public static final Mono INSTANCE = new ScalarPropagatingMono(); @@ -240,8 +238,12 @@ private ScalarPropagatingMono() { public void subscribe(CoreSubscriber actual) { Context traceContext = actual.currentContext().getOrDefault(REACTOR_PARENT_TRACE_CONTEXT_KEY, null); if (traceContext != null) { - try (Scope s = OpenTelemetrySpanSuppressionHelper.registerClientSpan(traceContext).makeCurrent()) { - actual.onSubscribe(Operators.scalarSubscription(actual, value)); + Object agentContext = OpenTelemetrySpanSuppressionHelper.registerClientSpan(traceContext); + AutoCloseable closeable = OpenTelemetrySpanSuppressionHelper.makeCurrent(agentContext, traceContext); + actual.onSubscribe(Operators.scalarSubscription(actual, value)); + try { + closeable.close(); + } catch (Throwable ignored) { } } else { actual.onSubscribe(Operators.scalarSubscription(actual, value)); diff --git a/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/implementation/OpenTelemetrySpanSuppressionHelper.java b/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/implementation/OpenTelemetrySpanSuppressionHelper.java index 888b0822744f..170004e76472 100644 --- a/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/implementation/OpenTelemetrySpanSuppressionHelper.java +++ b/sdk/core/azure-core-tracing-opentelemetry/src/main/java/com/azure/core/tracing/opentelemetry/implementation/OpenTelemetrySpanSuppressionHelper.java @@ -19,19 +19,31 @@ public class OpenTelemetrySpanSuppressionHelper { private static boolean agentDiscovered; private static final ClientLogger LOGGER = new ClientLogger(OpenTelemetrySpanSuppressionHelper.class); - + private static Method getAgentContextMethod; private static Method setSpanKeyMethod; private static Object clientSpanKey; + private static Method getAgentSpanMethod; + private static Method agentContextMakeCurrentMethod; static { agentDiscovered = true; try { - Class spanKeyClass = Class.forName("io.opentelemetry.instrumentation.api.instrumenter.SpanKey"); + Class agentContextStorageClass = Class.forName("io.opentelemetry.javaagent.instrumentation.opentelemetryapi.context.AgentContextStorage"); + Class agentContextClass = Class.forName("io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Context"); + Class spanKeyClass = Class.forName("io.opentelemetry.javaagent.shaded.instrumentation.api.instrumenter.SpanKey"); + Class bridgingClass = Class.forName("io.opentelemetry.javaagent.instrumentation.opentelemetryapi.trace.Bridging"); + Class agentSpanClass = Class.forName("io.opentelemetry.javaagent.shaded.io.opentelemetry.api.trace.Span"); + getAgentContextMethod = agentContextStorageClass.getDeclaredMethod("getAgentContext", io.opentelemetry.context.Context.class); + getAgentSpanMethod = bridgingClass.getDeclaredMethod("toAgentOrNull", io.opentelemetry.api.trace.Span.class); clientSpanKey = spanKeyClass.getDeclaredField("ALL_CLIENTS").get(null); - setSpanKeyMethod = spanKeyClass.getDeclaredMethod("storeInContext", Context.class, Span.class); + setSpanKeyMethod = spanKeyClass.getDeclaredMethod("storeInContext", agentContextClass, agentSpanClass); + agentContextMakeCurrentMethod = agentContextClass.getMethod("makeCurrent"); - if (setSpanKeyMethod.getReturnType() != Context.class) { + if (getAgentContextMethod.getReturnType() != agentContextClass + || getAgentSpanMethod.getReturnType() != agentSpanClass + || setSpanKeyMethod.getReturnType() != agentContextClass + || !AutoCloseable.class.isAssignableFrom(agentContextMakeCurrentMethod.getReturnType())) { agentDiscovered = false; } @@ -46,20 +58,42 @@ public class OpenTelemetrySpanSuppressionHelper { * @param traceContext OpenTelemetry context with client span * @return Agent context or null when agent is not running or doesn't behave as expected. */ - public static Context registerClientSpan(Context traceContext) { + public static Object registerClientSpan(Context traceContext) { Objects.requireNonNull(traceContext, "'traceContext' cannot be null"); if (agentDiscovered) { try { - return (Context) setSpanKeyMethod.invoke( + return setSpanKeyMethod.invoke( clientSpanKey, - traceContext, - Span.fromContext(traceContext)); + getAgentContextMethod.invoke(null, traceContext), + getAgentSpanMethod.invoke(null, Span.fromContext(traceContext))); } catch (Throwable t) { // should not happen, If it does, we'll log it once. LOGGER.warning("Failed to register client span on OpenTelemetry agent"); agentDiscovered = false; } } - return traceContext; + return null; + } + + /** + * Makes passed agent context current. Falls back to passed trace context when agent is not running + * or doesn't behave as expected. + * @param agentContext agent context instance obtained from {@link OpenTelemetrySpanSuppressionHelper#registerClientSpan} + * @param traceContext Regular OpenTelemetry context to fallback to. + * @return scope to be closed in the same thread as it was started. + */ + public static AutoCloseable makeCurrent(Object agentContext, Context traceContext) { + Objects.requireNonNull(traceContext, "'traceContext' cannot be null"); + if (agentDiscovered && agentContext != null) { + try { + return (AutoCloseable) agentContextMakeCurrentMethod.invoke(agentContext); + } catch (Throwable t) { + // should not happen, If it does, we'll log it once. + LOGGER.warning("Failed to make OpenTelemetry agent context current"); + agentDiscovered = false; + } + } + + return traceContext.makeCurrent(); } } From fcd55fe55cff22ea67e16a577c74e190d3b01f23 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Fri, 3 Dec 2021 16:35:44 -0800 Subject: [PATCH 08/17] more fixes --- .../amqp/implementation/AmqpChannelProcessor.java | 14 +++++--------- .../amqp/implementation/ReactorConnection.java | 5 ++++- .../implementation/AmqpChannelProcessorTest.java | 4 +++- .../amqp/implementation/ManagementChannelTest.java | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index ee8340730601..a39297f5b99d 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -18,8 +18,6 @@ import reactor.core.publisher.Operators; import java.time.Duration; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.RejectedExecutionException; @@ -28,9 +26,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Function; -import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; -import static com.azure.core.amqp.implementation.ClientConstants.FULLY_QUALIFIED_NAMESPACE_KEY; - public class AmqpChannelProcessor extends Mono implements Processor, CoreSubscriber, Disposable { @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater UPSTREAM = @@ -56,15 +51,16 @@ public class AmqpChannelProcessor extends Mono implements Processor, private volatile Disposable retrySubscription; public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, - Function> endpointStatesFunction, AmqpRetryPolicy retryPolicy, String loggerName) { + Function> endpointStatesFunction, AmqpRetryPolicy retryPolicy, ClientLogger logger) { this.endpointStatesFunction = Objects.requireNonNull(endpointStatesFunction, "'endpointStates' cannot be null."); this.retryPolicy = Objects.requireNonNull(retryPolicy, "'retryPolicy' cannot be null."); - Map loggingContext = new HashMap<>(); + /*Map loggingContext = new HashMap<>(); loggingContext.put(FULLY_QUALIFIED_NAMESPACE_KEY, Objects.requireNonNull(fullyQualifiedNamespace, "'fullyQualifiedNamespace' cannot be null.")); - loggingContext.put(ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null.")); - this.logger = new ClientLogger(loggerName, loggingContext); + loggingContext.put(ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null."));*/ + this.logger = Objects.requireNonNull(logger, "'retryPolicy' cannot be null."); + this.errorContext = new AmqpErrorContext(fullyQualifiedNamespace); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java index bf0ee0f18d03..9166fa41b59c 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java @@ -406,9 +406,12 @@ protected AmqpChannelProcessor createRequestResponseChan }) .repeat(); + Map loggingContext = createContextWithConnectionId(connectionId); + loggingContext.put(ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null.")); + return createChannel .subscribeWith(new AmqpChannelProcessor<>(connectionId, entityPath, - channel -> channel.getEndpointStates(), retryPolicy, RequestResponseChannel.class.getName())); + channel -> channel.getEndpointStates(), retryPolicy, new ClientLogger(RequestResponseChannel.class, loggingContext))); } @Override diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java index 53c3b1b4ed69..bc151714c88b 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java @@ -8,6 +8,8 @@ import com.azure.core.amqp.exception.AmqpErrorCondition; import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; +import com.azure.core.amqp.implementation.handler.LinkHandlerTest; +import com.azure.core.util.logging.ClientLogger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -57,7 +59,7 @@ void setup() { mocksCloseable = MockitoAnnotations.openMocks(this); channelProcessor = new AmqpChannelProcessor<>("connection-test", "test-path", - TestObject::getStates, retryPolicy, "TestLogger"); + TestObject::getStates, retryPolicy, new ClientLogger(LinkHandlerTest.class)); } @AfterEach diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java index 3626f5558b7f..314a33eef0ad 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java @@ -95,7 +95,7 @@ public void setup(TestInfo testInfo) { final AmqpChannelProcessor requestResponseMono = Mono.defer(() -> Mono.just(requestResponseChannel)).subscribeWith(new AmqpChannelProcessor<>( - "foo", "bar", RequestResponseChannel::getEndpointStates, retryPolicy, "TestLogger")); + "foo", "bar", RequestResponseChannel::getEndpointStates, retryPolicy, new ClientLogger(ManagementChannelTest.class))); when(tokenManager.authorize()).thenReturn(Mono.just(1000L)); when(tokenManager.getAuthorizationResults()).thenReturn(tokenProviderResults.flux()); From 8602b4ce132f7d198bd1118401d761038b667cf2 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Mon, 13 Dec 2021 21:36:01 -0800 Subject: [PATCH 09/17] review --- .../ActiveClientTokenManager.java | 7 +++-- .../implementation/AmqpChannelProcessor.java | 27 ++++++++++++------- .../amqp/implementation/AmqpLoggingUtils.java | 8 ++++-- .../amqp/implementation/ClientConstants.java | 1 + .../implementation/ReactorConnection.java | 10 ++++--- .../amqp/implementation/ReactorExecutor.java | 1 - .../amqp/implementation/ReactorSender.java | 9 +++++-- .../RequestResponseChannel.java | 3 +-- .../ServiceBusReactorReceiverTest.java | 2 ++ 9 files changed, 46 insertions(+), 22 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java index 2a91370a2488..c3e97a14ea3f 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java @@ -20,6 +20,7 @@ import java.util.concurrent.atomic.AtomicReference; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.ClientConstants.INTERVAL_KEY; /** * Manages the re-authorization of the client to the token audience against the CBS node. @@ -143,7 +144,8 @@ private Disposable scheduleRefreshTokenTask(Duration initialRefresh) { logger.atError() .addKeyValue("scopes", scopes) - .log("Error is transient. Rescheduling authorization task at interval {} ms", lastRefresh.toMillis(), amqpException); + .addKeyValue(INTERVAL_KEY, interval) + .log("Error is transient. Rescheduling authorization task.", amqpException); durationSource.emitNext(lastRefresh, (signalType, emitResult) -> { addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) @@ -156,7 +158,8 @@ private Disposable scheduleRefreshTokenTask(Duration initialRefresh) { .subscribe(interval -> { logger.atVerbose() .addKeyValue("scopes", scopes) - .log("Authorization successful. Refreshing token in {} ms.", interval); + .addKeyValue(INTERVAL_KEY, interval) + .log("Authorization successful. Refreshing token."); authorizationResults.emitNext(AmqpResponseCode.ACCEPTED, (signalType, emitResult) -> { addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index a39297f5b99d..c009b3651bd0 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -26,12 +26,16 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Function; +import static com.azure.core.amqp.implementation.ClientConstants.INTERVAL_KEY; + public class AmqpChannelProcessor extends Mono implements Processor, CoreSubscriber, Disposable { @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater UPSTREAM = AtomicReferenceFieldUpdater.newUpdater(AmqpChannelProcessor.class, Subscription.class, "upstream"); + private static final String RETRY_NUMBER_KEY = "retry"; + private final ClientLogger logger; private final AtomicBoolean isDisposed = new AtomicBoolean(); private final AtomicBoolean isRequested = new AtomicBoolean(); @@ -55,11 +59,7 @@ public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, this.endpointStatesFunction = Objects.requireNonNull(endpointStatesFunction, "'endpointStates' cannot be null."); this.retryPolicy = Objects.requireNonNull(retryPolicy, "'retryPolicy' cannot be null."); - - /*Map loggingContext = new HashMap<>(); - loggingContext.put(FULLY_QUALIFIED_NAMESPACE_KEY, Objects.requireNonNull(fullyQualifiedNamespace, "'fullyQualifiedNamespace' cannot be null.")); - loggingContext.put(ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null."));*/ - this.logger = Objects.requireNonNull(logger, "'retryPolicy' cannot be null."); + this.logger = Objects.requireNonNull(logger, "'logger' cannot be null."); this.errorContext = new AmqpErrorContext(fullyQualifiedNamespace); } @@ -178,20 +178,29 @@ public void onError(Throwable throwable) { return; } - logger.info("Retry #{}. Transient error occurred. Retrying after {} ms.", attempts, retryInterval.toMillis(), throwable); + logger.atInfo() + .addKeyValue(RETRY_NUMBER_KEY, attempts) + .addKeyValue(INTERVAL_KEY, retryInterval.toMillis()) + .log("Transient error occurred. Retrying.", throwable); retrySubscription = Mono.delay(retryInterval).subscribe(i -> { if (isDisposed()) { - logger.info("Retry #{}. Not requesting from upstream. Processor is disposed.", attempts); + logger.atInfo() + .addKeyValue(RETRY_NUMBER_KEY, attempts) + .log("Not requesting from upstream. Processor is disposed."); } else { - logger.info("Retry #{}. Requesting from upstream.", attempts); + logger.atInfo() + .addKeyValue(RETRY_NUMBER_KEY, attempts) + .log("Requesting from upstream."); requestUpstream(); isRetryPending.set(false); } }); } else { - logger.warning("Retry #{}. Retry attempts exhausted or exception was not retriable.", attempts, throwable); + logger.atWarning() + .addKeyValue(RETRY_NUMBER_KEY, attempts) + .log("Retry attempts exhausted or exception was not retriable.", throwable); lastError = throwable; isDisposed.set(true); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java index 44bebb6072fc..beb185b18ad9 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java @@ -23,14 +23,18 @@ /** * Utils for contextual logging. */ -public class AmqpLoggingUtils { +public final class AmqpLoggingUtils { + + private AmqpLoggingUtils() { + } /** * Creates logging context with connectionId. */ public static Map createContextWithConnectionId(String connectionId) { Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); - Map globalLoggingContext = new HashMap<>(); + // caller should be able to add more context, please keep the map mutable. + Map globalLoggingContext = new HashMap<>(1); globalLoggingContext.put(CONNECTION_ID_KEY, connectionId); return globalLoggingContext; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java index 6dad9c4b002f..d9024b734a78 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ClientConstants.java @@ -23,6 +23,7 @@ public final class ClientConstants { public static final String EMIT_RESULT_KEY = "emitResult"; public static final String SIGNAL_TYPE_KEY = "signalType"; public static final String HOSTNAME_KEY = "hostName"; + public static final String INTERVAL_KEY = "interval_ms"; /** * The default maximum allowable size, in bytes, for a batch to be sent. diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java index 9166fa41b59c..c5902a03b22c 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java @@ -139,8 +139,8 @@ public ReactorConnection(String connectionId, ConnectionOptions connectionOption if (isDisposed.getAndSet(true)) { logger.verbose("Connection was already disposed: Error occurred while connection was starting.", error); } else { - closeAsync(new AmqpShutdownSignal(false, false, String.format( - "Error occurred while connection was starting. Error: %s", error))).subscribe(); + closeAsync(new AmqpShutdownSignal(false, false, + "Error occurred while connection was starting. Error: " + error)).subscribe(); } }); @@ -393,6 +393,8 @@ protected Mono getReactorConnection() { protected AmqpChannelProcessor createRequestResponseChannel(String sessionName, String linkName, String entityPath) { + Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); + final Flux createChannel = createSession(sessionName) .cast(ReactorSession.class) .map(reactorSession -> new RequestResponseChannel(this, getId(), getFullyQualifiedNamespace(), linkName, @@ -407,7 +409,7 @@ protected AmqpChannelProcessor createRequestResponseChan .repeat(); Map loggingContext = createContextWithConnectionId(connectionId); - loggingContext.put(ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null.")); + loggingContext.put(ENTITY_PATH_KEY, entityPath); return createChannel .subscribeWith(new AmqpChannelProcessor<>(connectionId, entityPath, @@ -453,7 +455,7 @@ Mono closeAsync(AmqpShutdownSignal shutdownSignal) { try { dispatcher.invoke(() -> closeConnectionWork()); } catch (IOException e) { - logger.warning("IOException while scheduling closeConnection work. Manually disposing", e); + logger.warning("IOException while scheduling closeConnection work. Manually disposing.", e); closeConnectionWork(); } catch (RejectedExecutionException e) { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java index 98e168be3724..e5ffa0b3664f 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorExecutor.java @@ -49,7 +49,6 @@ class ReactorExecutor implements AsyncCloseable { this.timeout = Objects.requireNonNull(timeout, "'timeout' cannot be null."); this.exceptionHandler = Objects.requireNonNull(exceptionHandler, "'exceptionHandler' cannot be null."); this.hostname = Objects.requireNonNull(hostname, "'hostname' cannot be null."); - Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); this.logger = new ClientLogger(ReactorExecutor.class, createContextWithConnectionId(connectionId)); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java index 90a16e855a95..a4baaeb1cfab 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSender.java @@ -72,6 +72,7 @@ */ class ReactorSender implements AmqpSendLink, AsyncCloseable, AutoCloseable { private static final String DELIVERY_TAG_KEY = "deliveryTag"; + private static final String PENDING_SENDS_SIZE_KEY = "pending_sends_size"; private final String entityPath; private final Sender sender; private final SendLinkHandler handler; @@ -677,7 +678,9 @@ private void handleError(Throwable error) { if (isDisposed.getAndSet(true)) { logger.verbose("This was already disposed. Dropping error."); } else { - logger.verbose("Disposing of '{}' pending sends with error.", pendingSendsMap.size()); + logger.atVerbose() + .addKeyValue(PENDING_SENDS_SIZE_KEY, () -> String.valueOf(pendingSendsMap.size())) + .log("Disposing pending sends with error."); } pendingSendsMap.forEach((key, value) -> value.error(error)); @@ -697,7 +700,9 @@ private void handleClose() { if (isDisposed.getAndSet(true)) { logger.verbose("This was already disposed."); } else { - logger.verbose("Disposing of '{}' pending sends.", pendingSendsMap.size()); + logger.atVerbose() + .addKeyValue(PENDING_SENDS_SIZE_KEY, () -> String.valueOf(pendingSendsMap.size())) + .log("Disposing pending sends."); } pendingSendsMap.forEach((key, value) -> value.error(new AmqpException(true, message, context))); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java index 7743937df527..cc9f8209823e 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/RequestResponseChannel.java @@ -237,8 +237,7 @@ public Mono closeAsync() { .timeout(retryOptions.getTryTimeout()) .onErrorResume(TimeoutException.class, error -> { return Mono.fromRunnable(() -> { - logger.atInfo() - .log("Timed out waiting for RequestResponseChannel to complete closing. Manually closing."); + logger.info("Timed out waiting for RequestResponseChannel to complete closing. Manually closing."); onTerminalState("SendLinkHandler"); onTerminalState("ReceiveLinkHandler"); diff --git a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusReactorReceiverTest.java b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusReactorReceiverTest.java index 7ed02bbabdb4..75331257543f 100644 --- a/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusReactorReceiverTest.java +++ b/sdk/servicebus/azure-messaging-servicebus/src/test/java/com/azure/messaging/servicebus/implementation/ServiceBusReactorReceiverTest.java @@ -51,6 +51,7 @@ class ServiceBusReactorReceiverTest { private static final String ENTITY_PATH = "queue-name"; private static final String LINK_NAME = "a-link-name"; + private static final String CONNECTION_ID = "a-connection-id"; private final ClientLogger logger = new ClientLogger(ServiceBusReactorReceiver.class); private final EmitterProcessor endpointStates = EmitterProcessor.create(); @@ -110,6 +111,7 @@ void setup(TestInfo testInfo) throws IOException { when(receiveLinkHandler.getEndpointStates()).thenReturn(endpointStates); when(tokenManager.getAuthorizationResults()).thenReturn(Flux.create(sink -> sink.next(AmqpResponseCode.OK))); + when(receiveLinkHandler.getConnectionId()).thenReturn(CONNECTION_ID); when(connection.getShutdownSignals()).thenReturn(Flux.never()); From 06ed3d7e57f57df9f5012d399310f6764bb93946 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Mon, 13 Dec 2021 21:52:14 -0800 Subject: [PATCH 10/17] up --- .../com/azure/core/amqp/implementation/AmqpLoggingUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java index beb185b18ad9..871504866ec7 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpLoggingUtils.java @@ -85,7 +85,7 @@ public static LoggingEventBuilder addShutdownSignal(LoggingEventBuilder logBuild return logBuilder .addKeyValue("isTransient", shutdownSignal.isTransient()) .addKeyValue("isInitiatedByClient", shutdownSignal.isInitiatedByClient()) - // will call toString() if logging is enabled + // will call toString() when performing logging (if enabled) .addKeyValue("shutdownMessage", shutdownSignal); } } From c16b8a6b188ae03f450e96a1c717089d267aaaca Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Tue, 14 Dec 2021 10:47:42 -0800 Subject: [PATCH 11/17] AmqpChannelProcessor add constructor overload that creates logger and deprecate old one --- .../implementation/AmqpChannelProcessor.java | 21 +++++++++++++++++++ .../implementation/ReactorConnection.java | 4 ++-- .../AmqpChannelProcessorTest.java | 6 ++---- .../implementation/ManagementChannelTest.java | 2 +- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index c009b3651bd0..65dcff548b11 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -18,6 +18,7 @@ import reactor.core.publisher.Operators; import java.time.Duration; +import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.RejectedExecutionException; @@ -26,6 +27,8 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Function; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; +import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.INTERVAL_KEY; public class AmqpChannelProcessor extends Mono implements Processor, CoreSubscriber, Disposable { @@ -54,6 +57,10 @@ public class AmqpChannelProcessor extends Mono implements Processor, private volatile Disposable connectionSubscription; private volatile Disposable retrySubscription; + /** + * @deprecated Use constructor overload that does not take {@link ClientLogger} + */ + @Deprecated public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, Function> endpointStatesFunction, AmqpRetryPolicy retryPolicy, ClientLogger logger) { this.endpointStatesFunction = Objects.requireNonNull(endpointStatesFunction, @@ -64,6 +71,20 @@ public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, this.errorContext = new AmqpErrorContext(fullyQualifiedNamespace); } + + public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, String connectionId, + Function> endpointStatesFunction, AmqpRetryPolicy retryPolicy) { + this.endpointStatesFunction = Objects.requireNonNull(endpointStatesFunction, + "'endpointStates' cannot be null."); + this.retryPolicy = Objects.requireNonNull(retryPolicy, "'retryPolicy' cannot be null."); + + Map loggingContext = createContextWithConnectionId(connectionId); + loggingContext.put(ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null.")); + this.logger = new ClientLogger(AmqpChannelProcessor.class, loggingContext); + + this.errorContext = new AmqpErrorContext(fullyQualifiedNamespace); + } + @Override public void onSubscribe(Subscription subscription) { if (Operators.setOnce(UPSTREAM, this, subscription)) { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java index c5902a03b22c..25bf8f5e9309 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java @@ -412,8 +412,8 @@ protected AmqpChannelProcessor createRequestResponseChan loggingContext.put(ENTITY_PATH_KEY, entityPath); return createChannel - .subscribeWith(new AmqpChannelProcessor<>(connectionId, entityPath, - channel -> channel.getEndpointStates(), retryPolicy, new ClientLogger(RequestResponseChannel.class, loggingContext))); + .subscribeWith(new AmqpChannelProcessor<>(getFullyQualifiedNamespace(), entityPath, connectionId, + channel -> channel.getEndpointStates(), retryPolicy)); } @Override diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java index bc151714c88b..bc42248afd7b 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java @@ -8,8 +8,6 @@ import com.azure.core.amqp.exception.AmqpErrorCondition; import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; -import com.azure.core.amqp.implementation.handler.LinkHandlerTest; -import com.azure.core.util.logging.ClientLogger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -58,8 +56,8 @@ class AmqpChannelProcessorTest { void setup() { mocksCloseable = MockitoAnnotations.openMocks(this); - channelProcessor = new AmqpChannelProcessor<>("connection-test", "test-path", - TestObject::getStates, retryPolicy, new ClientLogger(LinkHandlerTest.class)); + channelProcessor = new AmqpChannelProcessor<>("namespace-test", "test-path", "connection-test", + TestObject::getStates, retryPolicy); } @AfterEach diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java index 314a33eef0ad..7f873f719ef6 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java @@ -95,7 +95,7 @@ public void setup(TestInfo testInfo) { final AmqpChannelProcessor requestResponseMono = Mono.defer(() -> Mono.just(requestResponseChannel)).subscribeWith(new AmqpChannelProcessor<>( - "foo", "bar", RequestResponseChannel::getEndpointStates, retryPolicy, new ClientLogger(ManagementChannelTest.class))); + "foo", "bar", "baz", RequestResponseChannel::getEndpointStates, retryPolicy)); when(tokenManager.authorize()).thenReturn(Mono.just(1000L)); when(tokenManager.getAuthorizationResults()).thenReturn(tokenProviderResults.flux()); From 0efd69e2f2efeb83cf9bfbf2da671eb0c4717d28 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Tue, 14 Dec 2021 11:27:42 -0800 Subject: [PATCH 12/17] remove clientlogger from Handler ctor --- .../handler/ConnectionHandler.java | 5 +---- .../amqp/implementation/handler/Handler.java | 8 ++++---- .../amqp/implementation/handler/LinkHandler.java | 8 +++----- .../handler/ReceiveLinkHandler.java | 4 +--- .../implementation/handler/SendLinkHandler.java | 4 +--- .../implementation/handler/SessionHandler.java | 4 +--- .../handler/WebSocketsConnectionHandler.java | 5 +---- .../WebSocketsProxyConnectionHandler.java | 4 ---- .../amqp/implementation/handler/HandlerTest.java | 13 +++++-------- .../implementation/handler/LinkHandlerTest.java | 16 +++++----------- 10 files changed, 22 insertions(+), 49 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java index c3d96ed58dad..f42ed44e6a4c 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ConnectionHandler.java @@ -10,7 +10,6 @@ import com.azure.core.util.ClientOptions; import com.azure.core.util.CoreUtils; import com.azure.core.util.UserAgentUtil; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.Proton; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; @@ -30,7 +29,6 @@ import java.util.Objects; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.FULLY_QUALIFIED_NAMESPACE_KEY; import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; @@ -62,8 +60,7 @@ public class ConnectionHandler extends Handler { public ConnectionHandler(final String connectionId, final ConnectionOptions connectionOptions, SslPeerDetails peerDetails) { super(connectionId, - Objects.requireNonNull(connectionOptions, "'connectionOptions' cannot be null.").getHostname(), - new ClientLogger(ConnectionHandler.class, createContextWithConnectionId(connectionId))); + Objects.requireNonNull(connectionOptions, "'connectionOptions' cannot be null.").getHostname()); add(new Handshaker()); this.connectionOptions = connectionOptions; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java index 95d389965fbb..2c8504721d24 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/Handler.java @@ -14,6 +14,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; /** * Base class for all proton-j handlers. @@ -34,14 +35,13 @@ public abstract class Handler extends BaseHandler implements Closeable { * @param hostname Hostname of the connection. This could be the DNS hostname or the IP address of the * connection. Usually of the form {@literal ".service.windows.net"} but can change if the * messages are brokered through an intermediary. - * @param logger Logger to use for messages. * - * @throws NullPointerException if {@code connectionId}, {@code hostname}, or {@code logger} is null. + * @throws NullPointerException if {@code connectionId} or {@code hostname} is null. */ - Handler(final String connectionId, final String hostname, final ClientLogger logger) { + Handler(final String connectionId, final String hostname) { this.connectionId = Objects.requireNonNull(connectionId, "'connectionId' cannot be null."); this.hostname = Objects.requireNonNull(hostname, "'hostname' cannot be null."); - this.logger = Objects.requireNonNull(logger, "'logger' cannot be null."); + this.logger = new ClientLogger(getClass(), createContextWithConnectionId(connectionId)); } /** diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java index df5842bad7d6..b961f5c9bdc4 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/LinkHandler.java @@ -6,7 +6,6 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.LinkErrorContext; import com.azure.core.amqp.implementation.ExceptionUtil; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; import org.apache.qpid.proton.engine.Event; @@ -15,8 +14,8 @@ import java.util.Objects; import static com.azure.core.amqp.implementation.AmqpErrorCode.TRACKING_ID_PROPERTY; -import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; +import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; /** @@ -36,13 +35,12 @@ abstract class LinkHandler extends Handler { * connection. Usually of the form {@literal ".service.windows.net"} but can change if the * messages are brokered through an intermediary. * @param entityPath The address within the message broker for this link. - * @param logger Logger to use for messages. * * @throws NullPointerException if {@code connectionId}, {@code hostname}, {@code entityPath}, or {@code logger} is * null. */ - LinkHandler(String connectionId, String hostname, String entityPath, ClientLogger logger) { - super(connectionId, hostname, logger); + LinkHandler(String connectionId, String hostname, String entityPath) { + super(connectionId, hostname); this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java index e0af148f3bd4..a75b2ef2bdf2 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/ReceiveLinkHandler.java @@ -3,7 +3,6 @@ package com.azure.core.amqp.implementation.handler; -import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.messaging.Modified; @@ -26,7 +25,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; @@ -53,7 +51,7 @@ public class ReceiveLinkHandler extends LinkHandler { private final String entityPath; public ReceiveLinkHandler(String connectionId, String hostname, String linkName, String entityPath) { - super(connectionId, hostname, entityPath, new ClientLogger(ReceiveLinkHandler.class, createContextWithConnectionId(connectionId))); + super(connectionId, hostname, entityPath); this.linkName = Objects.requireNonNull(linkName, "'linkName' cannot be null."); this.entityPath = Objects.requireNonNull(entityPath, "'entityPath' cannot be null."); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java index 81b2a24c25cd..c24c1ed9d186 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SendLinkHandler.java @@ -3,7 +3,6 @@ package com.azure.core.amqp.implementation.handler; -import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.engine.BaseHandler; import org.apache.qpid.proton.engine.Delivery; @@ -21,7 +20,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.EMIT_RESULT_KEY; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; @@ -47,7 +45,7 @@ public class SendLinkHandler extends LinkHandler { private final Sinks.Many deliveryProcessor = Sinks.many().multicast().onBackpressureBuffer(); public SendLinkHandler(String connectionId, String hostname, String linkName, String entityPath) { - super(connectionId, hostname, entityPath, new ClientLogger(SendLinkHandler.class, createContextWithConnectionId(connectionId))); + super(connectionId, hostname, entityPath); this.linkName = Objects.requireNonNull(linkName, "'linkName' cannot be null."); this.entityPath = entityPath; } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java index 7cad0c3b2991..cb0dc1fc788c 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/SessionHandler.java @@ -8,7 +8,6 @@ import com.azure.core.amqp.exception.SessionErrorContext; import com.azure.core.amqp.implementation.ExceptionUtil; import com.azure.core.amqp.implementation.ReactorDispatcher; -import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.logging.LoggingEventBuilder; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; @@ -21,7 +20,6 @@ import java.util.concurrent.RejectedExecutionException; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.SESSION_NAME_KEY; public class SessionHandler extends Handler { @@ -31,7 +29,7 @@ public class SessionHandler extends Handler { public SessionHandler(String connectionId, String hostname, String sessionName, ReactorDispatcher reactorDispatcher, Duration openTimeout) { - super(connectionId, hostname, new ClientLogger(SessionHandler.class, createContextWithConnectionId(connectionId))); + super(connectionId, hostname); this.sessionName = sessionName; this.openTimeout = openTimeout; this.reactorDispatcher = reactorDispatcher; diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java index 9e4d59541a32..1620cbb75a4b 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsConnectionHandler.java @@ -4,12 +4,11 @@ package com.azure.core.amqp.implementation.handler; import com.azure.core.amqp.implementation.ConnectionOptions; -import com.azure.core.util.logging.ClientLogger; import com.microsoft.azure.proton.transport.ws.impl.WebSocketImpl; import org.apache.qpid.proton.engine.Event; import org.apache.qpid.proton.engine.SslPeerDetails; import org.apache.qpid.proton.engine.impl.TransportInternal; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; + import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; /** @@ -24,7 +23,6 @@ public class WebSocketsConnectionHandler extends ConnectionHandler { private static final String SOCKET_PATH = "/$servicebus/websocket"; private static final String PROTOCOL = "AMQPWSB10"; - private final ClientLogger logger; /** * Creates a handler that handles proton-j's connection events using web sockets. @@ -35,7 +33,6 @@ public class WebSocketsConnectionHandler extends ConnectionHandler { public WebSocketsConnectionHandler(String connectionId, ConnectionOptions connectionOptions, SslPeerDetails peerDetails) { super(connectionId, connectionOptions, peerDetails); - logger = new ClientLogger(WebSocketsConnectionHandler.class, createContextWithConnectionId(connectionId)); } /** diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java index 38ecbdd66f87..263ce20793f6 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/handler/WebSocketsProxyConnectionHandler.java @@ -8,7 +8,6 @@ import com.azure.core.amqp.implementation.AmqpErrorCode; import com.azure.core.amqp.implementation.ConnectionOptions; import com.azure.core.util.CoreUtils; -import com.azure.core.util.logging.ClientLogger; import com.microsoft.azure.proton.transport.proxy.ProxyHandler; import com.microsoft.azure.proton.transport.proxy.impl.ProxyHandlerImpl; import com.microsoft.azure.proton.transport.proxy.impl.ProxyImpl; @@ -30,7 +29,6 @@ import java.util.Objects; import java.util.stream.Collectors; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.ClientConstants.HOSTNAME_KEY; @@ -40,7 +38,6 @@ public class WebSocketsProxyConnectionHandler extends WebSocketsConnectionHandler { private static final String HTTPS_URI_FORMAT = "https://%s:%s"; - private final ClientLogger logger; private final InetSocketAddress connectionHostname; private final ProxyOptions proxyOptions; private final String fullyQualifiedNamespace; @@ -64,7 +61,6 @@ public WebSocketsProxyConnectionHandler(String connectionId, ConnectionOptions c this.proxyOptions = Objects.requireNonNull(proxyOptions, "'proxyConfiguration' cannot be null."); this.fullyQualifiedNamespace = connectionOptions.getFullyQualifiedNamespace(); this.amqpBrokerHostname = connectionOptions.getFullyQualifiedNamespace() + ":" + connectionOptions.getPort(); - this.logger = new ClientLogger(WebSocketsProxyConnectionHandler.class, createContextWithConnectionId(connectionId)); if (proxyOptions.isProxyAddressConfigured()) { this.connectionHostname = (InetSocketAddress) proxyOptions.getProxyAddress().address(); } else { diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java index 0ee6b6c9fb59..56a2e35115fc 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/HandlerTest.java @@ -5,7 +5,6 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.engine.EndpointState; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -13,7 +12,6 @@ import org.mockito.Mockito; import reactor.test.StepVerifier; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -44,9 +42,8 @@ public void constructor() { final String hostname = "hostname"; // Act - assertThrows(NullPointerException.class, () -> new TestHandler(null, hostname, new ClientLogger(TestHandler.class))); - assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, null, new ClientLogger(TestHandler.class))); - assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, hostname, null)); + assertThrows(NullPointerException.class, () -> new TestHandler(null, hostname)); + assertThrows(NullPointerException.class, () -> new TestHandler(connectionId, null)); } @Test @@ -158,11 +155,11 @@ private static class TestHandler extends Handler { static final String HOSTNAME = "test-hostname"; TestHandler() { - super(CONNECTION_ID, HOSTNAME, new ClientLogger(TestHandler.class, createContextWithConnectionId(CONNECTION_ID))); + super(CONNECTION_ID, HOSTNAME); } - TestHandler(String connectionId, String hostname, ClientLogger logger) { - super(connectionId, hostname, logger); + TestHandler(String connectionId, String hostname) { + super(connectionId, hostname); } } } diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java index 7649fe7bc1ab..9719cbcbb68c 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/handler/LinkHandlerTest.java @@ -7,7 +7,6 @@ import com.azure.core.amqp.exception.AmqpErrorContext; import com.azure.core.amqp.exception.AmqpException; import com.azure.core.amqp.exception.LinkErrorContext; -import com.azure.core.util.logging.ClientLogger; import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.EndpointState; @@ -62,8 +61,7 @@ public class LinkHandlerTest { private final AmqpErrorCondition linkStolen = LINK_STOLEN; private final Symbol symbol = Symbol.getSymbol(linkStolen.getErrorCondition()); private final String description = "test-description"; - private final ClientLogger logger = new ClientLogger(LinkHandlerTest.class); - private final LinkHandler handler = new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH, logger); + private final LinkHandler handler = new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH); private AutoCloseable mocksCloseable; @BeforeEach @@ -333,13 +331,9 @@ public void onLinkFinal() { public void constructor() { // Act assertThrows(NullPointerException.class, - () -> new MockLinkHandler(null, HOSTNAME, ENTITY_PATH, logger)); + () -> new MockLinkHandler(null, HOSTNAME, ENTITY_PATH)); assertThrows(NullPointerException.class, - () -> new MockLinkHandler(CONNECTION_ID, null, ENTITY_PATH, logger)); - assertThrows(NullPointerException.class, - () -> new MockLinkHandler(CONNECTION_ID, HOSTNAME, null, logger)); - assertThrows(NullPointerException.class, - () -> new MockLinkHandler(CONNECTION_ID, HOSTNAME, ENTITY_PATH, null)); + () -> new MockLinkHandler(CONNECTION_ID, null, ENTITY_PATH)); } /** @@ -409,8 +403,8 @@ public void errorContextNoReferenceId(Map properties) { } private static final class MockLinkHandler extends LinkHandler { - MockLinkHandler(String connectionId, String hostname, String entityPath, ClientLogger logger) { - super(connectionId, hostname, entityPath, logger); + MockLinkHandler(String connectionId, String hostname, String entityPath) { + super(connectionId, hostname, entityPath); } } } From 90fe335a369eddd99ca9f47a51f0172dee47538b Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Mon, 3 Jan 2022 16:25:53 -0800 Subject: [PATCH 13/17] Fix AmqpChannelProcessor to take loggingContext --- .../checkstyle/checks/ThrowFromClientLoggerCheck.java | 7 ++++--- .../amqp/implementation/AmqpChannelProcessor.java | 11 ++--------- .../core/amqp/implementation/ReactorConnection.java | 3 +-- .../amqp/implementation/AmqpChannelProcessorTest.java | 5 ++--- .../amqp/implementation/ManagementChannelTest.java | 2 +- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/eng/code-quality-reports/src/main/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck.java b/eng/code-quality-reports/src/main/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck.java index 08caf8094ddc..1b98639f6418 100644 --- a/eng/code-quality-reports/src/main/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck.java +++ b/eng/code-quality-reports/src/main/java/com/azure/tools/checkstyle/checks/ThrowFromClientLoggerCheck.java @@ -125,9 +125,10 @@ public void visitToken(DetailAST token) { } } - /** - * - **/ + /* + * Checks if the expression includes call to log(), which verifies logging builder call + * e.g. logger.atError().log(ex) + */ private static boolean findLogMethodIdentifier(DetailAST root) { for (DetailAST ast = root.getFirstChild(); ast != null; ast = ast.getNextSibling()) { if (ast.getType() == TokenTypes.METHOD_CALL) { diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index 65dcff548b11..3a61e9f458ee 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -27,8 +27,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Function; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; -import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.INTERVAL_KEY; public class AmqpChannelProcessor extends Mono implements Processor, CoreSubscriber, Disposable { @@ -71,16 +69,11 @@ public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, this.errorContext = new AmqpErrorContext(fullyQualifiedNamespace); } - - public AmqpChannelProcessor(String fullyQualifiedNamespace, String entityPath, String connectionId, - Function> endpointStatesFunction, AmqpRetryPolicy retryPolicy) { + public AmqpChannelProcessor(String fullyQualifiedNamespace, Function> endpointStatesFunction, AmqpRetryPolicy retryPolicy, Map loggingContext) { this.endpointStatesFunction = Objects.requireNonNull(endpointStatesFunction, "'endpointStates' cannot be null."); this.retryPolicy = Objects.requireNonNull(retryPolicy, "'retryPolicy' cannot be null."); - - Map loggingContext = createContextWithConnectionId(connectionId); - loggingContext.put(ENTITY_PATH_KEY, Objects.requireNonNull(entityPath, "'entityPath' cannot be null.")); - this.logger = new ClientLogger(AmqpChannelProcessor.class, loggingContext); + this.logger = new ClientLogger(AmqpChannelProcessor.class, Objects.requireNonNull(loggingContext, "'loggingContext' cannot be null.")); this.errorContext = new AmqpErrorContext(fullyQualifiedNamespace); } diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java index 25bf8f5e9309..324279cb91bf 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java @@ -412,8 +412,7 @@ protected AmqpChannelProcessor createRequestResponseChan loggingContext.put(ENTITY_PATH_KEY, entityPath); return createChannel - .subscribeWith(new AmqpChannelProcessor<>(getFullyQualifiedNamespace(), entityPath, connectionId, - channel -> channel.getEndpointStates(), retryPolicy)); + .subscribeWith(new AmqpChannelProcessor<>(getFullyQualifiedNamespace(), channel -> channel.getEndpointStates(), retryPolicy, loggingContext)); } @Override diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java index bc42248afd7b..03f2682836e1 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/AmqpChannelProcessorTest.java @@ -25,6 +25,7 @@ import reactor.test.publisher.TestPublisher; import java.time.Duration; +import java.util.HashMap; import java.util.Objects; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; @@ -55,9 +56,7 @@ class AmqpChannelProcessorTest { @BeforeEach void setup() { mocksCloseable = MockitoAnnotations.openMocks(this); - - channelProcessor = new AmqpChannelProcessor<>("namespace-test", "test-path", "connection-test", - TestObject::getStates, retryPolicy); + channelProcessor = new AmqpChannelProcessor<>("namespace-test", TestObject::getStates, retryPolicy, new HashMap<>()); } @AfterEach diff --git a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java index 7f873f719ef6..8b492e059393 100644 --- a/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java +++ b/sdk/core/azure-core-amqp/src/test/java/com/azure/core/amqp/implementation/ManagementChannelTest.java @@ -95,7 +95,7 @@ public void setup(TestInfo testInfo) { final AmqpChannelProcessor requestResponseMono = Mono.defer(() -> Mono.just(requestResponseChannel)).subscribeWith(new AmqpChannelProcessor<>( - "foo", "bar", "baz", RequestResponseChannel::getEndpointStates, retryPolicy)); + "foo", RequestResponseChannel::getEndpointStates, retryPolicy, new HashMap<>())); when(tokenManager.authorize()).thenReturn(Mono.just(1000L)); when(tokenManager.getAuthorizationResults()).thenReturn(tokenProviderResults.flux()); From b418cb0dfcac9091d27421f156adc4567365c7e5 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Mon, 3 Jan 2022 19:45:22 -0800 Subject: [PATCH 14/17] up --- .../com/azure/core/amqp/implementation/AmqpChannelProcessor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index 3a61e9f458ee..704d10c2ee06 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -74,7 +74,6 @@ public AmqpChannelProcessor(String fullyQualifiedNamespace, Function Date: Mon, 10 Jan 2022 14:00:15 -0800 Subject: [PATCH 15/17] review comments --- .../ActiveClientTokenManager.java | 2 +- .../implementation/AmqpChannelProcessor.java | 2 +- .../azure/core/util/ConfigurationTests.java | 166 ------------------ 3 files changed, 2 insertions(+), 168 deletions(-) delete mode 100644 sdk/core/azure-core/src/test/java/com/azure/core/util/ConfigurationTests.java diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java index c3e97a14ea3f..10bbf7cac11c 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ActiveClientTokenManager.java @@ -122,7 +122,7 @@ public void close() { private Disposable scheduleRefreshTokenTask(Duration initialRefresh) { // EmitterProcessor can queue up an initial refresh interval before any subscribers are received. durationSource.emitNext(initialRefresh, (signalType, emitResult) -> { - addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult).log(" Could not emit initial refresh interval."); + addSignalTypeAndResult(logger.atVerbose(), signalType, emitResult).log("Could not emit initial refresh interval."); return false; }); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java index 704d10c2ee06..7c6e464548e5 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/AmqpChannelProcessor.java @@ -73,7 +73,7 @@ public AmqpChannelProcessor(String fullyQualifiedNamespace, Function getOrDefaultSupplier() { - return Stream.of( - Arguments.of(String.valueOf((byte) 42), (byte) 12, (byte) 42), - Arguments.of(String.valueOf((short) 42), (short) 12, (short) 42), - Arguments.of(String.valueOf(42), 12, 42), - Arguments.of(String.valueOf(42L), 12L, 42L), - Arguments.of(String.valueOf(42F), 12F, 42F), - Arguments.of(String.valueOf(42D), 12D, 42D), - Arguments.of(String.valueOf(true), false, true), - Arguments.of("42", "12", "42") - ); - } - - @Test - public void getOrDefaultReturnsDefault() { - assertEquals("42", new Configuration().get("empty", "42")); - } -} From b16914193c051b2f483acb7071d0a39bf815168e Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Mon, 10 Jan 2022 14:01:09 -0800 Subject: [PATCH 16/17] oops --- .../azure/core/util/ConfigurationTests.java | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 sdk/core/azure-core/src/test/java/com/azure/core/util/ConfigurationTests.java diff --git a/sdk/core/azure-core/src/test/java/com/azure/core/util/ConfigurationTests.java b/sdk/core/azure-core/src/test/java/com/azure/core/util/ConfigurationTests.java new file mode 100644 index 000000000000..bff1c787b6ed --- /dev/null +++ b/sdk/core/azure-core/src/test/java/com/azure/core/util/ConfigurationTests.java @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.core.util; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static com.azure.core.util.Configuration.PROPERTY_AZURE_TRACING_DISABLED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +/** + * Tests the configuration API. + */ +public class ConfigurationTests { + private static final String MY_CONFIGURATION = "myConfigurationABC123"; + private static final String EXPECTED_VALUE = "aConfigurationValueAbc123"; + private static final String UNEXPECTED_VALUE = "notMyConfigurationValueDef456"; + private static final String DEFAULT_VALUE = "theDefaultValueGhi789"; + + /** + * Verifies that a runtime parameter is able to be retrieved. + */ + @Test + public void runtimeConfigurationFound() { + Configuration configuration = spy(Configuration.class); + when(configuration.loadFromProperties(MY_CONFIGURATION)).thenReturn(EXPECTED_VALUE); + when(configuration.loadFromEnvironment(MY_CONFIGURATION)).thenReturn(null); + + assertEquals(EXPECTED_VALUE, configuration.get(MY_CONFIGURATION)); + } + + /** + * Verifies that an environment variable is able to be retrieved. + */ + @Test + public void environmentConfigurationFound() { + Configuration configuration = spy(Configuration.class); + when(configuration.loadFromProperties(MY_CONFIGURATION)).thenReturn(null); + when(configuration.loadFromEnvironment(MY_CONFIGURATION)).thenReturn(EXPECTED_VALUE); + + assertEquals(EXPECTED_VALUE, configuration.get(MY_CONFIGURATION)); + } + + /** + * Verifies that null is returned when a configuration isn't found. + */ + @Test + public void configurationNotFound() { + Configuration configuration = new Configuration(); + assertNull(configuration.get(MY_CONFIGURATION)); + } + + /** + * Verifies that runtime parameters are preferred over environment variables. + */ + @Test + public void runtimeConfigurationPreferredOverEnvironmentConfiguration() { + Configuration configuration = spy(Configuration.class); + when(configuration.loadFromProperties(MY_CONFIGURATION)).thenReturn(EXPECTED_VALUE); + when(configuration.loadFromEnvironment(MY_CONFIGURATION)).thenReturn(UNEXPECTED_VALUE); + + assertEquals(EXPECTED_VALUE, configuration.get(MY_CONFIGURATION)); + } + + /** + * Verifies that a found configuration value is preferred over the default value. + */ + @Test + public void foundConfigurationPreferredOverDefault() { + Configuration configuration = spy(Configuration.class); + when(configuration.loadFromEnvironment(MY_CONFIGURATION)).thenReturn(EXPECTED_VALUE); + + assertEquals(EXPECTED_VALUE, configuration.get(MY_CONFIGURATION, DEFAULT_VALUE)); + } + + /** + * Verifies that when a configuration value isn't found the default will be returned. + */ + @Test + public void fallbackToDefaultConfiguration() { + Configuration configuration = new Configuration(); + + assertEquals(DEFAULT_VALUE, configuration.get(MY_CONFIGURATION, DEFAULT_VALUE)); + } + + /** + * Verifies that a found configuration value is able to be mapped. + */ + @Test + public void foundConfigurationIsConverted() { + Configuration configuration = spy(Configuration.class); + when(configuration.loadFromProperties(MY_CONFIGURATION)).thenReturn(EXPECTED_VALUE); + + assertEquals(EXPECTED_VALUE.toUpperCase(), configuration.get(MY_CONFIGURATION, String::toUpperCase)); + } + + /** + * Verifies that when a configuration isn't found the converter returns null. + */ + @Test + public void notFoundConfigurationIsConvertedToNull() { + assertNull(new Configuration().get(MY_CONFIGURATION, String::toUpperCase)); + } + + @Test + public void cloneConfiguration() { + Configuration configuration = new Configuration() + .put("variable1", "value1") + .put("variable2", "value2"); + + Configuration configurationClone = configuration.clone(); + + // Verify that the clone has the expected values. + assertEquals(configuration.get("variable1"), configurationClone.get("variable1")); + assertEquals(configuration.get("variable2"), configurationClone.get("variable2")); + + // The clone should be a separate instance, verify its modifications won't affect the original copy. + configurationClone.remove("variable2"); + assertTrue(configuration.contains("variable2")); + } + + @Test + public void loadValueTwice() { + Configuration configuration = new Configuration(); + String tracingDisabled = configuration.get(PROPERTY_AZURE_TRACING_DISABLED); + String tracingDisabled2 = configuration.get(PROPERTY_AZURE_TRACING_DISABLED); + + assertEquals(tracingDisabled, tracingDisabled2); + } + + @ParameterizedTest + @MethodSource("getOrDefaultSupplier") + public void getOrDefault(String configurationValue, Object defaultValue, Object expectedValue) { + Configuration configuration = new Configuration() + .put("getOrDefault", configurationValue); + + assertEquals(expectedValue, configuration.get("getOrDefault", defaultValue)); + } + + private static Stream getOrDefaultSupplier() { + return Stream.of( + Arguments.of(String.valueOf((byte) 42), (byte) 12, (byte) 42), + Arguments.of(String.valueOf((short) 42), (short) 12, (short) 42), + Arguments.of(String.valueOf(42), 12, 42), + Arguments.of(String.valueOf(42L), 12L, 42L), + Arguments.of(String.valueOf(42F), 12F, 42F), + Arguments.of(String.valueOf(42D), 12D, 42D), + Arguments.of(String.valueOf(true), false, true), + Arguments.of("42", "12", "42") + ); + } + + @Test + public void getOrDefaultReturnsDefault() { + assertEquals("42", new Configuration().get("empty", "42")); + } +} From cdcdde57cc70987795d1dd01978195d6398e91bf Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Mon, 10 Jan 2022 16:54:38 -0800 Subject: [PATCH 17/17] don't use FluxUtil.monoError(builder) yet to simplify dependency management --- .../amqp/implementation/ReactorConnection.java | 9 +++++++-- .../amqp/implementation/ReactorSession.java | 17 +++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java index 324279cb91bf..88bdbb3724e3 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorConnection.java @@ -24,6 +24,7 @@ import org.apache.qpid.proton.reactor.Reactor; import reactor.core.Disposable; import reactor.core.Disposables; +import reactor.core.Exceptions; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.Sinks; @@ -50,7 +51,6 @@ import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.SESSION_NAME_KEY; import static com.azure.core.amqp.implementation.ClientConstants.SIGNAL_TYPE_KEY; -import static com.azure.core.util.FluxUtil.monoError; /** * An AMQP connection backed by proton-j. @@ -192,7 +192,12 @@ public Flux getShutdownSignals() { public Mono getManagementNode(String entityPath) { return Mono.defer(() -> { if (isDisposed()) { - return monoError(logger.atError().addKeyValue(ENTITY_PATH_KEY, entityPath), new IllegalStateException("Connection is disposed. Cannot get management instance.")); + // TODO(limolkova) this can be simplified with FluxUtil.monoError(LoggingEventBuilder), not using it for now + // to allow using azure-core-amqp with stable azure-core 1.24.0 to simplify dependency management + // we should switch to it once monoError(LoggingEventBuilder) ships in stable azure-core + return Mono.error(logger.atError() + .addKeyValue(ENTITY_PATH_KEY, entityPath) + .log(Exceptions.propagate(new IllegalStateException("Connection is disposed. Cannot get management instance.")))); } final AmqpManagementNode existing = managementNodes.get(entityPath); diff --git a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java index ca8dcf47d4fd..79609df90635 100644 --- a/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java +++ b/sdk/core/azure-core-amqp/src/main/java/com/azure/core/amqp/implementation/ReactorSession.java @@ -33,6 +33,7 @@ import org.apache.qpid.proton.engine.Session; import reactor.core.Disposable; import reactor.core.Disposables; +import reactor.core.Exceptions; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoSink; @@ -51,14 +52,13 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addSignalTypeAndResult; import static com.azure.core.amqp.implementation.AmqpLoggingUtils.createContextWithConnectionId; import static com.azure.core.amqp.implementation.ClientConstants.ENTITY_PATH_KEY; import static com.azure.core.amqp.implementation.ClientConstants.LINK_NAME_KEY; -import static com.azure.core.amqp.implementation.AmqpLoggingUtils.addErrorCondition; import static com.azure.core.amqp.implementation.ClientConstants.NOT_APPLICABLE; import static com.azure.core.amqp.implementation.ClientConstants.SESSION_NAME_KEY; -import static com.azure.core.util.FluxUtil.monoError; /** * Represents an AMQP session using proton-j reactor. @@ -353,7 +353,11 @@ protected Mono createConsumer(String linkName, String entityPat .addKeyValue(ENTITY_PATH_KEY, entityPath) .addKeyValue(LINK_NAME_KEY, linkName); - return monoError(logBuilder, new AmqpException(true, "Cannot create receive link from a closed session.", sessionHandler.getErrorContext())); + // TODO(limolkova) this can be simplified with FluxUtil.monoError(LoggingEventBuilder), not using it for now + // to allow using azure-core-amqp with stable azure-core 1.24.0 to simplify dependency management + // we should switch to it once monoError(LoggingEventBuilder) ships in stable azure-core + return Mono.error(logBuilder + .log(Exceptions.propagate(new AmqpException(true, "Cannot create receive link from a closed session.", sessionHandler.getErrorContext())))); } final LinkSubscription existingLink = openReceiveLinks.get(linkName); @@ -451,7 +455,12 @@ private Mono createProducer(String linkName, String entityPath, .addKeyValue(SESSION_NAME_KEY, sessionName) .addKeyValue(ENTITY_PATH_KEY, entityPath) .addKeyValue(LINK_NAME_KEY, linkName); - return monoError(logBuilder, new AmqpException(true, "Cannot create send link from a closed session.", sessionHandler.getErrorContext())); + + // TODO(limolkova) this can be simplified with FluxUtil.monoError(LoggingEventBuilder), not using it for now + // to allow using azure-core-amqp with stable azure-core 1.24.0 to simplify dependency management + // we should switch to it once monoError(LoggingEventBuilder) ships in stable azure-core + return Mono.error(logBuilder + .log(Exceptions.propagate(new AmqpException(true, "Cannot create send link from a closed session.", sessionHandler.getErrorContext())))); } final LinkSubscription existing = openSendLinks.get(linkName);