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 | var Time = require('./time').Time;
|
26 |
|
27 |
|
28 | var nt;
|
29 | var info;
|
30 | var state = {};
|
31 | var roots = [];
|
32 | var operations = [];
|
33 | var macroCallStack = {};
|
34 | var stackTraceCalls = 0;
|
35 | var operationFilter = /nodetime\.com/;
|
36 |
|
37 |
|
38 | exports.init = function() {
|
39 | nt = global.nodetime;
|
40 |
|
41 |
|
42 | nt.on('info', function(_info) {
|
43 | info = _info;
|
44 | });
|
45 |
|
46 | nt.on('call', function(point, time) {
|
47 | if(time.isMacro) macroCallStack[time.id] = time.begin;
|
48 | });
|
49 |
|
50 | nt.on('metric', function(metric) {
|
51 | if(!state[metric.scope]) state[metric.scope] = {};
|
52 | state[metric.scope][metric.name + (metric.unit ? ' (' + metric.unit + ')' : '')] = metric.value;
|
53 | });
|
54 |
|
55 |
|
56 |
|
57 | setInterval(function() {
|
58 | try {
|
59 |
|
60 | var now = nt.millis();
|
61 | for(var prop in macroCallStack) {
|
62 | if(macroCallStack[prop] + 60000 < now) {
|
63 | delete macroCallStack[prop];
|
64 | }
|
65 | }
|
66 |
|
67 | var firstCall = undefined;
|
68 | for(var prop in macroCallStack) {
|
69 | firstCall = macroCallStack[prop];
|
70 | break;
|
71 | }
|
72 |
|
73 | operations = operations.filter(function(s) {
|
74 | return (firstCall && s._begin >= firstCall);
|
75 | });
|
76 | }
|
77 | catch(e) {
|
78 | nt.error(e);
|
79 | }
|
80 | }, 10000);
|
81 |
|
82 |
|
83 | setInterval(function() {
|
84 | try {
|
85 | stackTraceCalls = 0;
|
86 | }
|
87 | catch(e) {
|
88 | nt.error(e);
|
89 | }
|
90 | }, 60000);
|
91 | }
|
92 |
|
93 | exports.time = function(scope, command, isMacro) {
|
94 | var t = new Time(scope, command, isMacro);
|
95 | t.start();
|
96 |
|
97 | return t;
|
98 | };
|
99 |
|
100 |
|
101 | exports.truncate = function(args) {
|
102 | if(!args) return undefined;
|
103 |
|
104 | if(typeof args === 'string') {
|
105 | return (args.length > 80 ? (args.substr(0, 80) + '...') : args);
|
106 | }
|
107 |
|
108 | if(!args.length) return undefined;
|
109 |
|
110 | var arr = [];
|
111 | var argsLen = (args.length > 10 ? 10 : args.length);
|
112 | for(var i = 0; i < argsLen; i++) {
|
113 | if(typeof args[i] === 'string') {
|
114 | if(args[i].length > 80) {
|
115 | arr.push(args[i].substr(0, 80) + '...');
|
116 | }
|
117 | else {
|
118 | arr.push(args[i]);
|
119 | }
|
120 | }
|
121 | else if(typeof args[i] === 'number') {
|
122 | arr.push(args[i]);
|
123 | }
|
124 | else if(args[i] === undefined) {
|
125 | arr.push('[undefined]');
|
126 | }
|
127 | else if(args[i] === null) {
|
128 | arr.push('[null]');
|
129 | }
|
130 | else if(typeof args[i] === 'object') {
|
131 | arr.push('[object]');
|
132 | }
|
133 | if(typeof args[i] === 'function') {
|
134 | arr.push('[function]');
|
135 | }
|
136 | }
|
137 |
|
138 | if(argsLen < args.length) arr.push('...');
|
139 |
|
140 | return arr;
|
141 | };
|
142 |
|
143 |
|
144 |
|
145 | exports.stackTrace = function() {
|
146 | if(nt.paused || this.stackTraceCalls++ > 1000) return undefined;
|
147 |
|
148 | var err = new Error();
|
149 | Error.captureStackTrace(err);
|
150 |
|
151 | if(err.stack) {
|
152 | var lines = err.stack.split("\n");
|
153 | lines.shift();
|
154 | lines = lines.filter(function(line) {
|
155 | return (!line.match(/nodetime/) || line.match(/nodetime\/test/));;
|
156 | });
|
157 |
|
158 | return lines;
|
159 | }
|
160 |
|
161 | return undefined;
|
162 | };
|
163 |
|
164 |
|
165 |
|
166 | exports.add = function(time, sample, label) {
|
167 | process.nextTick(function() {
|
168 | sample._version = nt.version;
|
169 | sample._ns = 'samples';
|
170 | sample._id = time.id;
|
171 | sample._isMacro = time.isMacro;
|
172 | sample._begin = time.begin;
|
173 | sample._end = time.end;
|
174 | sample._ms = time.ms;
|
175 | sample._ts = time.begin;
|
176 | sample._cputime = time.cputime
|
177 |
|
178 | if(label && label.length > 80) label = label.substring(0, 80) + '...';
|
179 | sample._label = label;
|
180 |
|
181 | sample['Response time (ms)'] = sample._ms;
|
182 | sample['Timestamp (ms)'] = sample._ts;
|
183 | if(sample._cputime !== undefined) sample['CPU time (ms)'] = sample._cputime;
|
184 |
|
185 |
|
186 | if(sample._isMacro) {
|
187 | sample['Operations'] = findOperations(sample);
|
188 | sample['Node state'] = state;
|
189 | sample['Node information'] = info;
|
190 |
|
191 | try {
|
192 | if(!nt.filterFunc || nt.filterFunc(sample)) {
|
193 | nt.emit('sample', sample);
|
194 | }
|
195 | }
|
196 | catch(err) {
|
197 | nt.error(err);
|
198 | }
|
199 |
|
200 | delete macroCallStack[sample._id];
|
201 | }
|
202 | else if(!sample['URL'] || !operationFilter.exec(sample['URL'])) {
|
203 | operations.push(sample);
|
204 |
|
205 | try {
|
206 | if(!nt.filterFunc || nt.filterFunc(sample)) {
|
207 | nt.emit('sample', sample);
|
208 | }
|
209 | }
|
210 | catch(err) {
|
211 | nt.error(err);
|
212 | }
|
213 | }
|
214 | });
|
215 | }
|
216 |
|
217 |
|
218 | var findOperations = function(sample) {
|
219 | var found = [];
|
220 |
|
221 | operations.forEach(function(s) {
|
222 | if(s._begin >= sample._begin && s._end <= sample._end)
|
223 | found.push(s);
|
224 | });
|
225 |
|
226 | found = found.sort(function(a, b) {
|
227 | return b._ms - a._ms;
|
228 | });
|
229 |
|
230 | return found.splice(0, 50);
|
231 | };
|
232 |
|
233 |
|
234 |
|