log.js 8.97 KB
define(['mUtil', 'ext/aop', 'mobilebone', "pageStack", 'zepto'], function (_u, aop, Mobilebone, stack) {
    var defs = {
        actionType: "/mobilemode/mobile/server.jsp?invoker=com.api.mobilemode.web.mobile.service.LogAction&action=",
        _OSType: _u.getOSType(),
        _ClientType: _u.getClientType()
    };
    var worker = null;
    var logger = {
        time: 0, // 用于统计页面停留时长
        cfg: {}, // 日志开关配置
        aynscUUIDs: {}, // 异步获取到的uuid  {pageid : Deferred对象}
        getUUID: function () { // 获取当前页面访问返回的uuid
            var pageid = _u.getCurrentPageId();

            return this.aynscUUIDs[pageid] || $.Deferred().resolve(-1);
        },
        action: function (actionName, data) {
            var settings = {}, deferred = $.Deferred();
            var _handle = function (res) {
                return deferred.resolve(res);
            };

            settings.url = _u.completeUrl(defs.actionType + actionName);
            settings.data = data;

            if (!window.Worker) {
                return $.ajax(settings).then(_handle);
            }

            if (!worker) {
                worker = new Worker("/mobilemode/mobile/dist/js/worker.js");
            }

            worker.onmessage = function (e) {
                _handle(e.data);
            };
            worker.postMessage({
                action: "ajax",
                args: settings,
            });

            return deferred;
        },
        // 写入日志
        write: function (actionName, data, preReq) {
            var deferred = $.Deferred();
            var _handle = function (res) {
                var data = JSON.parse(res).data;
                var uuid = data && data.uuid;

                if (uuid && uuid != -1) return deferred.resolve(uuid);
                return deferred.reject();
            };

            preReq = preReq || $.Deferred().resolve();// 前置请求
            data = data || {};

            data._OSType = defs._OSType;
            data._ClientType = defs._ClientType;

            preReq.then(function (uuid) {
                var isEmptyContent = data.hasOwnProperty("content") && !data.content;

                if (uuid) {
                    data.uuid = uuid;
                }

                // 对于未开启的日志类型和空content的情况 不做记录
                if (!logger.isEnabled(actionName) || isEmptyContent) return deferred.reject();

                return logger.action(actionName, data).then(_handle);
            });

            return deferred;
        },
        actions: {
            appAccess: null,
            pageAccess: function (pageIn, pageOut) {
                var now = new Date().getTime(), data = {};
                var pageInId = pageIn.id;
                // 页面切出时,才计算页面停留时长
                var pageid = pageOut && pageOut.id || pageInId || "";
                var _logPageAccess = function (data) {
                    return logger.write("logPageAccess", data, logger.actions.appAccess);
                };

                data.stay = now - (logger.time || now);
                logger.time = now;

                if (!pageid) return;

                data.pageid = _u.getPageIntId(pageid);
                logger.aynscUUIDs[pageid] = _logPageAccess(data);
                // 针对第一次进入(未在aynscUUIDs缓存)的页面,进行一次请求(stay为0)以获取uuid
                if (pageInId !== pageid && !logger.aynscUUIDs[pageInId]) {
                    logger.aynscUUIDs[pageInId] = _logPageAccess({ stay: 0, pageid: _u.getPageIntId(pageInId) });
                }
            },
            FunExec: function (f) {
                var preReq = logger.getUUID(), action;
                var data = {
                    content: f.arguments[0]
                };
                
                switch (f.fnName) {
                    case "SQL":
                        action = "logFunSQLExec"; break;
                    case "ajax":
                        action = "logFunInterfaceExec"; break;
                }
                
                var _chain = function (status) {
                    return function () {
                        data.status = status;
                        logger.write(action, data, preReq);
                    };
                };
                
                // 接口请求成功status为1 失败则为0
                var _result = aop.next(f); // 不破坏原函数的特性

                _result.then(_chain(1)).fail(_chain(0));

                return _result;
            },
            PlugInterfaceExec: function (f) {
                var preReq = logger.getUUID();
                var data = { content: f.arguments[0] };
                var _chain = function (status) {
                    return function () {
                        data.status = status;
                        logger.write("logPluginInterfaceExec", data, preReq);
                    };
                };

                aop.next(f).then(_chain(1)).fail(_chain(0));
            },
            PlugSQLExec: function (SQL, excuResult) {
                logger.write("logPluginSQLExec", { content: SQL, status: excuResult }, logger.getUUID());
            },
            log: function (type, content, status) {
                var preReq = logger.getUUID();
                var data = {}, actionName;

                switch (type) {
                    case "PLUGIN_INTERFACE_EXEC":
                        actionName = "logPluginInterfaceExec";
                    case "PLUGIN_SQL_EXEC":
                        actionName = actionName || "logPluginSQLExec";
                        data.content = content;
                        data.status = status;
                        logger.write(actionName, data, preReq);
                        break;
                }
            }
        },
        getConfig: function () {
            return this.action("getLogNodeConfig")
                .then(function (res) {
                    var res = JSON.parse(res);

                    logger.cfg = res.data;
                });
        },
        isEnabled: function (actionName) {
            var isEnabled = false, cfg = this.cfg;

            switch (actionName) {
                case "logAppAccess":
                    isEnabled = cfg.APP_ACCESS; break;
                case "logPageAccess":
                    isEnabled = cfg.PAGE_ACCESS; break;
                case "logFunSQLExec":
                    isEnabled = cfg.FUN_SQL_EXEC; break;
                case "logFunInterfaceExec":
                    isEnabled = cfg.FUN_INTERFACE_EXEC; break;
                case "logPluginInterfaceExec":
                    isEnabled = cfg.PLUGIN_INTERFACE_EXEC; break;
                case "logPluginSQLExec":
                    isEnabled = cfg.PLUGIN_SQL_EXEC; break;
            }
            return isEnabled;
        },
        init: function () {
            var isDisabledByUrl = ~location.search.indexOf("_logEnabled=false");

            if (isDisabledByUrl) return;

            var ats = this.actions;
            
            // 初始化 appAccess
            ats.appAccess = this.write("logAppAccess", { appid: _u.getAppid() }, this.getConfig());
            aop.after("callback", ats.pageAccess, Mobilebone);
            aop.around(/ajax|SQL/, ats.FunExec, Mobile_NS);
            aop.around("action", ats.PlugInterfaceExec, _u);
            // 初始化 mobilemode_util中的log方法 主要用于外部手动记录日志
            aop.after("log", ats.log, _u);
            // 针对页面关机行为 进行特殊处理
            window.onunload = beforeunloadlog; // 处理Emobile的关闭按钮和浏览器页面关闭的行为等
            window.doLeftButton = function () { // 处理Emobile中首页返回
                if (!stack.getPrevPage()) {
                    beforeunloadlog();
                }
                return "BACK";
            }
        }
    };

    var issend = false;

    function beforeunloadlog() {
        var data = {
            pageid: _u.getPageIntId(_u.getCurrentPageId()),
            stay: new Date().getTime() - logger.time,
            _OSType: defs._OSType,
            _ClientType: defs._ClientType
        }, _send = function (data) {
            if (!logger.isEnabled("logPageAccess")) return;

            var params = Object.keys(data).reduce(
                function (prev, key) {
                    return prev + key + "=" + data[key] + "&";
                },
                "").replace(/&$/, "");
            var url = _u.completeUrl(defs.actionType + "logPageAccess&" + params);

            if (navigator.sendBeacon) {
                return navigator.sendBeacon(url);
            }
            return $.ajax({ url: url, aynsc: true });
        };

        logger.actions.appAccess.then(function (uuid) {
            if (uuid === -1 || issend) return;

            data.uuid = uuid;
            issend = true;

            _send(data);
        });

        return "";
    }

    return {
        init: logger.init.bind(logger)
    };
});