最近公司但愿我作一个日志系统,用来排查手手游Bug用的。由于前些时候实现了vConsole在手机上的显示,因此以为是轻车熟路了。麻烦的是 : 须要玩家在出现bug后打开记录截图给我方策划,供前端开发人员分析,Low是Low了点,可是好实现。可是没过几天新的状况出现了:游戏闪退。Oh , My God!玩家截图的机会都没有了。只有硬着头皮搞正真的Log方案了。
我在GitHub上搜到Log4js,可是专门为node.js作的类库,放在Egret前端上折腾了大半天,由于Log4js依赖太多的node.js库,因此无赖放弃。只有在互联网上自找其余出路。前端
Ⅰ:Log数据的来源
①,重写window.console方法
②,监听window.onerror方法
思想:当日志信息的条数达到必定的数量,或者有重要日志信息,当即向Web服务器发送请求,要求服务器保存日志。
以下代码:node
(function(){ var ___log___ = console.log; var ___error___ = console.error; var ___warn___ = console.warn; var ___info___ = console.info; var ___trace___ = console.trace; console.error = function(errMessage){ _cacheLog( "error" , errMessage ); ___error___.apply(console,arguments); }; console.log = function(logMessage){ _cacheLog( "log" , logMessage ); ___log___.apply(console,arguments); }; console.warn = function(warnMessage){ _cacheLog( "warn" , warnMessage ); ___warn___.apply(console,arguments); }; console.info = function (infoMessage) { _cacheLog( "info" , infoMessage ); ___info___.apply( console , arguments ); }; console.trace = function ( traceMessage ) { _cacheLog( "trace" , traceMessage ); ___trace___.apply( console , arguments ); } })(); window.onerror = function(msg, url, line, col, error) { var extra = !col ? '' : '\ncolumn: ' + col; extra += !error ? '' : '\nerror: ' + error; _cacheLog( "system_error" , msg + "\nurl: " + url + "\nline: " + line + extra ); var suppressErrorAlert = true; return suppressErrorAlert; };
Ⅱ:请求服务器保存Logjson
var _saveLog = function ( $logList = null ) { var $logMsg = _getLogList( $logList ); if( $logMsg ){ var $jsonTotal = ___config___["data_json"]; $jsonTotal = $jsonTotal.replace("{1}" , ___playerID___); $jsonTotal = $jsonTotal.replace("{2}" , ___platfrom___); $jsonTotal = $jsonTotal.replace("{3}" , ___serverID___); $jsonTotal = $jsonTotal.replace("{4}" , $logMsg); // var $blob = new Blob([$jsonTotal], { type: "application/json" });//text/plain;charset=utf-8 var $oXHR = new XMLHttpRequest(); $oXHR.responseType = "text"; $oXHR.open(___config___["upload_method"], ___servicePath___); $oXHR.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );//application/x-www-form-urlencoded $oXHR.addEventListener('load', function(event){ console.log("upload--ok--"); }, true); $oXHR.send( $jsonTotal );//$blob } }
Ⅲ:请求服务器提供相关的Log
思想:服务器将某一个渠道旗下的一个服务器下的某一个玩家的全部的日志文件达成一个Zip压缩包 ,供H5前端客户下载后端
//放到公司的管理网站上 var _downLog = function () { var $jsonTotal = ___config___["down_json"]; $jsonTotal = $jsonTotal.replace("{1}" , ___playerID___); $jsonTotal = $jsonTotal.replace("{2}" , ___platfrom___); $jsonTotal = $jsonTotal.replace("{3}" , ___serverID___); var xhr = new XMLHttpRequest(); xhr.open('post', "https://localhost:44370/Home/DownLog", true); xhr.responseType = 'blob'; xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { // var name = xhr.getResponseHeader('content-disposition'); // var filename = name.substring(20, name.length); var blob = new Blob([xhr.response]); let link = document.createElement('a'); let url = URL.createObjectURL(blob); link.style.display = 'block'; link.href = url; link.download = "Log.zip"; document.body.appendChild(link); link.click(); } } xhr.send($jsonTotal); }
须要注意的是 , content-disposition header字段受到了限制。调用就会报Refused to get unsafe header的错误。服务器
之因此选择C# , 是由于我对C#比较熟悉。
Ⅰ:写Log的思想以下图所示:
①,平台 ,服务器ID , 玩家ID都是H5客户端传来的
②,生成TXT日志文件,须要注意的是:同一天的放在都一个Txt中。
③,过时Txt日志文件须要删除,以释放服务器的空间。
Ⅱ:C#后端处理H5前端的下载请求app
[HttpPost] public ActionResult DownLog(string data) { LogTxt log = JsonConvert.DeserializeObject<LogTxt>(data); string path = $"{this._settings.Value.Root}/pf_{log.Platform}/server_{log.ServerID}/player_{log.PlayerID}"; byte[] bts = null; System.Net.Mime.ContentDisposition cd = null; if (Directory.Exists(path)) { ZipFile.CreateFromDirectory( path, $"{path}.zip" ); bts = System.IO.File.ReadAllBytes($"{path}.zip"); System.IO.File.Delete($"{path}.zip"); cd = new System.Net.Mime.ContentDisposition { FileName = $"player_{log.PlayerID}.zip", Inline = false // false = prompt the user for downloading; true = browser to try to show the file inline }; } else { if (!System.IO.File.Exists($"{this._error.Value.Root}/{this._error.Value.Sub}.zip")) { IO.Instance.CreateFolder(this._error.Value.Root); IO.Instance.CreateFolder($"{this._error.Value.Root}/{this._error.Value.Sub}"); DirectoryInfo root = new DirectoryInfo($"{this._error.Value.Root}/{this._error.Value.Sub}"); FileInfo[] files = root.GetFiles(); if (files == null || files.Length == 0) { IO.Instance.CreateFile($"{this._error.Value.Root}/{this._error.Value.Sub}/error.txt"); IO.Instance.WriteCommon($"{this._error.Value.Root}/{this._error.Value.Sub}/error.txt", this._error.Value.Txt, true); } ZipFile.CreateFromDirectory( $"{this._error.Value.Root}/{this._error.Value.Sub}", $"{this._error.Value.Root}/{this._error.Value.Sub}.zip" ); } bts = System.IO.File.ReadAllBytes($"{this._error.Value.Root}/{this._error.Value.Sub}.zip"); cd = new System.Net.Mime.ContentDisposition { FileName = $"{this._error.Value.Sub}.zip", Inline = false // false = prompt the user for downloading; true = browser to try to show the file inline }; } Response.Headers.Add("Content-Disposition", cd.ToString()); Response.Headers.Add("X-Content-Type-Options", "nosniff"); return File(bts, "application/zip"); }
思想 :
①,根据前端提供的平台 ,服务器ID , 玩家ID来寻找相关日志
②,若是找到则将日志所在的整个文件夹打包压缩
③,若是没找到,查找有无error.zip 若有,则返回error.zip,没有就构建error.zip并返回ide