UNPKG

5.57 kBJavaScriptView Raw
1/*
2 * Copyright (c) 2012 Dmitri Melikyan
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to permit
9 * persons to whom the Software is furnished to do so, subject to the
10 * following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
18 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24
25var Time = require('./time').Time;
26
27
28var nt;
29var info;
30var state = {};
31var roots = [];
32var operations = [];
33var macroCallStack = {};
34var stackTraceCalls = 0;
35var operationFilter = /nodetime\.com/;
36
37
38exports.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 // cleanup operations
57 setInterval(function() {
58 try {
59 // expire root calls
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 // reset stack trace counter
83 setInterval(function() {
84 try {
85 stackTraceCalls = 0;
86 }
87 catch(e) {
88 nt.error(e);
89 }
90 }, 60000);
91}
92
93exports.time = function(scope, command, isMacro) {
94 var t = new Time(scope, command, isMacro);
95 t.start();
96
97 return t;
98};
99
100
101exports.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
145exports.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
166exports.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
218var 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