2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_SERVICE_DATA_H_
18 #define incl_HPHP_SERVICE_DATA_H_
26 #include <folly/RWSpinLock.h>
27 #include <folly/Synchronized.h>
28 #include <folly/stats/Histogram.h>
29 #include <folly/stats/MultiLevelTimeSeries.h>
30 #include <folly/Optional.h>
32 #include "hphp/util/assertions.h"
36 ///////////////////////////////////////////////////////////////////////////////
39 * A globally accessible statistics tracking facility. This can be used to keep
40 * track of internal runtime statistics in the form of flat counters, timeseries
41 * counters or histograms.
43 * ServiceData provides a globally accessible entry point to all the internal
44 * statistics. A 'statistic counter' of different types could be created by
45 * calling createCouter() createTimeSeries() or createHistogram(). The caller
46 * can then add values at different time points to the statistic counters. The
47 * statistic can then be retrieved and reported via the exportAll() call on
52 * All functions in ServiceData namespace are thread safe. It is safe
53 * (and recommended) to cache the object returned by create...() methods and
54 * repeatedly add data points to it. It is safe to call create...() with the
55 * same name from multiple threads. In this case, only one object will be
56 * created and passed back to different threads.
58 * All objects returned by returned by the various create...() calls are thread
59 * safe. It is okay to add data points to it from multiple threads concurrently.
60 * These objects are internally synchronized with spin locks.
64 * // create a flat counter named foo.
65 * auto counter = ServiceData::createCounter("foo");
66 * counter->increment();
68 * // create timeseries data named bar with default setting (avg value for the
69 * // last 1 minute, 10 minute, hour and all time).
70 * auto timeseries = ServiceData::createTimeSeries("bar");
71 * timeseries->addValue(3);
73 * // create a histogram with 10 buckets, min of 1, max of 100 and export the
74 * // 50th and 90th percentile value for reporting.
75 * auto histogram = ServiceData::createHistogram("blah", 10, 1, 100,
77 * histogram->addValue(10);
79 * // You can report the data like so.
80 * std::map<std::string, int64_t> statsMap;
81 * ServiceData::exportAll(statsMap);
83 * and statsMap will contain these keys:
86 * "bar.avg.60", "bar.avg.600", "bar.avg.3600", "bar.avg"
87 * "blah.hist.p50", "blah.hist.p90"
91 * ServiceData::createCounter("foo")->increment(); // don't do this.
92 * Don't do this in performance critical code. You will incur the cost of a
93 * std::map look up whenever createCounter() is called. Rather, you should call
94 * ServiceData::createCounter("foo") just once, cache the returned pointer and
95 * repeatedly adding data points to it.
97 namespace ServiceData
{
99 struct ExportedCounter
;
100 struct ExportedHistogram
;
101 struct ExportedTimeSeries
;
104 template <class ClassWithPrivateDestructor
>
105 struct FriendDeleter
;
108 enum class StatsType
{ AVG
, SUM
, RATE
, COUNT
, PCT
};
111 * Create a flat counter named 'name'. Return an existing counter if it has
112 * already been created.
114 ExportedCounter
* createCounter(const std::string
& name
);
117 * Callback-based counters
119 * Some counters have values that are updated much more frequently than data is
120 * requested from ServiceData, or there may not be a good location in the code
121 * to periodically update the value. For these cases, a callback-based counter
122 * may be used instead.
124 * registerCounterCallback() returns a unique handle that may be passed to
125 * deregisterCounterCallback() if the callback should be deregistered.
127 * The CounterCallback struct is provided as a convenience wrapper to manage
128 * the handle. The callback may be provided to its constructor, or to the
129 * init() function, if the callback isn't safe to call at CounterCallback's
130 * construction. Similarly, deinit() may be be called if the callback should be
131 * deregistered before CounterCallback's destruction.
133 * Once registered, callbacks must be safe to call at any time, from any
134 * thread, and may even be called before registerCounterCallback()
135 * returns. Callbacks are passed a reference to the std::map<std::string,
136 * int64_t> being populated, so one callback can add many counters (callbacks
137 * can also remove or modify existing counters, but this is discouraged).
139 using CounterFunc
= std::function
<void(std::map
<std::string
, int64_t>&)>;
140 using CounterHandle
= uint32_t;
142 CounterHandle
registerCounterCallback(CounterFunc func
);
143 void deregisterCounterCallback(CounterHandle key
);
145 struct CounterCallback
{
146 CounterCallback() = default;
148 explicit CounterCallback(CounterFunc func
) {
149 init(std::move(func
));
156 void init(CounterFunc func
) {
158 m_key
= registerCounterCallback(std::move(func
));
163 deregisterCounterCallback(*m_key
);
169 folly::Optional
<CounterHandle
> m_key
;
173 * Create a timeseries counter named 'name'. Return an existing one if it
174 * has already been created.
176 * Timeseries data is implemented as a number of buckets (buckted by time).
177 * As data point is added and time rolls forward, new bucket is created and
178 * the earliest bucket expires.
180 * We keep multiple of timeseries data at different granularity and update
181 * them simultaneously. This allows us to commute statistics at different
182 * levels. For example, we can simultaneously compute the avg of some counter
183 * value over the last 5 minutes, 10 minutes and hour. This is a similar
184 * concept to the load counters from the unix 'uptime' command.
186 * 'exportTypes' specifies what kind of statistics to export for each level.
187 * More export types can be added after the timeseries is created.
189 * 'levels' specifies at which granularity should the stats be tracked. The
190 * time duration must be strictly increasing. Special value '0' means all
191 * time and should always come last.
193 * 'numBuckets' specifies how many buckets to keep at each level. More buckets
194 * will produce more precise data at the expense of memory.
196 ExportedTimeSeries
* createTimeSeries(
197 const std::string
& name
,
198 const std::vector
<StatsType
>& exportTypes
=
199 std::vector
<StatsType
>{ StatsType::AVG
},
200 const std::vector
<std::chrono::seconds
>& levels
=
201 std::vector
<std::chrono::seconds
>{
202 std::chrono::seconds(60),
203 std::chrono::seconds(600),
204 std::chrono::seconds(3600),
205 std::chrono::seconds(0) /* all time */ },
206 int numBuckets
= 60);
209 * Create a histogram counter named 'name'. Return an existing one if it has
210 * already been created.
212 * 'bucketSize' specifies how many buckets to track for the histogram.
213 * 'min' is the minimal value in the histogram.
214 * 'max' is the maximal value in the histogram.
215 * 'exportPercentile' specifies at what percentile values we should report the
216 * stat. A set of doubles between 0 and 1.0. For example, 0.5 means p50 and
219 ExportedHistogram
* createHistogram(
220 const std::string
& name
,
224 const std::vector
<double>& exportPercentile
);
227 * Export all the statistics as simple key, value pairs.
229 void exportAll(std::map
<std::string
, int64_t>& statsMap
);
232 * Export a specific counter by key name.
234 folly::Optional
<int64_t> exportCounterByKey(const std::string
& key
);
236 // Interface for a flat counter. All methods are thread safe.
237 struct ExportedCounter
{
238 ExportedCounter() : m_value(0) {}
239 void increment() { m_value
.fetch_add(1, std::memory_order_relaxed
); }
240 void decrement() { m_value
.fetch_sub(1, std::memory_order_relaxed
); }
241 void addValue(int64_t value
) {
242 m_value
.fetch_add(value
, std::memory_order_relaxed
);
244 void setValue(int64_t value
) {
245 m_value
.store(value
, std::memory_order_relaxed
);
247 int64_t getValue() const { return m_value
.load(std::memory_order_relaxed
); }
250 friend struct detail::FriendDeleter
<ExportedCounter
>;
251 ~ExportedCounter() {}
253 std::atomic_int_fast64_t m_value
;
256 // Interface for timeseries data. All methods are thread safe.
257 struct ExportedTimeSeries
{
258 ExportedTimeSeries(int numBuckets
,
259 const std::vector
<std::chrono::seconds
>& durations
,
260 const std::vector
<StatsType
>& exportTypes
);
262 void addValue(int64_t value
);
263 void addValue(int64_t value
, int64_t times
);
264 void addValueAggregated(int64_t sum
, int64_t nsamples
);
267 int64_t getRateByDuration(std::chrono::seconds duration
);
269 folly::Optional
<int64_t> getCounter(StatsType type
, int seconds
);
271 void exportAll(const std::string
& prefix
,
272 std::map
<std::string
, int64_t>& statsMap
);
275 friend struct detail::FriendDeleter
<ExportedTimeSeries
>;
276 ~ExportedTimeSeries() {}
278 folly::Synchronized
<folly::MultiLevelTimeSeries
<int64_t>,
279 folly::RWSpinLock
> m_timeseries
;
280 const std::vector
<ServiceData::StatsType
> m_exportTypes
;
283 // Interface for histogram data. All methods are thread safe.
284 struct ExportedHistogram
{
285 ExportedHistogram(int64_t bucketSize
, int64_t min
, int64_t max
,
286 const std::vector
<double>& exportPercentiles
);
287 void addValue(int64_t value
);
288 void removeValue(int64_t value
);
289 void exportAll(const std::string
& prefix
,
290 std::map
<std::string
, int64_t>& statsMap
);
293 friend struct detail::FriendDeleter
<ExportedHistogram
>;
294 ~ExportedHistogram() {}
296 folly::Synchronized
<folly::Histogram
<int64_t>, folly::RWSpinLock
> m_histogram
;
297 const std::vector
<double> m_exportPercentiles
;
300 }; // namespace ServiceData
302 ///////////////////////////////////////////////////////////////////////////////
305 #include "hphp/util/service-data-inl.h"
307 #endif // incl_HPHP_SERVICE_DATA_H_