9a29dca6a8002eaf1b62f96a44baaa8c7a2d69ce
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / DurationStatsTracker.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
9 package org.opendaylight.yangtools.util;
10
11 import static java.util.concurrent.TimeUnit.MICROSECONDS;
12 import static java.util.concurrent.TimeUnit.MILLISECONDS;
13 import static java.util.concurrent.TimeUnit.NANOSECONDS;
14 import static java.util.concurrent.TimeUnit.SECONDS;
15
16 import com.google.common.util.concurrent.AtomicDouble;
17 import java.text.DecimalFormat;
18 import java.text.DecimalFormatSymbols;
19 import java.util.Date;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.atomic.AtomicLong;
22
23 /**
24  * Class that calculates and tracks time duration statistics.
25  *
26  * @author Thomas Pantelis
27  */
28 public class DurationStatsTracker {
29
30     private static final DecimalFormat decimalFormat;
31
32     private final AtomicLong totalDurations = new AtomicLong();
33     private final AtomicLong longestDuration = new AtomicLong();
34     private volatile long timeOfLongestDuration;
35     private final AtomicLong shortestDuration = new AtomicLong(Long.MAX_VALUE);
36     private volatile long timeOfShortestDuration;
37     private final AtomicDouble averageDuration = new AtomicDouble();
38
39     static {
40         final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance();
41         symbols.setDecimalSeparator('.');
42         decimalFormat = new DecimalFormat("0.00", symbols);
43     }
44
45     /**
46      * Add a duration to track.
47      *
48      * @param duration
49      *            the duration in nanoseconds.
50      */
51     public void addDuration(long duration) {
52
53         double currentAve = averageDuration.get();
54         long currentTotal = totalDurations.get();
55
56         long newTotal = currentTotal + 1;
57
58         // Calculate moving cumulative average.
59         double newAve = currentAve * currentTotal / newTotal + (double) duration / (double) newTotal;
60
61         averageDuration.compareAndSet(currentAve, newAve);
62         totalDurations.compareAndSet(currentTotal, newTotal);
63
64         long longest = longestDuration.get();
65         if (duration > longest) {
66             if (longestDuration.compareAndSet(longest, duration)) {
67                 timeOfLongestDuration = System.currentTimeMillis();
68             }
69         }
70
71         long shortest = shortestDuration.get();
72         if (duration < shortest) {
73             if (shortestDuration.compareAndSet(shortest, duration)) {
74                 timeOfShortestDuration = System.currentTimeMillis();
75             }
76         }
77     }
78
79     /**
80      * Returns the total number of tracked durations.
81      */
82     public long getTotalDurations() {
83         return totalDurations.get();
84     }
85
86     /**
87      * Returns the longest duration in nanoseconds.
88      */
89     public long getLongestDuration() {
90         return longestDuration.get();
91     }
92
93     /**
94      * Returns the shortest duration in nanoseconds.
95      */
96     public long getShortestDuration() {
97         long shortest = shortestDuration.get();
98         return shortest < Long.MAX_VALUE ? shortest : 0;
99     }
100
101     /**
102      * Returns the average duration in nanoseconds.
103      */
104     public double getAverageDuration() {
105         return averageDuration.get();
106     }
107
108     /**
109      * Returns the time stamp of the longest duration.
110      */
111     public long getTimeOfLongestDuration() {
112         return timeOfLongestDuration;
113     }
114
115     /**
116      * Returns the time stamp of the shortest duration.
117      */
118     public long getTimeOfShortestDuration() {
119         return timeOfShortestDuration;
120     }
121
122     /**
123      * Resets all statistics back to their defaults.
124      */
125     public void reset() {
126         totalDurations.set(0);
127         longestDuration.set(0);
128         timeOfLongestDuration = 0;
129         shortestDuration.set(Long.MAX_VALUE);
130         timeOfShortestDuration = 0;
131         averageDuration.set(0.0);
132     }
133
134     /**
135      * Returns the average duration as a displayable String with units, e.g.
136      * "12.34 ms".
137      */
138     public String getDisplayableAverageDuration() {
139         return formatDuration(getAverageDuration(), 0);
140     }
141
142     /**
143      * Returns the shortest duration as a displayable String with units and the
144      * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
145      */
146     public String getDisplayableShortestDuration() {
147         return formatDuration(getShortestDuration(), getTimeOfShortestDuration());
148     }
149
150     /**
151      * Returns the longest duration as a displayable String with units and the
152      * date/time at which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24".
153      */
154     public String getDisplayableLongestDuration() {
155         return formatDuration(getLongestDuration(), getTimeOfLongestDuration());
156     }
157
158     /**
159      * Returns formatted value of number, e.g. "12.34". Always is used dot as
160      * decimal separator.
161      */
162     private static synchronized String formatDecimalValue(double value) {
163         return decimalFormat.format(value);
164     }
165
166     private String formatDuration(double duration, long timeStamp) {
167         TimeUnit unit = chooseUnit((long) duration);
168         double value = duration / NANOSECONDS.convert(1, unit);
169
170         return timeStamp > 0 ? String.format("%s %s at %3$tD %3$tT", formatDecimalValue(value), abbreviate(unit),
171                 new Date(timeStamp)) : String.format("%s %s", formatDecimalValue(value), abbreviate(unit));
172     }
173
174     private static TimeUnit chooseUnit(long nanos) {
175         if (SECONDS.convert(nanos, NANOSECONDS) > 0) {
176             return SECONDS;
177         }
178
179         if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) {
180             return MILLISECONDS;
181         }
182
183         if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) {
184             return MICROSECONDS;
185         }
186
187         return NANOSECONDS;
188     }
189
190     private static String abbreviate(TimeUnit unit) {
191         switch (unit) {
192         case NANOSECONDS:
193             return "ns";
194         case MICROSECONDS:
195             return "\u03bcs"; // μs
196         case MILLISECONDS:
197             return "ms";
198         case SECONDS:
199             return "s";
200         default:
201             return "";
202         }
203     }
204 }