網(wǎng)站正常運(yùn)行中有時(shí)出現(xiàn)異常在所難免,查看系統(tǒng)運(yùn)行日志分析問(wèn)題并能夠根據(jù)錯(cuò)誤信息快速解決問(wèn)題尤為重要,ABP對(duì)于系統(tǒng)運(yùn)行日志這塊已經(jīng)做了很好的處理,默認(rèn)采用的Log4Net已經(jīng)足夠滿(mǎn)足開(kāi)發(fā)過(guò)程中的需要了(當(dāng)然有需要的話也可以更換為其它日志組件)。
ABP官網(wǎng)地址:https://aspnetboilerplate.com/ <https://aspnetboilerplate.com/>
?
一、日志文件
ABP框架默認(rèn)使用了Log4Net日志組件,日志記錄在txt文件中,也可以替換成其它日志組件諸如Nlog,方便將日志文件信息直接記錄到數(shù)據(jù)庫(kù)中,具體情形使用具體組件。
當(dāng)一個(gè)文件達(dá)到了在Log4Net配置中設(shè)置好的文件大小上限時(shí),在文件名后按照數(shù)字倒排后開(kāi)始繼續(xù)增加文件。
當(dāng)需要查看錯(cuò)誤信息時(shí),直接在日期最近的文件中找出錯(cuò)誤信息即可,但是這個(gè)過(guò)程比較繁瑣,還需要從日志文件中去查看,并且日志文件中雖然做了分類(lèi),哪些是正常信息,哪些是錯(cuò)誤信息,但是不太直觀,因此,可以考慮直接將日志文件在頁(yè)面中呈現(xiàn),對(duì)信息進(jìn)一步加工,方便直接查看。
?
參考了AbpZero中的部分代碼并根據(jù)實(shí)際需要進(jìn)行整合,開(kāi)始在頁(yè)面中設(shè)計(jì)日志展示層。?
?
二、頁(yè)面展示日志信息
1、
系統(tǒng)日志服務(wù)應(yīng)屬于整個(gè)系統(tǒng)中相對(duì)其他業(yè)務(wù)模塊獨(dú)立的一部分,因此,首先在應(yīng)用層中新建一個(gè)Logging文件夾并創(chuàng)建一個(gè)日志應(yīng)用層服務(wù)接口與其實(shí)現(xiàn)。在接口中聲明兩個(gè)方法,直接查看當(dāng)前最近的日志文件中的日志信息以及從服務(wù)器下載所有的日志文件。
/// <summary> /// 網(wǎng)站運(yùn)行日志應(yīng)用層服務(wù) /// </summary> public interface
IWebSiteLogAppService : IApplicationService {/// <summary> /// 獲取最近的一個(gè)日志文件 ///
</summary> /// <returns></returns> GetLatestWebLogsOutput GetLatestWebLogs();
/// <summary> /// 下載所有的日志文件 /// </summary> /// <returns></returns> FileDto
DownloadWebLogs(); }
? 首先考慮直接獲取最近的日志文件信息,直接讀取即可,遵循的規(guī)則是讀取指定文件夾下指定文件后綴名更改日期為最大的文件然后從中讀取日志信息,并返回到前端。
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層中新增一個(gè)控制器,并寫(xiě)一個(gè)方法調(diào)用日志服務(wù)獲取最近的日志文件信息,并處理好權(quán)限問(wèn)題及頁(yè)面左側(cè)菜單的展示。
/// <summary> /// 系統(tǒng)維護(hù)控制器 /// </summary> [AbpMvcAuthorize] public class
MaintenanceController : SurroundControllerBase {private readonly
IWebSiteLogAppService _webSiteLogAppService;public
MaintenanceController(IWebSiteLogAppService webSiteLogAppService) {
_webSiteLogAppService= webSiteLogAppService; } /// <summary> /// 首頁(yè) ///
</summary> /// <returns></returns> public IActionResult Index() { return
View(); }/// <summary> /// 獲取最近日志信息 /// </summary> /// <returns></returns>
public JsonResult GetLatestWebLogs() { var getLatestWebLogsOutput =
_webSiteLogAppService.GetLatestWebLogs();return Json(getLatestWebLogsOutput); }
}
? 增加一個(gè)視圖文件并開(kāi)始編寫(xiě)前端代碼獲取日志文件,利用abp前端封裝好的ajax請(qǐng)求快速的獲取日志文件,然后通過(guò)layui中提供的徽章進(jìn)行加工處理,如此一來(lái),通過(guò)顏色快速區(qū)分哪些是錯(cuò)誤信息,哪些信息權(quán)重更大,更值得關(guān)注,此處引用了一個(gè)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; }
? 通過(guò)刷新按鈕獲取最近的日志信息。
三、下載日志文件
? 也可以直接下載日志文件去分析,當(dāng)然,從使用頻率講,這個(gè)功能的權(quán)重遠(yuǎn)低于直接頁(yè)面查看,但是細(xì)想一下,如果說(shuō)一個(gè)異常發(fā)生,沒(méi)有及時(shí)去頁(yè)面中查看,那么就得去成堆的日志中翻找,反而凸顯其作用了。
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(); }
? 將日志文件全部讀取出來(lái),然后打包存儲(chǔ)在緩存中,前端點(diǎn)擊下載按鈕時(shí)后臺(tái)返回壓縮包的標(biāo)識(shí)信息供前端直接下載,此處在控制器中加入一個(gè)文件管理的控制器,來(lái)作為系統(tǒng)中大部分文件下載的渠道。
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); });
點(diǎn)擊日志下載,瀏覽器開(kāi)始執(zhí)行下載任務(wù)。?
至此,系統(tǒng)日志的頁(yè)面查看就完成了,對(duì)于加入諸如查詢(xún)等更加豐富的功能,可以再進(jìn)行擴(kuò)展,也可以考慮直接使用已有的組件更方便的呈現(xiàn)的日志信息而無(wú)需手動(dòng)實(shí)現(xiàn),諸如LogDashBoard等,可以很快速的接入到系統(tǒng)中。?
代碼地址:https://gitee.com/530521314/Partner.Surround.git
<https://gitee.com/530521314/Partner.Surround.git>
?
2019-08-03,望技術(shù)有成后能回來(lái)看見(jiàn)自己的腳步
熱門(mén)工具 換一換
