diff --git a/.travis.yml b/.travis.yml index f76b5e9..deee765 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,17 @@ language: node_js node_js: - - 0.8 + - "0.10" install: - - npm install mocha-browser nico + - npm install spm@ninja coveralls mocha before_script: - - git clone git://github.com/aralejs/nico-arale.git _theme - - node_modules/.bin/nico build --theme _theme -C _theme/nico.js + - node_modules/spm/bin/spm-install + - npm install script: - - node_modules/.bin/mocha-browser _site/tests/runner.html -S + - node_modules/spm/bin/spm-test after_success: - - npm install jscoverage coveralls - - node_modules/.bin/jscoverage --encoding=utf8 src _site/src-cov - - node_modules/.bin/mocha-browser _site/tests/runner.html?cov -S -R lcov | node_modules/.bin/coveralls + - node_modules/spm/bin/spm-test --coveralls | node_modules/.bin/coveralls diff --git a/Makefile b/Makefile index 25f43ff..c00fd45 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,10 @@ -THEME = $(HOME)/.spm/themes/arale +version = $(shell cat package.json | grep version | awk -F'"' '{print $$4}') PROJ_ROOT=$(CURDIR) +install: + @spm install + build: @spm build @googlecc ${PROJ_ROOT}/src/seer-monitor.js ${PROJ_ROOT}/dist/seer-monitor.js @@ -13,27 +16,19 @@ build: @cat ${PROJ_ROOT}/src/seer-jsniffer.js >> ${PROJ_ROOT}/dist/seer-debug.js build-doc: - @nico build -v -C $(THEME)/nico.js - -debug: - @nico server -C $(THEME)/nico.js --watch debug + @spm doc build publish: - @spm publish -s alipay - -server: - @nico server -C $(THEME)/nico.js + @spm publish + @spm doc publish + @git tag $(version) + @git push origin $(version) watch: - @nico server -C $(THEME)/nico.js --watch + @spm doc watch publish-doc: clean build-doc - @rm -fr _site/sea-modules - @spm publish --doc _site -s alipay - -publish-pages: clean build-doc - @ghp-import _site - @git push origin gh-pages + @spm doc publish clean: @rm -fr _site @@ -42,7 +37,7 @@ clean: reporter = spec url = tests/runner.html test: - @mocha-phantomjs --reporter=${reporter} http://127.0.0.1:8000/${url} + @spm test coverage: @rm -fr _site/src-cov diff --git a/README.md b/README.md index 3b1b9e2..c2c421a 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -# monitor + +# monitor: 通用前端监控采集脚本。 --- +[![spm package](http://spmjs.io/badge/zupu)](http://spmjs.io/package/zupu) [![Build Status](https://secure.travis-ci.org/totorojs/monitor.png?branch=master)](https://travis-ci.org/totorojs/monitor) [![Coverage Status](https://coveralls.io/repos/totorojs/monitor/badge.png?branch=master)](https://coveralls.io/r/totorojs/monitor) -通用前端监控采集脚本。 为了和整体的 totoro 质量体系保持一致,特为前端监控脚本取小名为 `chibi`, 龙猫中最小的那只龙猫(chibi totoro)。 diff --git a/monitor.js b/monitor.js new file mode 100644 index 0000000..e41fd87 --- /dev/null +++ b/monitor.js @@ -0,0 +1,211 @@ + +var win = window; +var doc = win.document; +var loc = win.location; +var M = win.monitor; + +var detector = require("detector"); +var Events = require("arale-events"); + +// 避免未引用先行脚本抛出异常。 +if(!M){M = {};} +if(!M._DATAS){M._DATAS = [];} +if(!M._EVENTS){M._EVENTS = [];} + +var _events = M._EVENTS; +var _evt = new Events(); +M.on = function(evt, handler){ + _evt.on(evt, handler); +}; +for(var i=0,l=_events.length; i https://static.alipay.com/ar??arale.js,a.js,b.js +// 的方式请求资源,需要特殊处理。 +// +// @param {String} uri, 仅处理绝对路径。 +// @return {String} 返回 uri 的文件路径,不包含参数和 jsessionid。 +function path(uri){ + if(undefined === uri || typeof(uri) !== "string"){return "";} + var len = uri.length; + + var idxSessionID = uri.indexOf(";jsessionid="); + if(idxSessionID < 0){idxSessionID = len;} + + // 旧版的合并 HTTP 服务。 + var idxMin = uri.indexOf("/min/?"); + if(idxMin >= 0){ + idxMin = uri.indexOf("?", idxMin); + } + if(idxMin < 0){idxMin = len;} + + var idxHash = uri.indexOf("#"); + if(idxHash < 0){idxHash = len;} + + var idxQ = uri.indexOf("??"); + idxQ = uri.indexOf("?", idxQ < 0 ? 0 : idxQ+2); + if(idxQ < 0){idxQ = len;} + + var idx = Math.min(idxSessionID, idxMin, idxHash, idxQ); + + return idx < 0 ? uri : uri.substr(0, idx); +} + +// 必要的字符串转义,保证发送的数据是安全的。 +// @param {String} str. +// @return {String} +function escapeString(str){ + return String(str).replace(/(?:\r\n|\r|\n)/g,""); +} + +// 将对象转为键值对参数字符串。 +function param(obj){ + if(Object.prototype.toString.call(obj) !== "[object Object]"){ + return ""; + } + var p = []; + for(var k in obj){ + if(!has(obj,k)){continue;} + if(typeOf(obj[k]) === "[object Array]"){ + for(var i=0,l=obj[k].length; i URLLength){return callback();} + + // @see http://www.javascriptkit.com/jsref/image.shtml + var img = new Image(1,1); + img.onload = img.onerror = img.onabort = function(){ + callback(); + img.onload = img.onerror = img.onabort = null; + img = null; + }; + + img.src = url; +} + +var sending = false; +/** + * 分时发送队列中的数据,避免 IE(6) 的连接请求数限制。 + */ +function timedSend(){ + if(sending){return;} + + var e = M._DATAS.shift(); + if(!e){return;} + sending = true; + + // 理论上应该在收集异常消息时修正 file,避免连接带有参数。 + // 但是收集部分在 seer 中,不适合放置大量的脚本。 + if(e.profile === "jserror"){ + e.file = path(e.file); + } + + var data = merge(DEFAULT_DATA, e); + data.rnd = rand(); // 避免缓存。 + + // 触发事件返回 false 时,取消后续执行。 + // 要求特定 profile 的事件,和全局事件都被触发。 + var eventResult = _evt.trigger(e.profile, data); + eventResult = _evt.trigger("*", data) && eventResult; + if(!eventResult){ + sending = false; + return timedSend(); + } + + send(LOG_SERVER, data, function(){ + sending = false; + timedSend(); + }); +} + +// timedSend 准备好后可以替换 push 方法,自动分时发送。 +var _push = M._DATAS.push; +M._DATAS.push = function(){ + _push.apply(M._DATAS, arguments); + timedSend(); +}; + +// 主动发送已捕获的异常。 +timedSend(); + +win.monitor = M; +module.exports = M; diff --git a/package.json b/package.json index b59b67c..8a2ac04 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { - "family": "alipay", "name": "monitor", - "version": "2.3.1", + "version": "2.4.0", "description": "通用前端监控脚本", "homepage": "https://github.com/totorojs/monitor", "author": "闲耘 ", @@ -16,13 +15,16 @@ "keywords": ["utility"], "licenses": "MIT", "scripts": { - "prepublish": "make build" }, "spm": { - "output": ["monitor.js"], - "alias": { - "detector": "arale/detector/1.2.1/detector", - "events": "arale/events/1.1.0/events" - } + "main": "monitor.js", + "dependencies": { + "detector": "2.0.1", + "arale-events": "1.2.0" + }, + "devDependencies": { + "expect.js": "0.3.1" + }, + "tests": "tests/*-spec.js" } } diff --git a/src/seer-jsniffer.js b/seer-jsniffer.js similarity index 100% rename from src/seer-jsniffer.js rename to seer-jsniffer.js diff --git a/src/seer-monitor.js b/seer-monitor.js similarity index 100% rename from src/seer-monitor.js rename to seer-monitor.js diff --git a/src/monitor.js b/src/monitor.js deleted file mode 100644 index 7a56663..0000000 --- a/src/monitor.js +++ /dev/null @@ -1,214 +0,0 @@ - -define(function(require, exports, module) { - - var win = window; - var doc = win.document; - var loc = win.location; - var M = win.monitor; - - var detector = require("detector"); - var Events = require("events"); - - // 避免未引用先行脚本抛出异常。 - if(!M){M = {};} - if(!M._DATAS){M._DATAS = [];} - if(!M._EVENTS){M._EVENTS = [];} - - var _events = M._EVENTS; - var _evt = new Events(); - M.on = function(evt, handler){ - _evt.on(evt, handler); - }; - for(var i=0,l=_events.length; i https://static.alipay.com/ar??arale.js,a.js,b.js - // 的方式请求资源,需要特殊处理。 - // - // @param {String} uri, 仅处理绝对路径。 - // @return {String} 返回 uri 的文件路径,不包含参数和 jsessionid。 - function path(uri){ - if(undefined === uri || typeof(uri) !== "string"){return "";} - var len = uri.length; - - var idxSessionID = uri.indexOf(";jsessionid="); - if(idxSessionID < 0){idxSessionID = len;} - - // 旧版的合并 HTTP 服务。 - var idxMin = uri.indexOf("/min/?"); - if(idxMin >= 0){ - idxMin = uri.indexOf("?", idxMin); - } - if(idxMin < 0){idxMin = len;} - - var idxHash = uri.indexOf("#"); - if(idxHash < 0){idxHash = len;} - - var idxQ = uri.indexOf("??"); - idxQ = uri.indexOf("?", idxQ < 0 ? 0 : idxQ+2); - if(idxQ < 0){idxQ = len;} - - var idx = Math.min(idxSessionID, idxMin, idxHash, idxQ); - - return idx < 0 ? uri : uri.substr(0, idx); - } - - // 必要的字符串转义,保证发送的数据是安全的。 - // @param {String} str. - // @return {String} - function escapeString(str){ - return String(str).replace(/(?:\r\n|\r|\n)/g,""); - } - - // 将对象转为键值对参数字符串。 - function param(obj){ - if(Object.prototype.toString.call(obj) !== "[object Object]"){ - return ""; - } - var p = []; - for(var k in obj){ - if(!has(obj,k)){continue;} - if(typeOf(obj[k]) === "[object Array]"){ - for(var i=0,l=obj[k].length; i URLLength){return callback();} - - // @see http://www.javascriptkit.com/jsref/image.shtml - var img = new Image(1,1); - img.onload = img.onerror = img.onabort = function(){ - callback(); - img.onload = img.onerror = img.onabort = null; - img = null; - }; - - img.src = url; - } - - var sending = false; - /** - * 分时发送队列中的数据,避免 IE(6) 的连接请求数限制。 - */ - function timedSend(){ - if(sending){return;} - - var e = M._DATAS.shift(); - if(!e){return;} - sending = true; - - // 理论上应该在收集异常消息时修正 file,避免连接带有参数。 - // 但是收集部分在 seer 中,不适合放置大量的脚本。 - if(e.profile === "jserror"){ - e.file = path(e.file); - } - - var data = merge(DEFAULT_DATA, e); - data.rnd = rand(); // 避免缓存。 - - // 触发事件返回 false 时,取消后续执行。 - // 要求特定 profile 的事件,和全局事件都被触发。 - var eventResult = _evt.trigger(e.profile, data); - eventResult = _evt.trigger("*", data) && eventResult; - if(!eventResult){ - sending = false; - return timedSend(); - } - - send(LOG_SERVER, data, function(){ - sending = false; - timedSend(); - }); - } - - // timedSend 准备好后可以替换 push 方法,自动分时发送。 - var _push = M._DATAS.push; - M._DATAS.push = function(){ - _push.apply(M._DATAS, arguments); - timedSend(); - }; - - // 主动发送已捕获的异常。 - timedSend(); - - win.monitor = M; - module.exports = M; -}); diff --git a/tests/monitor-spec.js b/tests/monitor-spec.js index fd65bb9..2bbe5e5 100644 --- a/tests/monitor-spec.js +++ b/tests/monitor-spec.js @@ -1,162 +1,159 @@ -define(function(require) { - require("../src/seer-monitor"); +require("../seer-monitor"); - var monitor = require("../src/monitor"); - var expect = require("expect"); - var util = require("./unitutil"); +var monitor = require("../monitor"); +var expect = require("expect.js"); +var util = require("./unitutil"); - describe("monitor", function() { +describe("monitor", function() { - it("monitor.log(seed)", function() { - expect(util.equals(monitor.log("seed"), { - profile: "log", - seed: "seed" - })).to.equal(true); - }); + it("monitor.log(seed)", function() { + expect(util.equals(monitor.log("seed"), { + profile: "log", + seed: "seed" + })).to.equal(true); + }); - it("monitor.log(seed, profile)", function() { - expect(util.equals(monitor.log("seed", "profile"), { - profile: "profile", - seed: "seed" - })).to.equal(true); - }); + it("monitor.log(seed, profile)", function() { + expect(util.equals(monitor.log("seed", "profile"), { + profile: "profile", + seed: "seed" + })).to.equal(true); + }); - it("monitor.log(object)", function() { - expect(util.equals(monitor.log({a:1, b:true}), { - profile: "log", - a: 1, - b: true - })).to.equal(true); - }); + it("monitor.log(object)", function() { + expect(util.equals(monitor.log({a:1, b:true}), { + profile: "log", + a: 1, + b: true + })).to.equal(true); + }); - // FIXME - //it("monitor.log(object, profile)", function() { - //console.log(monitor.log({profile:"b"},"a")) - //expect(util.equals(monitor.log({a:1, b:true}, "profile"), { - //profile: "profile", - //a: 1, - //b: true - //})).to.equal(true); - //}); + // FIXME + //it("monitor.log(object, profile)", function() { + //console.log(monitor.log({profile:"b"},"a")) + //expect(util.equals(monitor.log({a:1, b:true}, "profile"), { + //profile: "profile", + //a: 1, + //b: true + //})).to.equal(true); + //}); - after(test_monitor_error) + after(test_monitor_error) - }); +}); - function test_monitor_error(){ +function test_monitor_error(){ - require("../src/seer-jsniffer"); + require("../seer-jsniffer"); - describe("monitor: jsniffer", function() { + describe("monitor: jsniffer", function() { - it("monitor.error()", function() { - var ex = monitor.error(new Error("error message.")); + it("monitor.error()", function() { + var ex = monitor.error(new Error("error message.")); - expect(ex.msg).to.equal("error message."); - expect(ex.profile).to.equal("jserror"); - expect(ex.hasOwnProperty("file")).to.equal(true); - expect(ex.hasOwnProperty("line")).to.equal(true); - expect(ex.hasOwnProperty("col")).to.equal(true); - expect(ex.hasOwnProperty("num")).to.equal(true); - expect(ex.hasOwnProperty("type")).to.equal(true); - expect(ex.hasOwnProperty("stack")).to.equal(true); - expect(ex.hasOwnProperty("lost")).to.equal(true); - expect(ex.hasOwnProperty("lang")).to.equal(true); - expect(ex.uv).to.equal(1); - }); + expect(ex.msg).to.equal("error message."); + expect(ex.profile).to.equal("jserror"); + expect(ex.hasOwnProperty("file")).to.equal(true); + expect(ex.hasOwnProperty("line")).to.equal(true); + expect(ex.hasOwnProperty("col")).to.equal(true); + expect(ex.hasOwnProperty("num")).to.equal(true); + expect(ex.hasOwnProperty("type")).to.equal(true); + expect(ex.hasOwnProperty("stack")).to.equal(true); + expect(ex.hasOwnProperty("lost")).to.equal(true); + expect(ex.hasOwnProperty("lang")).to.equal(true); + expect(ex.uv).to.equal(1); + }); - it("monitor.error() repeat, without uv.", function() { - var ex1 = monitor.error(new Error("error message.")); - - expect(ex1.msg).to.equal("error message."); - expect(ex1.profile).to.equal("jserror"); - expect(ex1.hasOwnProperty("file")).to.equal(true); - expect(ex1.hasOwnProperty("line")).to.equal(true); - expect(ex1.hasOwnProperty("col")).to.equal(true); - expect(ex1.hasOwnProperty("num")).to.equal(true); - expect(ex1.hasOwnProperty("type")).to.equal(true); - expect(ex1.hasOwnProperty("stack")).to.equal(true); - expect(ex1.hasOwnProperty("lost")).to.equal(true); - expect(ex1.hasOwnProperty("lang")).to.equal(true); - expect(ex1.hasOwnProperty("uv")).to.equal(false); - }); + it("monitor.error() repeat, without uv.", function() { + var ex1 = monitor.error(new Error("error message.")); + + expect(ex1.msg).to.equal("error message."); + expect(ex1.profile).to.equal("jserror"); + expect(ex1.hasOwnProperty("file")).to.equal(true); + expect(ex1.hasOwnProperty("line")).to.equal(true); + expect(ex1.hasOwnProperty("col")).to.equal(true); + expect(ex1.hasOwnProperty("num")).to.equal(true); + expect(ex1.hasOwnProperty("type")).to.equal(true); + expect(ex1.hasOwnProperty("stack")).to.equal(true); + expect(ex1.hasOwnProperty("lost")).to.equal(true); + expect(ex1.hasOwnProperty("lang")).to.equal(true); + expect(ex1.hasOwnProperty("uv")).to.equal(false); + }); - it("try/catch: monitor.error()", function() { - try{ - throw new Error("error message ii."); - }catch(ex){ - var ex2 = monitor.error(ex); - - expect(ex2.msg).to.equal("error message ii."); - expect(ex2.profile).to.equal("jserror"); - expect(ex2.hasOwnProperty("file")).to.equal(true); - expect(ex2.hasOwnProperty("line")).to.equal(true); - expect(ex2.hasOwnProperty("col")).to.equal(true); - expect(ex2.hasOwnProperty("num")).to.equal(true); - expect(ex2.hasOwnProperty("type")).to.equal(true); - expect(ex2.hasOwnProperty("stack")).to.equal(true); - expect(ex2.hasOwnProperty("lost")).to.equal(true); - expect(ex2.hasOwnProperty("lang")).to.equal(true); - expect(ex2.uv).to.equal(1); - } - }); + it("try/catch: monitor.error()", function() { + try{ + throw new Error("error message ii."); + }catch(ex){ + var ex2 = monitor.error(ex); + + expect(ex2.msg).to.equal("error message ii."); + expect(ex2.profile).to.equal("jserror"); + expect(ex2.hasOwnProperty("file")).to.equal(true); + expect(ex2.hasOwnProperty("line")).to.equal(true); + expect(ex2.hasOwnProperty("col")).to.equal(true); + expect(ex2.hasOwnProperty("num")).to.equal(true); + expect(ex2.hasOwnProperty("type")).to.equal(true); + expect(ex2.hasOwnProperty("stack")).to.equal(true); + expect(ex2.hasOwnProperty("lost")).to.equal(true); + expect(ex2.hasOwnProperty("lang")).to.equal(true); + expect(ex2.uv).to.equal(1); + } + }); - it("try/catch: monitor.error() repeat, without uv.", function() { - try{ - throw new Error("error message ii."); - }catch(ex){ - var ex3 = monitor.error(ex); - - expect(ex3.msg).to.equal("error message ii."); - expect(ex3.profile).to.equal("jserror"); - expect(ex3.hasOwnProperty("file")).to.equal(true); - expect(ex3.hasOwnProperty("line")).to.equal(true); - expect(ex3.hasOwnProperty("col")).to.equal(true); - expect(ex3.hasOwnProperty("num")).to.equal(true); - expect(ex3.hasOwnProperty("type")).to.equal(true); - expect(ex3.hasOwnProperty("stack")).to.equal(true); - expect(ex3.hasOwnProperty("lost")).to.equal(true); - expect(ex3.hasOwnProperty("lang")).to.equal(true); - expect(ex3.hasOwnProperty("uv")).to.equal(false); - } - }); + it("try/catch: monitor.error() repeat, without uv.", function() { + try{ + throw new Error("error message ii."); + }catch(ex){ + var ex3 = monitor.error(ex); + + expect(ex3.msg).to.equal("error message ii."); + expect(ex3.profile).to.equal("jserror"); + expect(ex3.hasOwnProperty("file")).to.equal(true); + expect(ex3.hasOwnProperty("line")).to.equal(true); + expect(ex3.hasOwnProperty("col")).to.equal(true); + expect(ex3.hasOwnProperty("num")).to.equal(true); + expect(ex3.hasOwnProperty("type")).to.equal(true); + expect(ex3.hasOwnProperty("stack")).to.equal(true); + expect(ex3.hasOwnProperty("lost")).to.equal(true); + expect(ex3.hasOwnProperty("lang")).to.equal(true); + expect(ex3.hasOwnProperty("uv")).to.equal(false); + } + }); - after(test_monitor_on); + after(test_monitor_on); - }); - } + }); +} - function test_monitor_on(){ - describe("monitor.on", function(){ +function test_monitor_on(){ + describe("monitor.on", function(){ - function funcError(meta){ - expect(meta.profile).to.equals("jserror"); - expect(meta.msg).to.equals("test error message."); - } + function funcError(meta){ + expect(meta.profile).to.equals("jserror"); + expect(meta.msg).to.equals("test error message."); + } - monitor.on("log", function(meta){ - it("monitor.on('log')", function() { - expect("test").to.equal(meta.seed); - }); + monitor.on("log", function(meta){ + it("monitor.on('log')", function() { + expect("test").to.equal(meta.seed); }); - monitor.log("test"); + }); + monitor.log("test"); - it("monitor.on('jserror')", function() { - monitor.on("jserror", function(meta){ - expect(meta.profile).to.equal("jserror"); - expect("test error message.").to.equal(meta.msg); - }); + it("monitor.on('jserror')", function() { + monitor.on("jserror", function(meta){ + expect(meta.profile).to.equal("jserror"); + expect("test error message.").to.equal(meta.msg); }); - try{ - throw new Error("test error message."); - }catch(ex){ - monitor.error(ex); - } }); - } - -}); + try{ + throw new Error("test error message."); + }catch(ex){ + monitor.error(ex); + } + }); +}