1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18// Date: Wed Jul 29 23:25:43 CST 2015
19
20#ifndef BVAR_WINDOW_H
21#define BVAR_WINDOW_H
22
23#include <limits> // std::numeric_limits
24#include <math.h> // round
25#include <gflags/gflags_declare.h>
26#include "butil/logging.h" // LOG
27#include "bvar/detail/sampler.h"
28#include "bvar/detail/series.h"
29#include "bvar/variable.h"
30
31namespace bvar {
32
33DECLARE_int32(bvar_dump_interval);
34
35enum SeriesFrequency {
36 SERIES_IN_WINDOW = 0,
37 SERIES_IN_SECOND = 1
38};
39
40namespace detail {
41// Just for constructor reusing of Window<>
42template <typename R, SeriesFrequency series_freq>
43class WindowBase : public Variable {
44public:
45 typedef typename R::value_type value_type;
46 typedef typename R::sampler_type sampler_type;
47
48 class SeriesSampler : public detail::Sampler {
49 public:
50 struct Op {
51 explicit Op(R* var) : _var(var) {}
52 void operator()(value_type& v1, const value_type& v2) const {
53 _var->op()(v1, v2);
54 }
55 private:
56 R* _var;
57 };
58 SeriesSampler(WindowBase* owner, R* var)
59 : _owner(owner), _series(Op(var)) {}
60 ~SeriesSampler() {}
61 void take_sample() override {
62 if (series_freq == SERIES_IN_SECOND) {
63 // Get one-second window value for PerSecond<>, otherwise the
64 // "smoother" plot may hide peaks.
65 _series.append(_owner->get_value(1));
66 } else {
67 // Get the value inside the full window. "get_value(1)" is
68 // incorrect when users intend to see aggregated values of
69 // the full window in the plot.
70 _series.append(_owner->get_value());
71 }
72 }
73 void describe(std::ostream& os) { _series.describe(os, NULL); }
74 private:
75 WindowBase* _owner;
76 detail::Series<value_type, Op> _series;
77 };
78
79 WindowBase(R* var, time_t window_size)
80 : _var(var)
81 , _window_size(window_size > 0 ? window_size : FLAGS_bvar_dump_interval)
82 , _sampler(var->get_sampler())
83 , _series_sampler(NULL) {
84 CHECK_EQ(0, _sampler->set_window_size(_window_size));
85 }
86
87 ~WindowBase() {
88 hide();
89 if (_series_sampler) {
90 _series_sampler->destroy();
91 _series_sampler = NULL;
92 }
93 }
94
95 bool get_span(time_t window_size, detail::Sample<value_type>* result) const {
96 return _sampler->get_value(window_size, result);
97 }
98
99 bool get_span(detail::Sample<value_type>* result) const {
100 return get_span(_window_size, result);
101 }
102
103 virtual value_type get_value(time_t window_size) const {
104 detail::Sample<value_type> tmp;
105 if (get_span(window_size, &tmp)) {
106 return tmp.data;
107 }
108 return value_type();
109 }
110
111 value_type get_value() const { return get_value(_window_size); }
112
113 void describe(std::ostream& os, bool quote_string) const override {
114 if (butil::is_same<value_type, std::string>::value && quote_string) {
115 os << '"' << get_value() << '"';
116 } else {
117 os << get_value();
118 }
119 }
120
121#ifdef BAIDU_INTERNAL
122 void get_value(boost::any* value) const override { *value = get_value(); }
123#endif
124
125 time_t window_size() const { return _window_size; }
126
127 int describe_series(std::ostream& os, const SeriesOptions& options) const override {
128 if (_series_sampler == NULL) {
129 return 1;
130 }
131 if (!options.test_only) {
132 _series_sampler->describe(os);
133 }
134 return 0;
135 }
136
137 void get_samples(std::vector<value_type> *samples) const {
138 samples->clear();
139 samples->reserve(_window_size);
140 return _sampler->get_samples(samples, _window_size);
141 }
142
143protected:
144 int expose_impl(const butil::StringPiece& prefix,
145 const butil::StringPiece& name,
146 DisplayFilter display_filter) override {
147 const int rc = Variable::expose_impl(prefix, name, display_filter);
148 if (rc == 0 &&
149 _series_sampler == NULL &&
150 FLAGS_save_series) {
151 _series_sampler = new SeriesSampler(this, _var);
152 _series_sampler->schedule();
153 }
154 return rc;
155 }
156
157 R* _var;
158 time_t _window_size;
159 sampler_type* _sampler;
160 SeriesSampler* _series_sampler;
161};
162
163} // namespace detail
164
165// Get data within a time window.
166// The time unit is 1 second fixed.
167// Window relies on other bvar which should be constructed before this window
168// and destructs after this window.
169
170// R must:
171// - have get_sampler() (not require thread-safe)
172// - defined value_type and sampler_type
173template <typename R, SeriesFrequency series_freq = SERIES_IN_WINDOW>
174class Window : public detail::WindowBase<R, series_freq> {
175 typedef detail::WindowBase<R, series_freq> Base;
176 typedef typename R::value_type value_type;
177 typedef typename R::sampler_type sampler_type;
178public:
179 // Different from PerSecond, we require window_size here because get_value
180 // of Window is largely affected by window_size while PerSecond is not.
181 Window(R* var, time_t window_size) : Base(var, window_size) {}
182 Window(const butil::StringPiece& name, R* var, time_t window_size)
183 : Base(var, window_size) {
184 this->expose(name);
185 }
186 Window(const butil::StringPiece& prefix,
187 const butil::StringPiece& name, R* var, time_t window_size)
188 : Base(var, window_size) {
189 this->expose_as(prefix, name);
190 }
191};
192
193// Get data per second within a time window.
194// The only difference between PerSecond and Window is that PerSecond divides
195// the data by time duration.
196template <typename R>
197class PerSecond : public detail::WindowBase<R, SERIES_IN_SECOND> {
198 typedef detail::WindowBase<R, SERIES_IN_SECOND> Base;
199 typedef typename R::value_type value_type;
200 typedef typename R::sampler_type sampler_type;
201public:
202 // If window_size is non-positive or absent, use FLAGS_bvar_dump_interval.
203 PerSecond(R* var) : Base(var, -1) {}
204 PerSecond(R* var, time_t window_size) : Base(var, window_size) {}
205 PerSecond(const butil::StringPiece& name, R* var) : Base(var, -1) {
206 this->expose(name);
207 }
208 PerSecond(const butil::StringPiece& name, R* var, time_t window_size)
209 : Base(var, window_size) {
210 this->expose(name);
211 }
212 PerSecond(const butil::StringPiece& prefix,
213 const butil::StringPiece& name, R* var)
214 : Base(var, -1) {
215 this->expose_as(prefix, name);
216 }
217 PerSecond(const butil::StringPiece& prefix,
218 const butil::StringPiece& name, R* var, time_t window_size)
219 : Base(var, window_size) {
220 this->expose_as(prefix, name);
221 }
222
223 value_type get_value(time_t window_size) const override {
224 detail::Sample<value_type> s;
225 this->get_span(window_size, &s);
226 // We may test if the multiplication overflows and use integral ops
227 // if possible. However signed/unsigned 32-bit/64-bit make the solution
228 // complex. Since this function is not called often, we use floating
229 // point for simplicity.
230 if (s.time_us <= 0) {
231 return static_cast<value_type>(0);
232 }
233 if (butil::is_floating_point<value_type>::value) {
234 return static_cast<value_type>(s.data * 1000000.0 / s.time_us);
235 } else {
236 return static_cast<value_type>(round(s.data * 1000000.0 / s.time_us));
237 }
238 }
239};
240
241} // namespace bvar
242
243#endif //BVAR_WINDOW_H
244