/*
* Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.amazonaws.util;
import com.amazonaws.metrics.AwsSdkMetrics;
import com.amazonaws.metrics.MetricType;
import com.amazonaws.metrics.RequestMetricCollector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* In contrast to {@link AWSRequestMetrics}, which is intended to be a minimal
* support of AWS SDK request metrics, this class is the full support of AWS SDK
* request metrics including features such as properties and sub-events.
* <p>
* This class is instantiated instead of {@link AWSRequestMetrics} when request
* metric collection is required during a particular service request/response
* cycle.
*
* @deprecated metrics is deprecated
*/
@Deprecated
public class AWSRequestMetricsFullSupport extends AWSRequestMetrics {
/* Stores some key value pairs. */
private final Map<String, List<Object>> properties = new HashMap<String, List<Object>>();
/* A map to store events that are being profiled. */
private final Map<String, TimingInfo> eventsBeingProfiled = new HashMap<String, TimingInfo>();
/* Latency Logger */
private static final Log latencyLogger = LogFactory.getLog("com.amazonaws.latency");
private static final Object KEY_VALUE_SEPARATOR = "=";
private static final Object COMMA_SEPARATOR = ", ";
/**
* This constructor should be used in the case when AWS SDK metrics
* collector is enabled.
*
* @see AWSRequestMetricsFullSupport
*/
public AWSRequestMetricsFullSupport() {
super(TimingInfo.startTimingFullSupport());
}
/**
* Start an event which will be timed. The startTime and endTime are added
* to timingInfo only after endEvent is called. For every startEvent there
* should be a corresponding endEvent. If you start the same event without
* ending it, this will overwrite the old event. i.e. There is no support
* for recursive events yet. Having said that, if you start and end an event
* in that sequence multiple times, all events are logged in timingInfo in
* that order. This feature is enabled if the system property
* "com.amazonaws.sdk.enableRuntimeProfiling" is set, or if a
* {@link RequestMetricCollector} is in use either at the request, web
* service client, or AWS SDK level.
*
* @param eventName - The name of the event to start
* @see AwsSdkMetrics
*/
@Override
public void startEvent(String eventName) {
/* This will overwrite past events */
eventsBeingProfiled.put // ignoring the wall clock time
(eventName, TimingInfo.startTimingFullSupport(System.nanoTime()));
}
@Override
public void startEvent(MetricType f) {
startEvent(f.name());
}
/**
* End an event which was previously started. Once ended, log how much time
* the event took. It is illegal to end an Event that was not started. It is
* good practice to endEvent in a finally block. See Also startEvent.
*
* @param eventName - The name of the event to start
*/
@Override
public void endEvent(String eventName) {
TimingInfo event = eventsBeingProfiled.get(eventName);
/* Somebody tried to end an event that was not started. */
if (event == null) {
LogFactory.getLog(getClass()).warn
("Trying to end an event which was never started: " + eventName);
return;
}
event.endTiming();
this.timingInfo.addSubMeasurement(
eventName,
TimingInfo.unmodifiableTimingInfo(
event.getStartTimeNano(),
event.getEndTimeNano()));
}
@Override
public void endEvent(MetricType f) {
endEvent(f.name());
}
/**
* Add 1 to an existing count for a given event. If the count for that event
* does not exist, then it creates one and initializes it to 1. This feature
* is enabled if the system property
* "com.amazonaws.sdk.enableRuntimeProfiling" is set, or if a
* {@link RequestMetricCollector} is in use either at the request, web
* service client, or AWS SDK level.
*
* @param event - The name of the event to count
*/
@Override
public void incrementCounter(String event) {
timingInfo.incrementCounter(event);
}
@Override
public void incrementCounter(MetricType f) {
incrementCounter(f.name());
}
@Override
public void setCounter(String counterName, long count) {
timingInfo.setCounter(counterName, count);
}
@Override
public void setCounter(MetricType f, long count) {
setCounter(f.name(), count);
}
/**
* Add a property. If you add the same property more than once, it stores
* all values a list. This feature is enabled if the system property
* "com.amazonaws.sdk.enableRuntimeProfiling" is set, or if a
* {@link RequestMetricCollector} is in use either at the request, web
* service client, or AWS SDK level.
*
* @param propertyName The name of the property
* @param value The property value
*/
@Override
public void addProperty(String propertyName, Object value) {
List<Object> propertyList = properties.get(propertyName);
if (propertyList == null) {
propertyList = new ArrayList<Object>();
properties.put(propertyName, propertyList);
}
propertyList.add(value);
}
@Override
public void addProperty(MetricType f, Object value) {
addProperty(f.name(), value);
}
@Override
public void log() {
if (latencyLogger.isInfoEnabled()) {
StringBuilder builder = new StringBuilder();
for (Entry<String, List<Object>> entry : properties.entrySet()) {
keyValueFormat(entry.getKey(), entry.getValue(), builder);
}
for (Entry<String, Number> entry : timingInfo.getAllCounters()
.entrySet()) {
keyValueFormat(entry.getKey(), entry.getValue(), builder);
}
for (Entry<String, List<TimingInfo>> entry : timingInfo
.getSubMeasurementsByName().entrySet()) {
keyValueFormat(entry.getKey(), entry.getValue(), builder);
}
latencyLogger.info(builder.toString());
}
}
private void keyValueFormat(Object key, Object value, StringBuilder builder) {
builder.append(key).append(KEY_VALUE_SEPARATOR).append(value).append(COMMA_SEPARATOR);
}
@Override
public List<Object> getProperty(String propertyName) {
return properties.get(propertyName);
}
@Override
public List<Object> getProperty(MetricType f) {
return getProperty(f.name());
}
/** Always returns true. */
@Override
public final boolean isEnabled() {
return true;
}
}