959b783d8fdc0efcfd8724f0761f1d1a916f39c7
[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
15 import com.google.common.annotations.Beta;
16 import java.text.DecimalFormat;
17 import java.text.DecimalFormatSymbols;
18 import java.util.Date;
19 import java.util.concurrent.TimeUnit;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 /**
24  * Abstract class that calculates and tracks time duration statistics.
25  *
26  * @author Thomas Pantelis
27  * @author Robert Varga
28  */
29 @Beta
30 public abstract class DurationStatisticsTracker {
31     private static final Logger LOG = LoggerFactory.getLogger(DurationStatisticsTracker.class);
32     private static final DecimalFormat DECIMAL_FORMAT;
33
34     static {
35         final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
36         symbols.setDecimalSeparator('.');
37         DECIMAL_FORMAT = new DecimalFormat("0.00", symbols);
38     }
39
40     /**
41      * Create a concurrent {@link DurationStatisticsTracker}, which performs well
42      * in very contended environments.
43      *
44      * @return A new instance.
45      */
46     public static DurationStatisticsTracker createConcurrent() {
47         return new ConcurrentDurationStatisticsTracker();
48     }
49
50     /**
51      * Create a synchronized {@link DurationStatisticsTracker}, which performs well
52      * in non-contended environments.
53      *
54      * @return A new instance.
55      */
56     public static DurationStatisticsTracker createSynchronized() {
57         return new SynchronizedDurationStatsTracker();
58     }
59
60     /**
61      * Add a duration to track.
62      *
63      * @param duration
64      *            non-negative duration in nanoseconds.
65      */
66     public abstract void addDuration(long duration);
67
68     /**
69      * Returns the average duration in nanoseconds.
70      */
71     public abstract double getAverageDuration();
72
73     /**
74      * Returns the total number of tracked durations.
75      *
76      * @return Total number of measurements accumulated since last
77      *         {@link #reset()}.
78      */
79     public abstract long getTotalDurations();
80
81     /**
82      * Resets all statistics back to their defaults.
83      */
84     public abstract void reset();
85
86     /**
87      * Returns the longest duration in nanoseconds.
88      */
89     public final long getLongestDuration() {
90         return getDuration(getLongest());
91     }
92
93     /**
94      * Returns the shortest duration in nanoseconds.
95      */
96     public final long getShortestDuration() {
97         return getDuration(getShortest());
98     }
99
100     /**
101      * Returns the average duration as a displayable String with units, e.g.
102      * "12.34 ms".
103      */
104     public final String getDisplayableAverageDuration() {
105         return formatDuration(getAverageDuration(), null);
106     }
107
108     /**
109      * Returns the longest duration as a displayable String with units and the
110      * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
111      */
112     public final String getDisplayableLongestDuration() {
113         return formatDuration(getLongest());
114     }
115
116     /**
117      * Returns the shortest duration as a displayable String with units and the
118      * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
119      */
120     public final String getDisplayableShortestDuration() {
121         return formatDuration(getShortest());
122     }
123
124     /**
125      * Returns the time stamp of the longest duration.
126      */
127     public final long getTimeOfLongestDuration() {
128         return getTimeMillis(getLongest());
129     }
130
131     /**
132      * Returns the time stamp of the shortest duration.
133      */
134     public final long getTimeOfShortestDuration() {
135         return getTimeMillis(getShortest());
136     }
137
138     /**
139      * Get the shortest recorded duration and the time when it was recorded.
140      *
141      * @return Duration and timestamp.
142      */
143     abstract DurationWithTime getShortest();
144
145     /**
146      * Get the longest recorded duration and the time when it was recorded.
147      *
148      * @return Duration and timestamp.
149      */
150     abstract DurationWithTime getLongest();
151
152     /**
153      * Returns formatted value of number, e.g. "12.34". Always is used dot as
154      * decimal separator.
155      */
156     private static synchronized String formatDecimalValue(final double value) {
157         return DECIMAL_FORMAT.format(value);
158     }
159
160     private static long getDuration(final DurationWithTime current) {
161         return current == null ? 0L : current.getDuration();
162     }
163
164     private static long getTimeMillis(final DurationWithTime current) {
165         return current == null ? 0L : current.getTimeMillis();
166     }
167
168     private static String formatDuration(final double duration, final Long timeStamp) {
169         final TimeUnit unit = chooseUnit((long) duration);
170         final double value = duration / NANOSECONDS.convert(1, unit);
171
172         final StringBuilder sb = new StringBuilder();
173         sb.append(formatDecimalValue(value));
174         sb.append(' ');
175         sb.append(abbreviate(unit));
176
177         if (timeStamp != null) {
178             sb.append(String.format(" at %1$tD %1$tT", new Date(timeStamp)));
179         }
180
181         return sb.toString();
182     }
183
184     private static String formatDuration(final DurationWithTime current) {
185         if (current == null) {
186             return formatDuration(0, null);
187         }
188         return formatDuration(current.getDuration(), current.getTimeMillis());
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 "μ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             default:
222                 LOG.warn("Unhandled time unit {}", unit);
223                 return "";
224         }
225     }
226 }