All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.hystrix.HystrixRequestLog Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2012 Netflix, Inc.
 * 
 * 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.netflix.hystrix;

import com.netflix.hystrix.metric.HystrixRequestEventsStream;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableHolder;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Log of {@link HystrixCommand} executions and events during the current request.
 */
public class HystrixRequestLog {
    private static final Logger logger = LoggerFactory.getLogger(HystrixRequestLog.class);

    /**
     * RequestLog: Reduce Chance of Memory Leak
     * https://github.com/Netflix/Hystrix/issues/53
     * 
     * Upper limit on RequestLog before ignoring further additions and logging warnings.
     * 
     * Intended to help prevent memory leaks when someone isn't aware of the
     * HystrixRequestContext lifecycle or enabling/disabling RequestLog.
     */
    /* package */static final int MAX_STORAGE = 1000;

    private static final HystrixRequestVariableHolder currentRequestLog = new HystrixRequestVariableHolder(new HystrixRequestVariableLifecycle() {
        @Override
        public HystrixRequestLog initialValue() {
            return new HystrixRequestLog();
        }

        public void shutdown(HystrixRequestLog value) {
            //write this value to the Request stream
            HystrixRequestEventsStream.getInstance().write(value.getAllExecutedCommands());
        }
    });

    /**
     * History of {@link HystrixCommand} executed in this request.
     */
    private LinkedBlockingQueue> executedCommands = new LinkedBlockingQueue>(MAX_STORAGE);

    /**
     * History of {@link HystrixInvokableInfo} executed in this request.
     */
    private LinkedBlockingQueue> allExecutedCommands = new LinkedBlockingQueue>(MAX_STORAGE);

    // prevent public instantiation
    private HystrixRequestLog() {
    }

    /**
     * {@link HystrixRequestLog} for current request as defined by {@link HystrixRequestContext}.
     * 
     * @return {@link HystrixRequestLog}
     */
    public static HystrixRequestLog getCurrentRequest(HystrixConcurrencyStrategy concurrencyStrategy) {
        return currentRequestLog.get(concurrencyStrategy);
    }

