1 | (function (window) {
|
2 | 'use strict';
|
3 |
|
4 | |
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | var defaultConfig = {
|
21 | dryrun: false,
|
22 | debug: false,
|
23 | app: undefined,
|
24 | namespace: 'web',
|
25 | tags: {},
|
26 | aggregations: [],
|
27 | aggregationFrequency: 10,
|
28 | timer: {
|
29 | tags: {
|
30 | unit: 'ms'
|
31 | },
|
32 | aggregations: ['avg', 'p90', 'count']
|
33 | },
|
34 | counter: {
|
35 | tags: {},
|
36 | aggregations: ['sum', 'count']
|
37 | },
|
38 | gauge: {
|
39 | tags: {},
|
40 | aggregations: ['last']
|
41 | },
|
42 | timeout: 2000,
|
43 | flushInterval: 10000,
|
44 | sampleRate: 100
|
45 | };
|
46 |
|
47 | var logger;
|
48 |
|
49 | var statful = {
|
50 | config: {
|
51 | apiAddress: 'https://beacon.statful.com'
|
52 | },
|
53 | endpoints: {
|
54 | metrics: 'beacon/metrics'
|
55 | },
|
56 | perf: window.performance,
|
57 |
|
58 | |
59 |
|
60 |
|
61 | initialize: function (clientConfig) {
|
62 | var self = this;
|
63 |
|
64 |
|
65 | self.mergeConfigs = function (clientConfig) {
|
66 | if (typeof clientConfig !== 'object' || clientConfig === null) {
|
67 | clientConfig = {};
|
68 | }
|
69 |
|
70 |
|
71 | Object.keys(defaultConfig).forEach(function (key) {
|
72 | self.config[key] = defaultConfig[key];
|
73 | });
|
74 |
|
75 | Object.keys(clientConfig).forEach(function (key) {
|
76 | self.config[key] = clientConfig[key];
|
77 | });
|
78 | };
|
79 |
|
80 | self.metricsData = function (name, type, value, tags, aggregations, aggregationFrequency, namespace, sampleRate) {
|
81 | return {
|
82 | name: name,
|
83 | type: type,
|
84 | value: value,
|
85 | tags: self.util.setTags(tags || {}, self.config.tags, self.config[type].tags, self.config.app),
|
86 | aggregations: self.util.setAggregations(aggregations, self.config.aggregations, self.config[type].aggregations),
|
87 | aggregationFrequency: self.util.setAggregationFrequency(aggregationFrequency, self.config.aggregationFrequency, self.config[type].aggregationFrequency),
|
88 | namespace: namespace || self.config.namespace,
|
89 | sampleRate: sampleRate || self.config.sampleRate
|
90 | };
|
91 | };
|
92 |
|
93 | this.mergeConfigs(clientConfig);
|
94 |
|
95 |
|
96 | logger = new StatfulLogger(self.config.debug);
|
97 |
|
98 |
|
99 | self.util = new StatfulUtil({
|
100 | apiAddress: this.config.apiAddress,
|
101 | debug: this.config.debug,
|
102 | dryrun: this.config.dryrun,
|
103 | flushInterval: this.config.flushInterval,
|
104 | timeout: this.config.timeout
|
105 | });
|
106 |
|
107 |
|
108 | self.util.registerQueue('metrics', this.endpoints.metrics, this.config.flushInterval);
|
109 | },
|
110 |
|
111 | |
112 |
|
113 |
|
114 |
|
115 |
|
116 | measureTimeUserTiming: function (measureName) {
|
117 | var time;
|
118 | var measure = statful.perf.getEntriesByName(measureName).filter(function filterMeasures(entry) {
|
119 | return entry.entryType === 'measure';
|
120 | });
|
121 |
|
122 | if (measure.length > 0) {
|
123 |
|
124 | time = measure[measure.length - 1].duration;
|
125 | } else {
|
126 | logger.debug('Measure ' + measureName + ' not found');
|
127 | }
|
128 |
|
129 | return time;
|
130 | },
|
131 |
|
132 | |
133 |
|
134 |
|
135 |
|
136 | clearMarks: function (marks) {
|
137 | try {
|
138 | if (marks) {
|
139 | marks.forEach(function (mark) {
|
140 | if (mark) {
|
141 | statful.perf.clearMarks(mark);
|
142 | }
|
143 | });
|
144 | } else {
|
145 | statful.perf.clearMarks();
|
146 | }
|
147 | } catch (ex) {
|
148 | logger.error(ex);
|
149 | }
|
150 | },
|
151 |
|
152 | |
153 |
|
154 |
|
155 | clearResources: function () {
|
156 | try {
|
157 | statful.perf.clearResourceTimings();
|
158 | } catch (ex) {
|
159 | logger.error(ex);
|
160 | }
|
161 | },
|
162 |
|
163 | |
164 |
|
165 |
|
166 |
|
167 | clearMeasures: function (measures) {
|
168 | try {
|
169 | if (measures) {
|
170 | measures.forEach(function (measure) {
|
171 | statful.perf.clearMeasures(measure);
|
172 | });
|
173 | } else {
|
174 | statful.perf.clearMeasures();
|
175 | }
|
176 | } catch (ex) {
|
177 | logger.error(ex);
|
178 | }
|
179 | },
|
180 |
|
181 | |
182 |
|
183 |
|
184 |
|
185 | registerMark: function (markName) {
|
186 | try {
|
187 | logger.debug('Register Mark', markName);
|
188 | if (markName) {
|
189 | statful.perf.mark(markName);
|
190 | } else {
|
191 | logger.error('Undefined resource name to register as a mark');
|
192 | }
|
193 | } catch (ex) {
|
194 | logger.error(ex);
|
195 | }
|
196 | },
|
197 |
|
198 | |
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 | registerMeasure: function (measureName, metricName, options) {
|
205 | try {
|
206 | logger.debug('Register Measure', measureName, metricName, options);
|
207 | if (measureName) {
|
208 | var defaults = {
|
209 | clearMarks: false,
|
210 | clearMeasures: false
|
211 | };
|
212 |
|
213 | options = options || {};
|
214 |
|
215 |
|
216 | Object.keys(options).forEach(function (key) {
|
217 | defaults[key] = options[key];
|
218 | });
|
219 |
|
220 |
|
221 | if (!defaults.endMark) {
|
222 | this.registerMark(measureName);
|
223 | defaults.endMark = measureName;
|
224 | }
|
225 |
|
226 | statful.perf.measure(measureName, defaults.startMark, defaults.endMark);
|
227 |
|
228 |
|
229 | var time = this.measureTimeUserTiming(measureName);
|
230 |
|
231 | if (time) {
|
232 |
|
233 | this.util.addItemToQueue('metrics', new this.metricsData(metricName, 'timer', time,
|
234 | defaults.tags, defaults.aggregations, defaults.aggregationFrequency, defaults.namespace, defaults.sampleRate));
|
235 | } else {
|
236 | logger.error('Failed to get measure time to register as timer value');
|
237 | }
|
238 |
|
239 | if (defaults.clearMarks) {
|
240 | this.clearMarks([defaults.startMark, defaults.endMark]);
|
241 | }
|
242 |
|
243 | if (defaults.clearMeasures) {
|
244 | this.clearMeasures([measureName]);
|
245 | }
|
246 | } else {
|
247 | logger.error('Undefined resource name to register as a measure');
|
248 | }
|
249 | } catch (ex) {
|
250 | logger.error(ex);
|
251 | }
|
252 | },
|
253 |
|
254 | |
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 | timer: function (metricName, metricValue, options) {
|
261 | try {
|
262 | logger.debug('Register Timer', metricName, metricValue, options);
|
263 | if (metricName && metricValue >= 0) {
|
264 | options = options || {};
|
265 |
|
266 |
|
267 | var item = new this.metricsData(metricName, 'timer', metricValue, options.tags, options.agg, options.aggFreq, options.namespace, options.sampleRate);
|
268 | this.util.addItemToQueue('metrics', item);
|
269 | } else {
|
270 | logger.error('Undefined metric name or invalid value to register as a timer');
|
271 | }
|
272 | } catch (ex) {
|
273 | logger.error(ex);
|
274 | }
|
275 | },
|
276 |
|
277 | |
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | counter: function (metricName, metricValue, options) {
|
284 | try {
|
285 | logger.debug('Register Counter', metricName, options);
|
286 | metricValue = metricValue || 1;
|
287 |
|
288 | if (metricName) {
|
289 | options = options || {};
|
290 |
|
291 |
|
292 | var item = new this.metricsData(metricName, 'counter', metricValue, options.tags, options.agg, options.aggFreq, options.namespace, options.sampleRate);
|
293 | this.util.addItemToQueue('metrics', item);
|
294 | } else {
|
295 | logger.error('Undefined metric name to register as a counter');
|
296 | }
|
297 | } catch (ex) {
|
298 | logger.error(ex);
|
299 | }
|
300 | },
|
301 |
|
302 | |
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 | gauge: function (metricName, metricValue, options) {
|
309 | try {
|
310 | logger.debug('Register Gauge', metricName, metricValue, options);
|
311 | if (metricName && metricValue) {
|
312 | options = options || {};
|
313 |
|
314 |
|
315 | var item = new this.metricsData(metricName, 'gauge', metricValue, options.tags, options.agg, options.aggFreq, options.namespace, options.sampleRate);
|
316 | this.util.addItemToQueue('metrics', item);
|
317 | } else {
|
318 | logger.error('Undefined metric name/value to register as a gauge');
|
319 | }
|
320 | } catch (ex) {
|
321 | logger.error(ex);
|
322 | }
|
323 | }
|
324 | };
|
325 |
|
326 | window.statful = statful;
|
327 |
|
328 | })(window);
|