10cb39ce7571f6b629c80944d593795a9540a27c
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / DurationStatisticsTracker.java
1 /*
2  * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.util;
9
10 import static java.util.concurrent.TimeUnit.MICROSECONDS;
11 import static java.util.concurrent.TimeUnit.MILLISECONDS;
12 import static java.util.concurrent.TimeUnit.NANOSECONDS;
13 import static java.util.concurrent.TimeUnit.SECONDS;
14 import com.google.common.annotations.Beta;
15 import java.text.DecimalFormat;
16 import java.text.DecimalFormatSymbols;
17 import java.util.Date;
18 import java.util.concurrent.TimeUnit;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 /**
23  * Abstract class that calculates and tracks time duration statistics.
24  *
25  * @author Thomas Pantelis
26  * @author Robert Varga
27  */
28 @Beta
29 public abstract class DurationStatisticsTracker {
30     private static final Logger LOG = LoggerFactory.getLogger(DurationStatisticsTracker.class);
31     private static final DecimalFormat DECIMAL_FORMAT;
32
33     static {
34         final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
35         symbols.setDecimalSeparator('.');
36         DECIMAL_FORMAT = new DecimalFormat("0.00", symbols);
37     }
38
39     /**
40      * Create a concurrent {@link DurationStatisticsTracker}, which performs well
41      * in very contended environments.
42      *
43      * @return A new instance.
44      */
45     public static DurationStatisticsTracker createConcurrent() {
46         return new ConcurrentDurationStatisticsTracker();
47     }
48
49     /**
50      * Create a synchronized {@link DurationStatisticsTracker}, which performs well
51      * in non-contended environments.
52      *
53      * @return A new instance.
54      */
55     public static DurationStatisticsTracker createSynchronized() {
56         return new SynchronizedDurationStatsTracker();
57     }
58
59     /**
60      * Add a duration to track.
61      *
62      * @param duration
63      *            non-negative duration in nanoseconds.
64      */
65     public abstract void addDuration(long duration);
66
67     /**
68      * Returns the average duration in nanoseconds.
69      */
70     public abstract double getAverageDuration();
71
72     /**
73      * Returns the total number of tracked durations.
74      *
75      * @return Total number of measurements accumulated since last
76      *         {@link #reset()}.
77      */
78     public abstract long getTotalDurations();
79
80     /**
81      * Resets all statistics back to their defaults.
82      */
83     public abstract void reset();
84
85     /**
86      * Get the shortest recorded duration and the time when it was recorded.
87      *
88      * @return Duration and timestamp.
89      */
90     protected abstract DurationWithTime getShortest();
91
92     /**
93      * Get the longest recorded duration and the time when it was recorded.
94      *
95      * @return Duration and timestamp.
96      */
97     protected abstract DurationWithTime getLongest();
98
99     /**
100      * Returns the longest duration in nanoseconds.
101      */
102     public final long getLongestDuration() {
103         return getDuration(getLongest());
104     }
105
106     /**
107      * Returns the shortest duration in nanoseconds.
108      */
109     public final long getShortestDuration() {
110         return getDuration(getShortest());
111     }
112
113     /**
114      * Returns the average duration as a displayable String with units, e.g.
115      * "12.34 ms".
116      */
117     public final String getDisplayableAverageDuration() {
118         return formatDuration(getAverageDuration(), null);
119     }
120
121     /**
122      * Returns the longest duration as a displayable String with units and the
123      * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
124      */
125     public final String getDisplayableLongestDuration() {
126         return formatDuration(getLongest());
127     }
128
129     /**
130      * Returns the shortest duration as a displayable String with units and the
131      * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
132      */
133     public final String getDisplayableShortestDuration() {
134         return formatDuration(getShortest());
135     }
136
137     /**
138      * Returns the time stamp of the longest duration.
139      */
140     public final long getTimeOfLongestDuration() {
141         return getTimeMillis(getLongest());
142     }
143
144     /**
145      * Returns the time stamp of the shortest duration.
146      */
147     public final long getTimeOfShortestDuration() {
148         return getTimeMillis(getShortest());
149     }
150
151     /**
152      * Returns formatted value of number, e.g. "12.34". Always is used dot as
153      * decimal separator.
154      */
155     private static synchronized String formatDecimalValue(final double value) {
156         return DECIMAL_FORMAT.format(value);
157     }
158
159     private static long getDuration(final DurationWithTime current) {
160         return current == null ? 0L : current.getDuration();
161     }
162
163     private static long getTimeMillis(final DurationWithTime current) {
164         return current == null ? 0L : current.getTimeMillis();
165     }
166
167     private static String formatDuration(final double duration, final Long timeStamp) {
168         final TimeUnit unit = chooseUnit((long) duration);
169         final double value = duration / NANOSECONDS.convert(1, unit);
170
171         final StringBuilder sb = new StringBuilder();
172         sb.append(formatDecimalValue(value));
173         sb.append(' ');
174         sb.append(abbreviate(unit));
175
176         if (timeStamp != null) {
177             sb.append(String.format(" at %1$tD %1$tT", new Date(timeStamp)));
178         }
179
180         return sb.toString();
181     }
182
183     private static String formatDuration(final DurationWithTime current) {
184         if (current != null) {
185             return formatDuration(current.getDuration(), current.getTimeMillis());
186         } else {
187             return formatDuration(0, null);
188         }
189     }
190
191     private static TimeUnit chooseUnit(final long nanos) {
192         // TODO: this could be inlined, as we are doing needless divisions
193         if (NANOSECONDS.toSeconds(nanos) > 0) {
194             return SECONDS;
195         }
196         if (NANOSECONDS.toMillis(nanos) > 0) {
197             return MILLISECONDS;
198         }
199         if (NANOSECONDS.toMicros(nanos) > 0) {
200             return MICROSECONDS;
201         }
202         return NANOSECONDS;
203     }
204
205     private static String abbreviate(final TimeUnit unit) {
206         switch (unit) {
207         case NANOSECONDS:
208             return "ns";
209         case MICROSECONDS:
210             return "\u03bcs"; // μs
211         case MILLISECONDS:
212             return "ms";
213         case SECONDS:
214             return "s";
215         case MINUTES:
216             return "m";
217         case HOURS:
218             return "h";
219         case DAYS:
220             return "d";
221         }
222
223         LOG.warn("Unhandled time unit {}", unit);
224         return "";
225     }
226 }