    /**
     * {@link HystrixRequestLog} for current request as defined by {@link HystrixRequestContext}.
     * 

* NOTE: This uses the default {@link HystrixConcurrencyStrategy} or global override. If an injected strategy is being used by commands you must instead use * {@link #getCurrentRequest(HystrixConcurrencyStrategy)}. * * @return {@link HystrixRequestLog} */ public static HystrixRequestLog getCurrentRequest() { return currentRequestLog.get(HystrixPlugins.getInstance().getConcurrencyStrategy()); } /** * Retrieve {@link HystrixCommand} instances that were executed during this {@link HystrixRequestContext}. * * @return {@code Collection>} */ @Deprecated public Collection> getExecutedCommands() { return Collections.unmodifiableCollection(executedCommands); } /** * Retrieve {@link HystrixCommand} instances that were executed during this {@link HystrixRequestContext}. * * @return {@code Collection>} */ public Collection> getAllExecutedCommands() { return Collections.unmodifiableCollection(allExecutedCommands); } /** * Add {@link HystrixCommand} instance to the request log. * * @param command * {@code HystrixCommand} */ /* package */void addExecutedCommand(HystrixInvokableInfo command) { if (!allExecutedCommands.offer(command)) { // see RequestLog: Reduce Chance of Memory Leak https://github.com/Netflix/Hystrix/issues/53 logger.warn("RequestLog ignoring command after reaching limit of " + MAX_STORAGE + ". See https://github.com/Netflix/Hystrix/issues/53 for more information."); } // TODO remove this when deprecation completed if (command instanceof HystrixCommand) { @SuppressWarnings("rawtypes") HystrixCommand _c = (HystrixCommand) command; if (!executedCommands.offer(_c)) { // see RequestLog: Reduce Chance of Memory Leak https://github.com/Netflix/Hystrix/issues/53 logger.warn("RequestLog ignoring command after reaching limit of " + MAX_STORAGE + ". See https://github.com/Netflix/Hystrix/issues/53 for more information."); } } } /** * Formats the log of executed commands into a string usable for logging purposes. *

* Examples: *

    *
  • TestCommand[SUCCESS][1ms]
  • *
  • TestCommand[SUCCESS][1ms], TestCommand[SUCCESS, RESPONSE_FROM_CACHE][1ms]x4
  • *
  • TestCommand[TIMEOUT][1ms]
  • *
  • TestCommand[FAILURE][1ms]
  • *
  • TestCommand[THREAD_POOL_REJECTED][1ms]
  • *
  • TestCommand[THREAD_POOL_REJECTED, FALLBACK_SUCCESS][1ms]
  • *
  • TestCommand[EMIT, SUCCESS][1ms]
  • *
  • TestCommand[EMITx5, SUCCESS][1ms]
  • *
  • TestCommand[EMITx5, FAILURE, FALLBACK_EMITx6, FALLBACK_FAILURE][100ms]
  • *
  • TestCommand[FAILURE, FALLBACK_SUCCESS][1ms], TestCommand[FAILURE, FALLBACK_SUCCESS, RESPONSE_FROM_CACHE][1ms]x4
  • *
  • GetData[SUCCESS][1ms], PutData[SUCCESS][1ms], GetValues[SUCCESS][1ms], GetValues[SUCCESS, RESPONSE_FROM_CACHE][1ms], TestCommand[FAILURE, FALLBACK_FAILURE][1ms], TestCommand[FAILURE, * FALLBACK_FAILURE, RESPONSE_FROM_CACHE][1ms]
  • *
*

* If a command has a multiplier such as x4, that means this command was executed 4 times with the same events. The time in milliseconds is the sum of the 4 executions. *

* For example, TestCommand[SUCCESS][15ms]x4 represents TestCommand being executed 4 times and the sum of those 4 executions was 15ms. These 4 each executed the run() method since * RESPONSE_FROM_CACHE was not present as an event. * * If an EMIT or FALLBACK_EMIT has a multiplier such as x5, that means a HystrixObservableCommand was used and it emitted that number of OnNexts. *

* For example, TestCommand[EMITx5, FAILURE, FALLBACK_EMITx6, FALLBACK_FAILURE][100ms] represents TestCommand executing observably, emitted 5 OnNexts, then an OnError. * This command also has an Observable fallback, and it emits 6 OnNexts, then an OnCompleted. * * @return String request log or "Unknown" if unable to instead of throwing an exception. */ public String getExecutedCommandsAsString() { try { LinkedHashMap aggregatedCommandsExecuted = new LinkedHashMap(); Map aggregatedCommandExecutionTime = new HashMap(); StringBuilder builder = new StringBuilder(); int estimatedLength = 0; for (HystrixInvokableInfo command : allExecutedCommands) { builder.setLength(0); builder.append(command.getCommandKey().name()); List events = new ArrayList(command.getExecutionEvents()); if (events.size() > 0) { Collections.sort(events); //replicate functionality of Arrays.toString(events.toArray()) to append directly to existing StringBuilder builder.append("["); for (HystrixEventType event : events) { switch (event) { case EMIT: int numEmissions = command.getNumberEmissions(); if (numEmissions > 1) { builder.append(event).append("x").append(numEmissions).append(", "); } else { builder.append(event).append(", "); } break; case FALLBACK_EMIT: int numFallbackEmissions = command.getNumberFallbackEmissions(); if (numFallbackEmissions > 1) { builder.append(event).append("x").append(numFallbackEmissions).append(", "); } else { builder.append(event).append(", "); } break; default: builder.append(event).append(", "); } } builder.setCharAt(builder.length() - 2, ']'); builder.setLength(builder.length() - 1); } else { builder.append("[Executed]"); } String display = builder.toString(); estimatedLength += display.length() + 12; //add 12 chars to display length for appending totalExecutionTime and count below Integer counter = aggregatedCommandsExecuted.get(display); if( counter != null){ aggregatedCommandsExecuted.put(display, counter + 1); } else { // add it aggregatedCommandsExecuted.put(display, 1); } int executionTime = command.getExecutionTimeInMilliseconds(); if (executionTime < 0) { // do this so we don't create negative values or subtract values executionTime = 0; } counter = aggregatedCommandExecutionTime.get(display); if( counter != null && executionTime > 0){ // add to the existing executionTime (sum of executionTimes for duplicate command displayNames) aggregatedCommandExecutionTime.put(display, aggregatedCommandExecutionTime.get(display) + executionTime); } else { // add it aggregatedCommandExecutionTime.put(display, executionTime); } } builder.setLength(0); builder.ensureCapacity(estimatedLength); for (String displayString : aggregatedCommandsExecuted.keySet()) { if (builder.length() > 0) { builder.append(", "); } builder.append(displayString); int totalExecutionTime = aggregatedCommandExecutionTime.get(displayString); builder.append("[").append(totalExecutionTime).append("ms]"); int count = aggregatedCommandsExecuted.get(displayString); if (count > 1) { builder.append("x").append(count); } } return builder.toString(); } catch (Exception e) { logger.error("Failed to create HystrixRequestLog response header string.", e); // don't let this cause the entire app to fail so just return "Unknown" return "Unknown"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy