This repository has been archived by the owner on Sep 29, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
jolokia-cubism.js
278 lines (250 loc) · 11.6 KB
/
jolokia-cubism.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
* Copyright 2012 Roland Huss
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Jolokia integration into cubism (http://square.github.com/cubism/)
*
* This integration requires the following
*/
(function () {
var builder = function (cubism,Jolokia) {
var VERSION = "1.7.1";
var ctx_jolokia = function (url, opts) {
var source = {},
context = this,
j4p = createAgent(url, opts),
step = 5e3; // 5 seconds by default
try
{
// Connect to start and stop events
context.on("start",function() {
j4p.start();
});
context.on("stop",function() {
j4p.stop();
});
}
catch(err)
{
// Still waiting for pull request https://github.com/square/cubism/pull/19 for supporting
// "start" and "stop" events
}
/**
* Factory method for create a metric objects which has various variants.
*
* If the first argument is a Jolokia request object (i.e. not a function), this request
* is used for sending requests periodically.
*
* If the first argument is a function, this function is used for calculating the numeric value
* to be plotted. The rest of the arguments can be one or more request objects, which are registered and their
* responses are put as arguments to the given callback function.
*
* The last argument, if an object but not a Jolokia request (i.e. there is no <code>type</code> key), is
* taken as an option object with the following possible keys:
*
* <ul>
* <li><b>name</b>: name used in charts</li>
* <li><b>delta</b>: delta value in milliseconds for creating delta (velocity) charts. This is done by
* taking the value measured that many milliseconds ago and subtract them from each other.</li>
* <li><b>keepDelay</b>: how many seconds back the fetched values should be kept.</li>
* </ul>
*
* Finally, if the last argument is a pure string, then this string is used as name for the chart.
*
* @return the metric objects which can be used in Cubism for creating charts.
*/
source.metric = function () {
var values = [];
// If the first argument is a function, this callback function is used for calculating the
// value of a data point. The remaining arguments should be one or more Jolokia requests objects
// and the callback function given will be called with as many response objects after each server query.
// The callback function needs to return a single calculated numerical value
var name;
var argsLen = arguments.length;
var options = {};
// Create metric upfront so that it can be used in extraction functions. The name defaults to the mbean name
// but can be given as first argument
var lastIdx = arguments.length - 1;
var lastArg = arguments[lastIdx];
if (typeof lastArg == "string") {
name = lastArg;
argsLen = lastIdx;
}
// Options can be given as an object (but not a request with a 'type' property)
if (typeof lastArg == "object" && !lastArg.type) {
options = lastArg;
name = options.name;
argsLen = lastIdx;
}
if (!name && typeof arguments[0] != "function") {
name = arguments[0].mbean;
}
// Metric which maps our previously locally stored values to the ones requested by cubism
var metric = context.metric(mapValuesFunc(values, options.keepDelay, context.width), name);
if (options.delta) {
// Use cubism metric chaining for calculating the difference value and keep care that the
// metric keeps old values up to the delta value
var prevMetric = metric.shift(-options.delta);
metric = metric.subtract(prevMetric);
if (name) {
metric.toString = function () {
return name
};
}
}
// If an extraction function is given, this can be used for fine grained manipulations of
// the answer
if (typeof arguments[0] == "function") {
var func = arguments[0];
var respFunc = function (resp) {
var isError = false;
for (var i = 0; i < arguments.length; i++) {
if (j4p.isError(arguments[i])) {
isError = true;
break;
}
}
values.unshift(
{ time:Date.now(), value:isError ? NaN : func.apply(metric, arguments) }
);
};
var args = [ respFunc ];
for (var i = 1; i < argsLen; i++) {
args.push(arguments[i]);
}
j4p.register.apply(j4p, args);
} else {
// Register the argument given directly as a Jolokia request. The request must return a single
// numerical value
var request = arguments[0];
j4p.register(function (resp) {
values.unshift({
time: Date.now(),
value:j4p.isError(resp) ? NaN : Number(resp.value)
});
}, request);
}
return metric;
};
// Start up fetching of values in the background
source.start = function (newStep) {
newStep = newStep || step;
j4p.start(newStep);
};
// Stop fetching of values in the background
source.stop = function() { j4p.stop() };
// Check whether the scheduler is running
source.isRunning = function() { return j4p.isRunning() };
// Startup poller which will call the agent periodically
return source;
// =======================================================================================
// Private helper method
// Create a new Jolokia agent or reuse a given one
function createAgent(url, opts) {
if (url instanceof Jolokia) {
return url;
} else {
var args;
if (typeof url == "string") {
args = {url:url};
if (opts) {
for (var key in opts) {
if (opts.hasOwnProperty(key)) {
args[key] = opts[key];
}
}
}
} else {
args = url;
}
return new Jolokia(args);
}
}
// Generate function which picks the requested values from the values
// stored periodically by the Jolokia poller.
function mapValuesFunc(values, keepDelay, width) {
return function (cStart, cStop, cStep, callback) {
cStart = +cStart;
cStop = +cStop;
var retVals = [],
cTime = cStop,
vLen = values.length,
vIdx = 0,
vStart = vLen > 0 ? values[vLen - 1].time : undefined;
if (!vLen || cStop < vStart) {
// Nothing fetched yet or seeked interval doesn't overlap with stored values --> return only NaNs
for (var t = cStart; t <= cStop; t += cStep) {
retVals.push(NaN);
}
return callback(null, retVals);
}
// Fill up wit NaN until we reach the first stored val
while (cTime > values[0].time + cStep) {
retVals.unshift(NaN);
cTime -= cStep;
}
while (cTime >= cStart && cTime >= vStart) {
// Count down stored values until we find the next best 'fit'
// (equals or closest before the step-calculated ime)
while (values[vIdx].time > cTime) {
vIdx++;
}
retVals.unshift(values[vIdx].value);
cTime -= cStep;
}
// Finally prepend with 'NaN' for data not yet fetched
while (cTime >= cStart) {
retVals.unshift(NaN);
cTime -= cStep;
}
// Remove older values
if (vLen > width) {
if (!keepDelay) {
values.length = width;
} else {
var keepUntil = values[width].time - keepDelay,
i = width;
while (i < vLen && values[i].time > keepUntil) {
i++;
}
values.length = i;
}
}
callback(null, retVals);
}
}
};
ctx_jolokia.VERSION = VERSION;
cubism.context.prototype.jolokia = ctx_jolokia;
return ctx_jolokia;
};
// =====================================================================================================
// Register either at the global Jolokia object global or as an AMD module
(function (root) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as a named module
define(["cubism","./jolokia"],function (cubism,Jolokia) {
return builder(cubism,Jolokia);
});
} else {
if (root.Jolokia && root.cubism) {
builder(root.cubism,root.Jolokia);
} else {
console.error("No " + (root.cubism ? "Cubism" : "Jolokia") + " definition found. " +
"Please include jolokia.js and cubism.js before jolokia-cubism.js");
}
}
})(this);
})();