-
Notifications
You must be signed in to change notification settings - Fork 35
/
seer.js
140 lines (128 loc) · 3.7 KB
/
seer.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
(function(global) {
if(global.monitor){return;}
var M = global.monitor = {};
M._DATAS = [];
var lost_resources = [];
var _lost_resources = {};
/**
* 客户端资源加载失败时调用这个接口。
*/
M.lost = function(uri){
if(_lost_resources.hasOwnProperty(uri)){return;}
_lost_resources[uri] = true;
lost_resources.push(uri);
};
var EVENTS = M._EVENTS = [];
M.on = function(evt, handler){
EVENTS.push([evt, handler]);
};
M.off = function(){};
var DEFAULT_PROFILE = "log";
/**
* 通用监控接口。
* @param {String} seed, 监控点。
* @param {String} profile, 监控类型,默认为 `log`。
* @return {Object}
*/
M.log = function(seed, profile){
if(!seed){return;}
// 老版产品监控。
if(arguments.length >= 3){return;}
var data;
if(Object.prototype.toString.call(seed) === "[object Object]"){
data = seed;
data.profile = seed.profile || DEFAULT_PROFILE;
}else{
data = {
profile: profile || DEFAULT_PROFILE,
seed: seed
};
}
M._DATAS.push(data);
return data;
};
var RE_FUNCTION = /^function\b[^\)]+\)/;
/**
* 获得函数名。
* @param {Function} func, 函数对象。
* @return {String} 函数名。
*/
function function_name(func){
var match = String(func).match(RE_FUNCTION);
return match ? match[0] : "";
}
/**
* 函数调用堆栈。
* XXX: 匿名函数的支持。
* @param {Function} call, function's caller.
* @return {String} stack trace.
*/
function stacktrace(call){
var stack = [];
while(call.arguments && call.arguments.callee && call.arguments.callee.caller){
call = call.arguments.callee.caller;
stack.push("at " + function_name(call));
// Because of a bug in Navigator 4.0, we need this line to break.
// c.caller will equal a rather than null when we reach the end
// of the stack. The following line works around this.
if (call.caller === call){break;}
}
return stack.join("\n");
}
var ERROR_CACHE = {};
/**
* JavaScript 异常统一处理函数。
* @param {String} message, 异常消息。
* @param {String} file, 异常所在文件。
* @param {Number} line, 异常所在行。
* @param {Number,String} number, 异常编码,IE 支持。
* @return {Object} 主要用于单元测试,本身可以不返回。
*/
function error(message, file, line, number, stack){
if(!stack && arguments.callee.caller){
stack = stacktrace(arguments.callee.caller);
}
var data = {
profile: "jserror",
msg: message || "",
file: file || "",
line: line || 0,
num: number || "",
stack: stack || "",
lost: lost_resources.join(",")
};
var key = file+":"+line+":"+message;
if(!ERROR_CACHE.hasOwnProperty(key)){
data.uv = 1;
ERROR_CACHE[key] = true;
}
M._DATAS.push(data);
return data;
}
/**
* JavaScript 异常接口,用于监控 `try/catch` 中被捕获的异常。
* @param {Error} err, JavaScript 异常对象。
* @return {Object} 主要用于单元测试。
*/
M.error = function(ex){
if (!(ex instanceof Error)) {return;}
var stack = ex.stack || ex.stacktrace;
return error(
ex.message || ex.description,
ex.fileName,
ex.lineNumber || ex.line,
ex.number,
stack
);
};
/**
* 全局 JavaScript 异常监控。
* @return {Boolean} 返回 `true` 则控制台捕获异常。
* 返回 `false` 则控制台不捕获异常。
* 建议返回 `false`。
*/
global.onerror = function(message, file, line) {
error(message, file, line);
return false;
};
})(this);