前言
CocosCreator项目中总会遇到一些问题,有时候就需要一个比较好用的Log工具来记录,遇到问题时把这些Log发到服务器或者复制到哪里,可以比较方便的分析问题。
使用方法
设置Log等级
//设置打印的log等级
Logger.SetLevel(LogLv.DEBUG);
打印Log
//打印log,带脚本名称
Logger.debug('游戏开始','GameManager')
//打印log
Logger.debug('游戏开始')
Log追踪
将debug
方法中的注释打开即可
//下面两行打开
let stackFunc = (new Error).stack.split("\n")[2].split(" ")[5];
let logMsg = tag == "" ? "[DEBUG] " + msg + " at " + stackFunc : "[DEBUG] [" + tag + "] " + msg;
//将这一句注释
//let logMsg = tag == "" ? "[DEBUG] " + msg : "[DEBUG] [" + tag + "] " + msg;
复制到剪贴板
Logger.PrintLog();
源码
在项目中创建一个LogUtil.ts
,将以下内容复制到里面。
/**
* 日志等级枚举
*/
export enum LogLv {
DEBUG = 0,
INFO,
WARN,
ERROR
}
/**
* 日志信息
*
* @export
* @class LogInfo
*/
export class LogInfo {
level: number;
msg: string;
time: string;
}
/**
* 日志
*
* @export
* @class Logger
*/
export default class Logger {
/**
* 最大记录条数
*
* @private
* @static
* @type {number}
* @memberof Logger
*/
private static MAX_LEN: number = 100000;
/**
*
* 当log满了之后删掉几个
* @private
* @static
* @type {number}
* @memberof Logger
*/
private static CLEAR_COUNT: number = 1;
/**
* log等级
*
* @public
* @static
* @type {number}
* @memberof Logger
*/
private static level: number = 0;
/**
* log列表
*
* @private
* @static
* @type {Array<LogInfo>}
* @memberof Logger
*/
private static logs: Array<LogInfo> = [];
/**
* 设置Log记录等级
*
* @static
* @param {LogLv} level
* @memberof Logger
*/
public static SetLevel(level: LogLv): void {
this.level = Number(level);
}
/**
* 收集、打印调试等级的日志
* Logger.debug("log");
* @static
* @param {string} msg
* @param {string} [tag="default"]
* @return {*} {void}
* @memberof Logger
*/
public static debug(msg: string, tag: string = "",): void {
if (Logger.level > LogLv.DEBUG) {
return;
}
//测试功能 自动寻找方法
// let stackFunc = (new Error).stack.split("\n")[2].split(" ")[5];
// let logMsg = tag == "" ? "[DEBUG] " + msg + " at " + stackFunc : "[DEBUG] [" + tag + "] " + msg;
let logMsg = tag == "" ? "[DEBUG] " + msg : "[DEBUG] [" + tag + "] " + msg;
Logger._addLog(LogLv.DEBUG, logMsg);
console.log(logMsg);
}
public static info(msg: string, tag: string = "",): void {
if (Logger.level > LogLv.INFO) {
return;
}
let logMsg = tag == "" ? "[INFO] " + msg : "[INFO] [" + tag + "] " + msg;
Logger._addLog(LogLv.INFO, logMsg);
console.info(logMsg);
}
public static warn(msg: string, tag: string = "",): void {
if (Logger.level > LogLv.WARN) {
return;
}
let logMsg = tag == "" ? "[WARN] " + msg : "[WARN] [" + tag + "] " + msg;
Logger._addLog(LogLv.WARN, logMsg);
console.warn(logMsg);
}
public static error(msg: string, tag: string = "",): void {
if (Logger.level > LogLv.ERROR) {
return;
}
let logMsg = tag == "" ? "[ERROR] " + msg : "[ERROR] [" + tag + "] " + msg;
Logger._addLog(LogLv.ERROR, logMsg);
console.error(logMsg);
}
/**
* 添加缓存日志
*
* @private
* @static
* @param {LogLv} level
* @param {string} logMsg
* @memberof Logger
*/
private static _addLog(level: LogLv, logMsg: string): void {
if (Logger.logs.length >= Logger.MAX_LEN) {
Logger.logs.splice(0, Logger.CLEAR_COUNT);
}
Logger.logs.push({
level: level,
msg: logMsg,
time: this.FormatDate(Date.now(), "yyyy-MM-dd HH:mm:ss")
});
}
/**
* 输出所有Log
*
* @static
* @memberof Logger
*/
public static PrintLog() {
//线上环境这个dir会有问题,所以换成for循环
//console.dir(Logger.logs);
let logStr = new String("");
for (let i = 0; i < Logger.logs.length; i++) {
const element = Logger.logs[i];
logStr += element.time + " " + element.msg + "\n";
}
cc.sys.localStorage.setItem("log", logStr);
// console.log(logStr);
// 存储到剪贴板
// console.log("当前平台是:" + cc.sys.platform);
if (cc.sys.platform == cc.sys.WECHAT_GAME) {
wx.setClipboardData({
data: logStr.toString(), //复制内容
success: function (res) {
// wx.getClipboardData({
// success: function(res) {
// console.log("复制成功:", res.data);
// return true;
// }
// });
Logger.debug("success复制成功:");
// return true;
}
});
} else {
this.CopyTextEvent(logStr.toString());
}
// wx.setClipboardData(Object object)
}
// 拷贝文本
public static CopyTextEvent(copyStr: string) {
const el = document.createElement('textarea');
el.value = copyStr;
// Prevent keyboard from showing on mobile
el.setAttribute('readonly', '');
//el.style.contain = 'strict';
el.style.position = 'absolute';
el.style.left = '-9999px';
el.style.fontSize = '12pt'; // Prevent zooming on iOS
const selection = getSelection()!;
let originalRange;
if (selection.rangeCount > 0) {
originalRange = selection.getRangeAt(0);
}
document.body.appendChild(el);
el.select();
// Explicit selection workaround for iOS
el.selectionStart = 0;
el.selectionEnd = copyStr.length;
let success = false;
try {
success = document.execCommand('copy');
} catch (err) { }
document.body.removeChild(el);
if (originalRange) {
selection.removeAllRanges();
selection.addRange(originalRange);
}
}
/**
* 格式化时间
* 调用 FormatDate(strDate, "yyyy-MM-dd HH:mm:ss")
* @param strDate (中国标准时间)时间戳等
* @param strFormat 返回格式
* @returns
*/
public static FormatDate(strDate: any, strFormat?: any) {
if (!strDate) return;
if (!strFormat) strFormat = "yyyy-MM-dd";
switch (typeof strDate) {
case "string":
strDate = new Date(strDate.replace(/-/g, "/"));
break;
case "number":
strDate = new Date(strDate);
break;
}
if (strDate instanceof Date) {
const dict: any = {
yyyy: strDate.getFullYear(),
M: strDate.getMonth() + 1,
d: strDate.getDate(),
H: strDate.getHours(),
m: strDate.getMinutes(),
s: strDate.getSeconds(),
MM: ("" + (strDate.getMonth() + 101)).substr(1),
dd: ("" + (strDate.getDate() + 100)).substr(1),
HH: ("" + (strDate.getHours() + 100)).substr(1),
mm: ("" + (strDate.getMinutes() + 100)).substr(1),
ss: ("" + (strDate.getSeconds() + 100)).substr(1)
};
return strFormat.replace(/(yyyy|MM?|dd?|HH?|ss?|mm?)/g, function () {
return dict[arguments[0]];
});
}
}
/**
* eLog - displays calling line number & message & dumps vars as pretty json string
* @param {string} msg - string to display in log message
* @param {any} dispVars - any number of variables (ellipsis , aka Rest parameters) to dump
* {@link https://github.com/evanw/node-source-map-support usable by typescript node-source-map-support module}
* {@link https://github.com/mozilla/source-map/ Mozilla source-map library & project}
* {@link http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/ good introduction to sourcemaps}
*/
private static eLog(msg: string, ...dispVars: any[]) {
//console.dir((new Error).stack);
/**
* go one line back for the caller
* @type {string}
*/
let stackLine = (new Error).stack.split("\n")[2];
console.dir(stackLine);
let stackLineSelect = stackLine.split(" ");
for (let i = 0; i < stackLineSelect.length; i++) {
const element = stackLineSelect[i];
Logger.debug("---" + element)
}
let selectFunction = stackLineSelect[5];
Logger.debug("触发方法为" + selectFunction);
/**
* retrieve the file basename & positional data, after the last `/` to the `)`
*/
//
let caller_line = stackLine.slice(stackLine.lastIndexOf('/'), stackLine.lastIndexOf(')'))
console.dir(caller_line);
/**
* test for no `/` ; if there is no `/` then use filename without a prefixed path
*/
// if (caller_line.length == 0) {
// caller_line = stackLine.slice(stackLine.lastIndexOf('('), stackLine.lastIndexOf(')'))
// }
//
/**
* filename_base - parse out the file basename; remove first `/` char and go to `:`
*/
//let filename_base = caller_line.slice(0 + 1, caller_line.indexOf(':'));
/**
* line_no - parse out the line number ; remove first `:` char and go to 2nd `:`
*/
//let line_no = caller_line.slice(caller_line.indexOf(':') + 1, caller_line.lastIndexOf(':'));
/**
* line_pos - line positional - from the last `:` to the end of the string
*/
//let line_pos = caller_line.slice(caller_line.lastIndexOf(':') + 1);
//Logger.debug(`eLog called by ${filename_base} on line# ${line_no} @ char# ${line_pos} said:\n${msg}`);
// print out the input variables as pretty JSON strings
// dispVars.forEach(value => {
// Logger.debug(JSON.stringify(value, null, 2));
// });
}
}