basicstat.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview A basic statistics tracker.
  16. *
  17. */
  18. goog.provide('goog.stats.BasicStat');
  19. goog.require('goog.asserts');
  20. goog.require('goog.log');
  21. goog.require('goog.string.format');
  22. goog.require('goog.structs.CircularBuffer');
  23. /**
  24. * Tracks basic statistics over a specified time interval.
  25. *
  26. * Statistics are kept in a fixed number of slots, each representing
  27. * an equal portion of the time interval.
  28. *
  29. * Most methods optionally allow passing in the current time, so that
  30. * higher level stats can synchronize operations on multiple child
  31. * objects. Under normal usage, the default of goog.now() should be
  32. * sufficient.
  33. *
  34. * @param {number} interval The stat interval, in milliseconds.
  35. * @constructor
  36. * @final
  37. */
  38. goog.stats.BasicStat = function(interval) {
  39. goog.asserts.assert(interval > 50);
  40. /**
  41. * The time interval that this statistic aggregates over.
  42. * @type {number}
  43. * @private
  44. */
  45. this.interval_ = interval;
  46. /**
  47. * The number of milliseconds in each slot.
  48. * @type {number}
  49. * @private
  50. */
  51. this.slotInterval_ = Math.floor(interval / goog.stats.BasicStat.NUM_SLOTS_);
  52. /**
  53. * The array of slots.
  54. * @type {goog.structs.CircularBuffer}
  55. * @private
  56. */
  57. this.slots_ =
  58. new goog.structs.CircularBuffer(goog.stats.BasicStat.NUM_SLOTS_);
  59. };
  60. /**
  61. * The number of slots. This value limits the accuracy of the get()
  62. * method to (this.interval_ / NUM_SLOTS). A 1-minute statistic would
  63. * be accurate to within 2 seconds.
  64. * @type {number}
  65. * @private
  66. */
  67. goog.stats.BasicStat.NUM_SLOTS_ = 50;
  68. /**
  69. * @type {goog.log.Logger}
  70. * @private
  71. */
  72. goog.stats.BasicStat.prototype.logger_ =
  73. goog.log.getLogger('goog.stats.BasicStat');
  74. /**
  75. * @return {number} The interval which over statistics are being
  76. * accumulated, in milliseconds.
  77. */
  78. goog.stats.BasicStat.prototype.getInterval = function() {
  79. return this.interval_;
  80. };
  81. /**
  82. * Increments the count of this statistic by the specified amount.
  83. *
  84. * @param {number} amt The amount to increase the count by.
  85. * @param {number=} opt_now The time, in milliseconds, to be treated
  86. * as the "current" time. The current time must always be greater
  87. * than or equal to the last time recorded by this stat tracker.
  88. */
  89. goog.stats.BasicStat.prototype.incBy = function(amt, opt_now) {
  90. var now = opt_now ? opt_now : goog.now();
  91. this.checkForTimeTravel_(now);
  92. var slot = /** @type {goog.stats.BasicStat.Slot_} */ (this.slots_.getLast());
  93. if (!slot || now >= slot.end) {
  94. slot = new goog.stats.BasicStat.Slot_(this.getSlotBoundary_(now));
  95. this.slots_.add(slot);
  96. }
  97. slot.count += amt;
  98. slot.min = Math.min(amt, slot.min);
  99. slot.max = Math.max(amt, slot.max);
  100. };
  101. /**
  102. * Returns the count of the statistic over its configured time
  103. * interval.
  104. * @param {number=} opt_now The time, in milliseconds, to be treated
  105. * as the "current" time. The current time must always be greater
  106. * than or equal to the last time recorded by this stat tracker.
  107. * @return {number} The total count over the tracked interval.
  108. */
  109. goog.stats.BasicStat.prototype.get = function(opt_now) {
  110. return this.reduceSlots_(
  111. opt_now, function(sum, slot) { return sum + slot.count; }, 0);
  112. };
  113. /**
  114. * Returns the magnitute of the largest atomic increment that occurred
  115. * during the watched time interval.
  116. * @param {number=} opt_now The time, in milliseconds, to be treated
  117. * as the "current" time. The current time must always be greater
  118. * than or equal to the last time recorded by this stat tracker.
  119. * @return {number} The maximum count of this statistic.
  120. */
  121. goog.stats.BasicStat.prototype.getMax = function(opt_now) {
  122. return this.reduceSlots_(opt_now, function(max, slot) {
  123. return Math.max(max, slot.max);
  124. }, Number.MIN_VALUE);
  125. };
  126. /**
  127. * Returns the magnitute of the smallest atomic increment that
  128. * occurred during the watched time interval.
  129. * @param {number=} opt_now The time, in milliseconds, to be treated
  130. * as the "current" time. The current time must always be greater
  131. * than or equal to the last time recorded by this stat tracker.
  132. * @return {number} The minimum count of this statistic.
  133. */
  134. goog.stats.BasicStat.prototype.getMin = function(opt_now) {
  135. return this.reduceSlots_(opt_now, function(min, slot) {
  136. return Math.min(min, slot.min);
  137. }, Number.MAX_VALUE);
  138. };
  139. /**
  140. * Passes each active slot into a function and accumulates the result.
  141. *
  142. * @param {number|undefined} now The current time, in milliseconds.
  143. * @param {function(number, goog.stats.BasicStat.Slot_): number} func
  144. * The function to call for every active slot. This function
  145. * takes two arguments: the previous result and the new slot to
  146. * include in the reduction.
  147. * @param {number} val The initial value for the reduction.
  148. * @return {number} The result of the reduction.
  149. * @private
  150. */
  151. goog.stats.BasicStat.prototype.reduceSlots_ = function(now, func, val) {
  152. now = now || goog.now();
  153. this.checkForTimeTravel_(now);
  154. var rval = val;
  155. var start = this.getSlotBoundary_(now) - this.interval_;
  156. for (var i = this.slots_.getCount() - 1; i >= 0; --i) {
  157. var slot = /** @type {goog.stats.BasicStat.Slot_} */ (this.slots_.get(i));
  158. if (slot.end <= start) {
  159. break;
  160. }
  161. rval = func(rval, slot);
  162. }
  163. return rval;
  164. };
  165. /**
  166. * Computes the end time for the slot that should contain the count
  167. * around the given time. This method ensures that every bucket is
  168. * aligned on a "this.slotInterval_" millisecond boundary.
  169. * @param {number} time The time to compute a boundary for.
  170. * @return {number} The computed boundary.
  171. * @private
  172. */
  173. goog.stats.BasicStat.prototype.getSlotBoundary_ = function(time) {
  174. return this.slotInterval_ * (Math.floor(time / this.slotInterval_) + 1);
  175. };
  176. /**
  177. * Checks that time never goes backwards. If it does (for example,
  178. * the user changes their system clock), the object state is cleared.
  179. * @param {number} now The current time, in milliseconds.
  180. * @private
  181. */
  182. goog.stats.BasicStat.prototype.checkForTimeTravel_ = function(now) {
  183. var slot = /** @type {goog.stats.BasicStat.Slot_} */ (this.slots_.getLast());
  184. if (slot) {
  185. var slotStart = slot.end - this.slotInterval_;
  186. if (now < slotStart) {
  187. goog.log.warning(
  188. this.logger_,
  189. goog.string.format(
  190. 'Went backwards in time: now=%d, slotStart=%d. Resetting state.',
  191. now, slotStart));
  192. this.reset_();
  193. }
  194. }
  195. };
  196. /**
  197. * Clears any statistics tracked by this object, as though it were
  198. * freshly created.
  199. * @private
  200. */
  201. goog.stats.BasicStat.prototype.reset_ = function() {
  202. this.slots_.clear();
  203. };
  204. /**
  205. * A struct containing information for each sub-interval.
  206. * @param {number} end The end time for this slot, in milliseconds.
  207. * @constructor
  208. * @private
  209. */
  210. goog.stats.BasicStat.Slot_ = function(end) {
  211. /**
  212. * End time of this slot, exclusive.
  213. * @type {number}
  214. */
  215. this.end = end;
  216. };
  217. /**
  218. * Aggregated count within this slot.
  219. * @type {number}
  220. */
  221. goog.stats.BasicStat.Slot_.prototype.count = 0;
  222. /**
  223. * The smallest atomic increment of the count within this slot.
  224. * @type {number}
  225. */
  226. goog.stats.BasicStat.Slot_.prototype.min = Number.MAX_VALUE;
  227. /**
  228. * The largest atomic increment of the count within this slot.
  229. * @type {number}
  230. */
  231. goog.stats.BasicStat.Slot_.prototype.max = Number.MIN_VALUE;