Знаю, знаю... да - написал велосипед
Вобщем вот:
JS часть
/**
* Модуль отправки Javascript ошибок на сервер
*
* @author Hapson <hapson5703@gmail.com>
* @copyright 2014 Hapson
* @version 1.0.0
*/
var ErrorHandler = (function(win){
var EH = {}, oldH;
var SG = {
url: undefined,
debug: true,
log: true
};
EH.setting = function(set){
/**
* Установка настроек и запуск модуля логирования ошибок
*
* @set {object} - массив настроек
* set.url {string} - URL, на который отсылать сообщения
* set.debug {boolean} - true: стандартный вывод ошибок
* false: попытка отключить стандартный вывод ошибок
* set.log {boolean} - true: отправлять ошибки на сервер
* false: не отправлять
*/
if(Object.prototype.toString.call(set) !== "[object Object]"){
throw new TypeError("No configuration or incorrect ErrorHandler");
}
oldH = win.onerror; win.onerror = handler;
SG.url = typeof set.url === "string" ? set.url : undefined;
SG.debug = !!set.debug ? true : false;
if(typeof win.opera !== "undefined"){SG.debug = !SG.debug;}
SG.log = !!set.log ? true : false;
};
EH.log = function(error){
/**
* Отправляет ошибку на сервер
*
* @error {string|object Error} - строка ошибки или объект класса Error
* для получения более информативного стека, рекомендуется передавать объект класса Error
*/
if(Object.prototype.toString.call(error) !== "[object Error]"){
try{throw new Error(""+ error);}catch(ex){error = ex;}
}
var ms, sk;
ms = error.message || "noMessage";
ms = (error.name || "noName") +": "+ ms +" at ";
ms += (error.fileName || win.location.href) +":";
ms += error.lineNumber || error.line || "noLine";
sk = error.stacktrace || error.stack || getStack(EH.log) || "noStack";
sendLog({type: "JS_EXCEPTION", message: ms, stack:sk});
};
function handler(message, file, line, col, error){
/**
* Обработчик ошибок
*
* @message {string} - сообщение об ошибке
* @file {string} - файл, в котором произошла ошибка
* @line {number} - номер строки
* @col {number} - номер символа
* @error {object Error} - объект класса Error
*/
if(SG.log && SG.url){
var res = prepareWinError(""+ message, file, line, col, error);
if(res){sendLog(res);}
}
if(oldH){return oldH;}
return SG.debug ? false : true;
}
function prepareWinError(message, file, line, col, error){
/**
* Форматирует ошибку
*
* @return {object Object}
*/
if(message == "" || (/script *error/i.test(message) && line == 0)){return false;}
col = col || "noColumn";
var ms = message +" at "+ file +":"+ line +":"+ col;
var sk = (error && (error.stacktrace || error.stack)) ? error.stacktrace || error.stack : getStack(ErrorHandler) || "noStack";
return {type: "JS_ERROR", "message": ms, "stack": sk};
}
function sendLog(arr){
/**
* Отправляет ошибку на сервер
* При наличии объекта XmlHttpRequest, отправляет ошибку методом POST
* В противном случае отправка производится методом GET
*
* @arr {object Object} - массив с полями type, message и stack
*/
var xhr = getXHR();
var param = "type="+ arr.type +"&"+
"message="+ encode(arr.message) +"&"+
"stack="+ encode(arr.stack) +"&"+
"platform="+ encode(navigator.platform) +"&"+
"url="+ encode(win.location.href);
if(!xhr){
var url = SG.url + (/\?/.test(SG.url) ? "&" : "?") + param;
try{new Image().src = url;}catch(ex){} return;
}
xhr.open("POST", SG.url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("HTTP_X_FORWARDED_FOR", "XmlHttpRequest");
xhr.send(param);
}
function getXHR(){
try{return new XMLHttpRequest();}catch(e){}
try{return new ActiveXObject("Msxml2.XMLHTTP.3.0");}catch(er){return false;}
}
function getStack(fn){
/**
* Формирует псевдо-стек. Работает преимущественно в IE
*/
if(!fn.caller){return false;}
var stack = [], name;
while(fn = fn.caller){
name = fn.toString().replace(/\{[\s\S]*\}/gm, '');
stack.push(name);
}
return stack.reverse().join(' -> ');
}
function encode(str){return encodeURIComponent(str);}
return EH;
}(window));
// настраиваем и включаем
ErrorHandler.setting({
url: window.location.protocol +"//"+ window.location.hostname +"/js_log_handler.php",
debug: false,
log: true
});