Логирование сообщений консоли и стека вызова функций в JavaScript с последующей передачей на сервер для отладки

При отладке javascript в реальных условиях нужно отслеживать не только сам факт ошибки, но и понять цепочку событий, которая к ней привела. Для понимания проблемы нужно знать стек вызова функций и хорошо бы получить сообщения, которые выводились вашим скриптом в консоль. Ниже приведен мой код, используемый в сайтах, который позволяет решить эти задачи и оперативно решать возникающие проблемы на моих сайтах и сайтах клиентов:

<script>
    window.onerror = function (msg, url, line) {
        try {
            if (msg && url && typeof url != "undefined") if (url.indexOf(window.location.hostname) >= 0) {
                var data = new FormData();
                data.append('msg', msg);
                data.append('url', url);
                data.append('ref', document.location.href);
                data.append('line', line);
                //if(typeof console.trace ==="function") data.append('trace', console.trace());
                data.append('logs', console.logs.slice(-10).join("\n"));
                data.append('trace', getStackTrace().join('\n'));
                if (typeof obj !== "undefined") data.append('obj', JSON.stringify(obj));
                var xhr = new XMLHttpRequest();
                xhr.open('POST', '/jserr.php');
                xhr.send(data);
                console.log('Отправил ошибку: ', msg);
                return true;
            }
            console.log(msg, url, line);
        } catch (e) {
            console.error('Не отправил ошибку: ', e);
        }
        return true;
    };
    console.stdlog = console.log.bind(console);
    console.stdwarn = console.warn.bind(console);
    console.stderror = console.error.bind(console);
    console.logs = [];
    console.error = function () {
        console.logs.push('error:' + objToString(arguments));
        console.stderror.apply(console, arguments);
    }
    console.warn = function () {
        console.logs.push('warn:' + objToString(arguments));
        console.stdwarn.apply(console, arguments);
    }
    console.log = function () {
        console.logs.push(objToString(arguments));
        console.stdlog.apply(console, arguments);
    }

    function objToString(obj, ndeep) {
        if (obj == null) {
            return String(obj);
        }
        switch (typeof obj) {
            case "string":
                return '"' + obj + '"';
            case "function":
                return obj.name + '()' || obj.toString();
            case "object":
                if (ndeep > 3) {
                    return '...';
                } // исключаю бесконечную рекурсию
                if (//Array.isArray(obj) ||
                    obj.join !== undefined) {
                    return '[' + obj.join(',') + ']';
                } else {
                    return '{' + Object.keys(obj).map(function (key) {
                        if (key === 'self') return 'self:..';
                        else if (ndeep > 1 && (key === 'window' || key === 'document' || key === 'location' || key === 'top')) return key + ':..';
                        else return key + ': ' + objToString(obj[key], (ndeep || 1) + 1);
                    }).join(',') + '}';
                }
            default:
                return obj.toString();
        }
    }

    function getStackTrace() {
        let stack = new Error().stack || '';
        stack = stack.split('\n').map(function (line) {
            return line.trim();
        });
        return stack.splice(stack[0] == 'Error' ? 2 : 1);
    }
</script>
// setTimeout('console.warn("TEST");document.getElementsByTagName("INPUT")[0].value.aa;adlhds();',1000);

И функция получения и сохранения данных на PHP jserr.php:

< ?php
define("AdminMail","МЫЛО@АДМИНА");    // период кеширования сутки 24*60*60=86400

if (
    $_SERVER['REQUEST_METHOD'] !== 'POST' ||
    (
        empty($_SERVER['HTTP_REFERER']) ||
        strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) === false
    )
)    exit;


if (
    !isset($_POST['msg']) &&
    !isset($_POST['url']) &&
    !isset($_POST['line'])
)    exit;

$key = md5($_POST['url'] . ':' .  $_POST['line'].':'.$_POST['msg']);
$file = __DIR__ . '/log_error/js' . $key . '.log';

if(!is_file($file)){
    $msg = var_export($_POST,!0)./*"Message: ".$_POST['msg']."\n"."\n".
    "URL: ".$_POST['url']."\n".
    "Ref: ".$_POST['ref']."\n".
    "Line: ".$_POST['line']."\n".
        (empty($_POST['trace'])?'':"trace: ".$_POST['trace']."\n").
        (empty($_POST['trace'])?'':"trace: ".$_POST['trace']."\n").*/
        "\n".
        "Browser: ".$_SERVER['HTTP_USER_AGENT'];

    file_put_contents($file, $msg, LOCK_EX);

    mail(AdminMail, "Javascript error", $msg,
        "From: <noreply@".preg_replace("/www\./i","",$_SERVER['HTTP_HOST']).">\nContent-Type: text/plain; charset=utf-8");
}
?>

.