/* * Copyright 2014 Avanza Bank AB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.avanza.astrix.ft.hystrix; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.avanza.astrix.beans.config.AstrixConfig; import com.avanza.astrix.beans.config.BeanConfiguration; import com.avanza.astrix.beans.core.AstrixBeanKey; import com.avanza.astrix.beans.core.AstrixBeanSettings; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixEventType; import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; final class FailedServiceInvocationLogger extends HystrixEventNotifier { private static final Logger log = LoggerFactory.getLogger(FailedServiceInvocationLogger.class); private final BeanMapping beanMapping; private final AstrixConfig astrixConfig; private final Map<AstrixBeanKey<?>, FailedBeanInvocationLogger> beanLoggerByBeanKey = new ConcurrentHashMap<>(); public FailedServiceInvocationLogger(BeanMapping beanMapping, AstrixConfig config) { this.beanMapping = beanMapping; this.astrixConfig = config; } @Override public void markEvent(HystrixEventType eventType, HystrixCommandKey key) { logEvent(eventType, key); } private void logEvent(HystrixEventType eventType, HystrixCommandKey commandKey) { getBeanInvocationLogger(commandKey).orElse(createNonBeanInvocationCommandLogger(commandKey)) .accept(eventType); } private Consumer<HystrixEventType> createNonBeanInvocationCommandLogger(HystrixCommandKey key) { return eventType -> { switch (eventType) { case FAILURE: case SEMAPHORE_REJECTED: case THREAD_POOL_REJECTED: case TIMEOUT: case SHORT_CIRCUITED: log.info(String.format("Aborted command execution: cause=%s astrixBean=null hystrixCommandKey=%s", eventType, key.name())); break; default: // Do nothing } }; } private Optional<Consumer<HystrixEventType>> getBeanInvocationLogger(HystrixCommandKey commandKey) { return beanMapping.getBeanKey(commandKey) .map(beanKey -> getOrCreateBeanInvocationLogger(commandKey, beanKey)); } private Consumer<HystrixEventType> getOrCreateBeanInvocationLogger(HystrixCommandKey commandKey, AstrixBeanKey<?> beanKey) { return beanLoggerByBeanKey.computeIfAbsent(beanKey, k -> createLogger(commandKey, beanKey)); } private FailedBeanInvocationLogger createLogger(HystrixCommandKey commandKey, AstrixBeanKey<?> beanKey) { return new FailedBeanInvocationLogger(astrixConfig.getBeanConfiguration(beanKey), beanKey, commandKey); } private static final class FailedBeanInvocationLogger implements Consumer<HystrixEventType> { private final BeanConfiguration beanConfiguration; private final AstrixBeanKey<?> beanKey; private final HystrixCommandKey hystrixCommandKey; public FailedBeanInvocationLogger(BeanConfiguration beanConfiguration, AstrixBeanKey<?> beanKey, HystrixCommandKey hystrixCommandKey) { this.beanConfiguration = beanConfiguration; this.beanKey = beanKey; this.hystrixCommandKey = hystrixCommandKey; } @Override public void accept(HystrixEventType t) { logEvent(t); } private void logEvent(HystrixEventType eventType) { switch (eventType) { case FAILURE: log.info(String.format("Aborted command execution: cause=%s astrixBean=%s hystrixCommandKey=%s", eventType, beanKey, hystrixCommandKey.name())); break; case SEMAPHORE_REJECTED: logSemaphoreRejectedRequest(eventType); break; case THREAD_POOL_REJECTED: logThreadPoolRejectedRequest(eventType); break; case TIMEOUT: logTimeoutRequest(eventType); break; case SHORT_CIRCUITED: log.info(String.format("Aborted command execution: cause=%s astrixBean=%s hystrixCommandKey=%s", eventType, beanKey, hystrixCommandKey.name())); break; default: // Do nothing } } private void logTimeoutRequest(HystrixEventType eventType) { log.info(String.format("Aborted command execution: cause=%s astrixBean=%s hystrixCommandKey=%s TIMEOUT=%s [ms]", eventType, beanKey, hystrixCommandKey.name(), beanConfiguration.get(AstrixBeanSettings.TIMEOUT).get())); } private void logThreadPoolRejectedRequest(HystrixEventType eventType) { log.info(String.format("Aborted command execution: cause=%s astrixBean=%s hystrixCommandKey=%s CORE_SIZE=%s QUEUE_SIZE_REJECTION_THRESHOLD=%s", eventType, beanKey, hystrixCommandKey.name(), beanConfiguration.get(AstrixBeanSettings.CORE_SIZE).get(), beanConfiguration.get(AstrixBeanSettings.QUEUE_SIZE_REJECTION_THRESHOLD).get())); } private void logSemaphoreRejectedRequest(HystrixEventType eventType) { log.info(String.format("Aborted command execution: cause=%s astrixBean=%s hystrixCommandKey=%s MAX_CONCURRENT_REQUESTS=%s", eventType, beanKey, hystrixCommandKey.name(), beanConfiguration.get(AstrixBeanSettings.MAX_CONCURRENT_REQUESTS).get())); } } }