擴展Console的原由
目前網上還沒有類似的教程哦。
Console作爲日誌輸出調試的功能還是很強大的。可以打印正常的日誌信息,錯誤信息(log,info,warn,error)等等,還可以詳細詳細顯示函數調用棧,信息的輸出函數名以及代碼位置,功能可謂非常強大。
但是有個問題就是無法根據日誌的等級來自由控制輸出權限。比如我設置一個等級,只能輸出warn和error這樣。雖然有可能發佈的時候,會把Console基本有從代碼中去掉,但是調試的時候有時也需要調整等級了。比如想暫時把log給屏蔽掉,只輸出info或者warn以上信息。
最開始是想寫個Log日誌類來擴展Console的,增加控制日誌等級之類的功能。寫好之後,發現一個很鬱悶的問題,那就是自己的日誌輸出,遠遠沒有Console自帶的強大。因爲Console輸出信息的時候,可以把當前類和那一行代碼的信息給打印出來。這個很強大,對於調試非常有用,而自己寫的Log沒有這個功能。
用自己的Log進行擴展,則無法顯示出真實的代碼路徑,變成自己寫的Log路徑了。看下圖
當然這個輸出日誌路徑是方便本地調試看。如果需要把日誌錯誤等信息收集發給服務器,自定義擴展Log還是可行的。
針對這樣的情況,我對Console對象進行了擴展。增加了相應的功能。
Console增加等級控制和擴展分析
演示代碼爲TypeScript的代碼。後面會給出完整的TypeScript和JavaScript的代碼。
1日誌輸出等級控制
首先是準備增加等級控制,比如我可以調整日誌的輸出級別,來決定trace,log,info,error等輸出。原理其實很簡單,利用原型重寫對應的方法。根據等級,把小於日誌輸出等級的方法設置爲一個空方法,什麼也不做,這樣就無法輸出信息了。
對輸出方法進行分級別
/** 調試級別,trace函數路徑 **/
static TRACE:number = 0;
/** 普通日誌級別 **/
static LOG:number = 1;
/** 信息日誌級別 **/
static INFO:number = 2;
/** 警告日誌級別 **/
static WARN:number = 3;
/** 錯誤日誌級別 **/
static ERROR:number = 4;
/** 需要屏蔽覆蓋的方法名 **/
private static funNames:string[] = ["trace","log","info","warn","error"];
實現過程是這樣的:
先把console原來的trace,log等function給存放起來,當進行等級控制時,先把所有的方法都設置爲空方法,然後把大於等於日誌級別的方法給恢復過來。
//設置回最初的方法
for(var i:number = 0; i < this.funNames.length; i++)
{
//定義個空方法屏蔽掉
console[this.funNames[i]] = this.emptyFun;
}
//根據this.$level來還原輸出方法
for(var i:number = this.$level; i < this.funNames.length; i++)
{
//還原成最初的方法
console[this.funNames[i]] = this.funList[i];
}
原理就是這麼簡單,然後進行包裝的代碼就會稍微多一點。看輸出效果。
// //開啓擴展日誌
asf.ConsolePlus.init();
console.log("擴展日誌的正常模式");
console.log("log的信息");
console.info("info的信息");
console.warn("warn的信息");
// console.error("error的信息");
console.log("把日誌等級調整爲WANR");
//設置日誌等級,warn,就只有warn的信息才能輸出
asf.ConsolePlus.setLevel(asf.ConsolePlus.WARN);
console.log("log的信息");
console.info("info的信息");
console.warn("warn的信息");
console.error("error的信息");
輸出結果是這樣的
可以看到把日誌等級調整爲WARN之後,就無法看到log和info的信息了。
強化Console的輸出方法
這個實現其實主要是後期擴展,需要把日誌傳給服務器或者其他調試端的時候才需要的。主要原理其實是在原有的log,info等方法的後面增加自己的信息(當然也可以發給服務器)。類似java的AOP面向切面編程。
/**
* 創建擴展函數
* @param oldFun 原有的函數
* @param funName 函數名稱
*/
private static createPlusFun(oldFun:Function,funName:string):void
{
var fun:Function = function (message?: any, ...optionalParams: any[]):void
{
console.group("[" + funName + "][" + new Date() + "]");
oldFun.apply(console,arguments);
console.groupEnd();
}
this.funPulsList.push(fun);
}
asf.ConsolePlus.openConsolePlus();
console.log("log的信息");
console.info("info的信息");
console.warn("warn的信息");
前後增加了個組顯示的簡單擴展,看具體的輸出效果。
完整的TypeScritp和JavaScript代碼
TypeScritp代碼
/**
* Created by sodaChen on 2017/3/1.
*/
namespace asf
{
/**
* 輸出信息擴展類
* @author sodaChen
* Date:2017/2/15
*/
export class ConsolePlus
{
/////////////////日誌級別////////////////
/** 調試級別,trace函數路徑 **/
static TRACE:number = 0;
/** 普通日誌級別 **/
static LOG:number = 1;
/** 信息日誌級別 **/
static INFO:number = 2;
/** 警告日誌級別 **/
static WARN:number = 3;
/** 錯誤日誌級別 **/
static ERROR:number = 4;
/** 默認是最初的日誌級別,改變這個值。可以改變日誌輸出的結果 **/
private static $level:number = ConsolePlus.LOG;
/** 是否開啓擴展模式 **/
private static isPlusMode:boolean;
/** 需要屏蔽覆蓋的方法名 **/
private static funNames:string[] = ["trace","log","info","warn","error"];
/** console最初的方法緩存 **/
private static funList:Function[];
/** 擴展後的方法級別 **/
private static funPulsList:Function[];
/** 空方法 **/
private static emptyFun:Function;
static init(isPlusMode:boolean = false):void
{
this.funList = [];
this.funPulsList = [];
this.emptyFun = function(){};
//存放原來的方法
for(var i:number = 0; i < this.funNames.length; i++)
{
this.funList.push(console[this.funNames[i]]);
}
//生成新的擴展方法
for(var i:number = 0; i < this.funNames.length; i++)
{
this.createPlusFun(this.funList[i],this.funNames[i]);
}
this.setConsoleMode(isPlusMode);
}
/**
* 設置日誌等級,所有大於等於這個日誌級別的輸出都可以正常輸出,小於的則不能進行輸出
* @param level
*/
static setLevel(level:number):void
{
this.$level = level;
this.openConsole();
}
/**
* 設置輸出模式
* @param isPlusMode 是否爲擴展模式
*/
static setConsoleMode(isPlusMode:boolean):void
{
this.isPlusMode = isPlusMode;
if(isPlusMode)
this.openConsolePlus();
else
this.openConsole();
}
/**
* 打開日誌
*/
static openConsole():void
{
this.closeConsole();
//擴展默認,則調用擴展方法
if(this.isPlusMode)
{
this.openConsolePlus();
return ;
}
for(var i:number = this.$level; i < this.funNames.length; i++)
{
//還原成最初的方法
console[this.funNames[i]] = this.funList[i];
}
}
/**
* 關閉所有的日誌輸出
*/
static closeConsole():void
{
//設置回最初的方法
for(var i:number = 0; i < this.funNames.length; i++)
{
//定義個空方法屏蔽掉
console[this.funNames[i]] = this.emptyFun;
}
}
/**
* 開啓日誌輸出的擴展模式
*/
static openConsolePlus():void
{
this.isPlusMode = true;
//擴展console的所有方法
for(var i:number = this.$level; i < this.funNames.length; i++)
{
console[this.funNames[i]] = this.funPulsList[i];
}
}
/**
* 關閉日誌輸出的擴展模式
*/
static closeConsolePlus():void
{
this.isPlusMode = false;
//還原成原來的方法
this.openConsole();
}
/**
* 創建擴展函數
* @param oldFun 原有的函數
* @param funName 函數名稱
*/
private static createPlusFun(oldFun:Function,funName:string):void
{
var fun:Function = function (message?: any, ...optionalParams: any[]):void
{
console.group("[" + funName + "][" + new Date() + "]");
oldFun.apply(console,arguments);
console.groupEnd();
}
this.funPulsList.push(fun);
}
}
}
完整的調試信息代碼:
// //開啓擴展日誌
asf.ConsolePlus.init();
console.log("擴展日誌的正常模式");
console.log("log的信息");
console.info("info的信息");
console.warn("warn的信息");
// console.error("error的信息");
console.log("把日誌等級調整爲WANR");
//設置日誌等級,warn,就只有warn的信息才能輸出
asf.ConsolePlus.setLevel(asf.ConsolePlus.WARN);
console.log("log的信息");
console.info("info的信息");
console.warn("warn的信息");
console.error("error的信息");
//設置回正常的log級別
asf.ConsolePlus.setLevel(asf.ConsolePlus.LOG);
console.log("啓動Console的擴展模式");
asf.ConsolePlus.openConsolePlus();
console.log("log的信息");
console.info("info的信息");
console.warn("warn的信息");
console.error("error的信息");
//輸出擴展模式也支持日誌等級的
console.log("Console的擴展模式調整日誌級別到warn");
asf.ConsolePlus.setLevel(asf.ConsolePlus.WARN);
console.log("log的信息");
console.info("info的信息");
console.warn("warn的信息");
console.error("error的信息");
asf.ConsolePlus.closeConsolePlus();
console.log("1關閉Console的擴展模式,正常輸出啦");
//得調級別才能輸出log
asf.ConsolePlus.setLevel(asf.ConsolePlus.LOG);
console.log("2關閉Console的擴展模式,正常輸出啦");
JavaScript代碼
注意,這個js代碼是TypeScript生成的代碼,所以有部分會很奇怪。具體我沒單獨調試運行過,請注意。
var asf;
(function (asf) {
/**
* 輸出信息擴展類
* @author sodaChen
* Date:2017/2/15
*/
var ConsolePlus = (function () {
function ConsolePlus() {
}
ConsolePlus.init = function (isPlusMode) {
if (isPlusMode === void 0) { isPlusMode = false; }
this.funList = [];
this.funPulsList = [];
this.emptyFun = function () { };
//存放原來的方法
for (var i = 0; i < this.funNames.length; i++) {
this.funList.push(console[this.funNames[i]]);
}
//生成新的擴展方法
for (var i = 0; i < this.funNames.length; i++) {
this.createPlusFun(this.funList[i], this.funNames[i]);
}
this.setConsoleMode(isPlusMode);
};
/**
* 設置日誌等級,所有大於等於這個日誌級別的輸出都可以正常輸出,小於的則不能進行輸出
* @param level
*/
ConsolePlus.setLevel = function (level) {
this.$level = level;
this.openConsole();
};
/**
* 設置輸出模式
* @param isPlusMode 是否爲擴展模式
*/
ConsolePlus.setConsoleMode = function (isPlusMode) {
this.isPlusMode = isPlusMode;
if (isPlusMode)
this.openConsolePlus();
else
this.openConsole();
};
/**
* 打開日誌
*/
ConsolePlus.openConsole = function () {
this.closeConsole();
//擴展默認,則調用擴展方法
if (this.isPlusMode) {
this.openConsolePlus();
return;
}
for (var i = this.$level; i < this.funNames.length; i++) {
//還原成最初的方法
console[this.funNames[i]] = this.funList[i];
}
};
/**
* 關閉所有的日誌輸出
*/
ConsolePlus.closeConsole = function () {
//設置回最初的方法
for (var i = 0; i < this.funNames.length; i++) {
//定義個空方法屏蔽掉
console[this.funNames[i]] = this.emptyFun;
}
};
/**
* 開啓日誌輸出的擴展模式
*/
ConsolePlus.openConsolePlus = function () {
this.isPlusMode = true;
//擴展console的所有方法
for (var i = this.$level; i < this.funNames.length; i++) {
console[this.funNames[i]] = this.funPulsList[i];
}
};
/**
* 關閉日誌輸出的擴展模式
*/
ConsolePlus.closeConsolePlus = function () {
this.isPlusMode = false;
//還原成原來的方法
this.openConsole();
};
/**
* 創建擴展函數
* @param oldFun 原有的函數
* @param funName 函數名稱
*/
ConsolePlus.createPlusFun = function (oldFun, funName) {
var fun = function (message) {
var optionalParams = [];
for (var _i = 1; _i < arguments.length; _i++) {
optionalParams[_i - 1] = arguments[_i];
}
console.group("[" + funName + "][" + new Date() + "]");
oldFun.apply(console, arguments);
console.groupEnd();
};
this.funPulsList.push(fun);
};
/////////////////日誌級別////////////////
/** 調試級別,trace函數路徑 **/
ConsolePlus.TRACE = 0;
/** 普通日誌級別 **/
ConsolePlus.LOG = 1;
/** 信息日誌級別 **/
ConsolePlus.INFO = 2;
/** 警告日誌級別 **/
ConsolePlus.WARN = 3;
/** 錯誤日誌級別 **/
ConsolePlus.ERROR = 4;
/** 默認是最初的日誌級別,改變這個值。可以改變日誌輸出的結果 **/
ConsolePlus.$level = ConsolePlus.LOG;
/** 需要屏蔽覆蓋的方法名 **/
ConsolePlus.funNames = ["trace", "log", "info", "warn", "error"];
return ConsolePlus;
}());
asf.ConsolePlus = ConsolePlus;
})(asf || (asf = {}));
完整的調試代碼可以直接用上面那個ts的調試代碼,使用過程是完全一樣。(ts生成出來的那部分js也是一樣的,不帖了)