網站正常運行中有時出現異常在所難免,查看系統運行日志分析問題並能夠根據錯誤信息快速解決問題尤為重要,ABP對於系統運行日志這塊已經做了很好的處理,默認采用的Log4Net已經足夠滿足開發過程中的需要了(當然有需要的話也可以更換為其它日志組件)。
ABP官網地址:https://aspnetboilerplate.com/
一、日志文件
ABP框架默認使用了Log4Net日志組件,日志記錄在txt文件中,也可以替換成其它日志組件諸如Nlog,方便將日志文件信息直接記錄到數據庫中,具體情形使用具體組件。
當一個文件達到了在Log4Net配置中設置好的文件大小上限時,在文件名后按照數字倒排后開始繼續增加文件。
當需要查看錯誤信息時,直接在日期最近的文件中找出錯誤信息即可,但是這個過程比較繁瑣,還需要從日志文件中去查看,並且日志文件中雖然做了分類,哪些是正常信息,哪些是錯誤信息,但是不太直觀,因此,可以考慮直接將日志文件在頁面中呈現,對信息進一步加工,方便直接查看。
參考了AbpZero中的部分代碼並根據實際需要進行整合,開始在頁面中設計日志展示層。
二、頁面展示日志信息
1、系統日志服務應屬於整個系統中相對其他業務模塊獨立的一部分,因此,首先在應用層中新建一個Logging文件夾並創建一個日志應用層服務接口與其實現。在接口中聲明兩個方法,直接查看當前最近的日志文件中的日志信息以及從服務器下載所有的日志文件。
/// <summary> /// 網站運行日志應用層服務 /// </summary> public interface IWebSiteLogAppService : IApplicationService { /// <summary> /// 獲取最近的一個日志文件 /// </summary> /// <returns></returns> GetLatestWebLogsOutput GetLatestWebLogs(); /// <summary> /// 下載所有的日志文件 /// </summary> /// <returns></returns> FileDto DownloadWebLogs(); }
首先考慮直接獲取最近的日志文件信息,直接讀取即可,遵循的規則是讀取指定文件夾下指定文件后綴名更改日期為最大的文件然后從中讀取日志信息,並返回到前端。
public GetLatestWebLogsOutput GetLatestWebLogs() { var directory = new DirectoryInfo(AppConsts.LogFilePath); if (!directory.Exists) { return new GetLatestWebLogsOutput { LatestWebLogLines = new List<string>() }; } var lastLogFile = directory.GetFiles("*.txt", SearchOption.AllDirectories) .OrderByDescending(f => f.LastWriteTime) .FirstOrDefault(); if (lastLogFile == null) { return new GetLatestWebLogsOutput(); } var lines = AppFileHelper.ReadLines(lastLogFile.FullName).Reverse().Take(1000).ToList(); var logLineCount = 0; var lineCount = 0; foreach (var line in lines) { if (line.StartsWith("DEBUG") || line.StartsWith("INFO") || line.StartsWith("WARN") || line.StartsWith("ERROR") || line.StartsWith("FATAL")) logLineCount++; lineCount++; if (logLineCount == 100) break; } return new GetLatestWebLogsOutput { LatestWebLogLines = lines.Take(lineCount).Reverse().ToList() }; }
2、在前端處理日志信息,Mvc層中新增一個控制器,並寫一個方法調用日志服務獲取最近的日志文件信息,並處理好權限問題及頁面左側菜單的展示。
/// <summary> /// 系統維護控制器 /// </summary> [AbpMvcAuthorize] public class MaintenanceController : SurroundControllerBase { private readonly IWebSiteLogAppService _webSiteLogAppService; public MaintenanceController(IWebSiteLogAppService webSiteLogAppService) { _webSiteLogAppService = webSiteLogAppService; } /// <summary> /// 首頁 /// </summary> /// <returns></returns> public IActionResult Index() { return View(); } /// <summary> /// 獲取最近日志信息 /// </summary> /// <returns></returns> public JsonResult GetLatestWebLogs() { var getLatestWebLogsOutput = _webSiteLogAppService.GetLatestWebLogs(); return Json(getLatestWebLogsOutput); } }
增加一個視圖文件並開始編寫前端代碼獲取日志文件,利用abp前端封裝好的ajax請求快速的獲取日志文件,然后通過layui中提供的徽章進行加工處理,如此一來,通過顏色快速區分哪些是錯誤信息,哪些信息權重更大,更值得關注,此處引用了一個lodash.js,該js中提供了許多的輔助方法。
function getFormattedLogs(logLines) { var resultHtml = ''; $.each(logLines, function (index, logLine) { resultHtml += '<span>' + _.escape(logLine) .replace('DEBUG', '<span class="layui-badge layui-bg-gray">DEBUG</span>') .replace('INFO', '<span class="layui-badge layui-bg-green">INFO</span>') .replace('WARN', '<span class="layui-badge layui-bg-orange">WARN</span>') .replace('ERROR', '<span class="layui-badge">ERROR</span>') .replace('FATAL', '<span class="layui-badge">FATAL</span>') + '</span><br/>'; }); return resultHtml; }
通過刷新按鈕獲取最近的日志信息。
三、下載日志文件
也可以直接下載日志文件去分析,當然,從使用頻率講,這個功能的權重遠低於直接頁面查看,但是細想一下,如果說一個異常發生,沒有及時去頁面中查看,那么就得去成堆的日志中翻找,反而凸顯其作用了。
public FileDto DownloadWebLogs() { var logFiles = GetAllLogFiles(); var zipFileDto = new FileDto("WebSiteLogs.zip", MimeTypeNames.ApplicationZip); using (var outputZipFileStream = new MemoryStream()) { using (var zipStream = new ZipArchive(outputZipFileStream, ZipArchiveMode.Create)) { foreach (var logFile in logFiles) { var entry = zipStream.CreateEntry(logFile.Name); using (var entryStream = entry.Open()) { using (var fs = new FileStream(logFile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 0x1000, FileOptions.SequentialScan)) { fs.CopyTo(entryStream); entryStream.Flush(); } } } } _tempFileCacheManager.SetFile(zipFileDto.FileToken, outputZipFileStream.ToArray()); } return zipFileDto; } private List<FileInfo> GetAllLogFiles() { var directory = new DirectoryInfo(AppConsts.LogFilePath); return directory.GetFiles("*.*", SearchOption.TopDirectoryOnly).ToList(); }
將日志文件全部讀取出來,然后打包存儲在緩存中,前端點擊下載按鈕時后台返回壓縮包的標識信息供前端直接下載,此處在控制器中加入一個文件管理的控制器,來作為系統中大部分文件下載的渠道。
var waitIndex = parent.layer.load(2); abp.ajax({ type:"Get", url: "@Url.Action("DownloadWebLogs", "Maintenance")", abpHandleError: false }).done(function (file) { location.href = '@Url.Action("DownloadTempFile", "File")' + abp.utils.formatString("?fileToken={0}&fileType={1}&fileName={2}", file.fileToken, file.fileType, file.fileName); }).fail(function (jqXHR) { parent.layer.msg(jqXHR.message, { icon: 5 }); }).always(function () { parent.layer.close(waitIndex); });
點擊日志下載,瀏覽器開始執行下載任務。
至此,系統日志的頁面查看就完成了,對於加入諸如查詢等更加豐富的功能,可以再進行擴展,也可以考慮直接使用已有的組件更方便的呈現的日志信息而無需手動實現,諸如LogDashBoard等,可以很快速的接入到系統中。
代碼地址:https://gitee.com/530521314/Partner.Surround.git
2019-08-03,望技術有成后能回來看見自己的腳步