[.Net]快速設定寫log檔方法(log4net)
以.net Winform為例(console, web也差不多):
1.加入log4net.dll參考(下載log4net, 選擇log4net-2.0.8-bin-newkey.zip下載, 解壓縮後資料夾選擇bin\net, 請勿選到bin\net-cp, 因為cp表示client profile, 是簡易版)
ps. 2022/11/17補充:
現在直接於visual studio裡面的nuget裡面直接安裝log4net會是最快、最簡單的方式。
2..exe的路徑加入log4net.config文字檔案,文字內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<log4net>
<root>
<level value="DEBUG"/>
<appender-ref ref="LogFileAppender"/>
</root>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<!--.log檔案寫入指定的路徑-->
<!--<file type="log4net.Util.PatternString" value="C:\temp\ReturnToFactoryExpiredSendMail\.log"/>-->
<!--.log檔案寫入.exe的相對路徑-->
<file type="log4net.Util.PatternString" value="LogFiles\.log"/>
<preserveLogFileNameExtension value="true"/>
<staticLogFileName value="false"/>
<param name="AppendToFile" value="true"/>
<rollingStyle value="Composite"/>
<datePattern value="yyyyMMdd"/>
<encoding value="UTF-8"/>
<!--只要設定MaxDateRollBackups屬性的話就會出現錯誤,目前已知的log4net bug-->
<!--<MaxDateRollBackups value="14" />-->
<maxSizeRollBackups value="-1" />
<maximumFileSize value="100MB"/>
<countDirection value="1"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date [%thread] %-5level %logger (%line) - %message%newline"/>
</layout>
</appender>
</log4net>
ps.2019/09/25補充
也可直接下載作者準備好的log4net的dll以及log4net.config
官網的dll是一大包,有80多MB得下載
ps. 2022/11/17補充:
現在直接於visual studio裡面的nuget裡面直接安裝log4net會是最快、最簡單的方式。
ps. 2022/11/17
由於官方的MaxDateRollBackups設定已經被認為bug且不會自動刪除舊的.log檔案,
這邊作者自行補充刪除舊的.log檔案的方式,直接複製下列程式碼即可,可 N 天內的.log檔案會保留,更舊的.log檔就會刪除 :
static string WriteLogFlag =
System.Configuration.ConfigurationManager.AppSettings["WriteLogFlag"];
static int Log4netDeleteOldFileDays = 90;//刪除超過90天的log檔案
static string Log4netPath = Directory.GetCurrentDirectory() + "\\LogFiles";
if (Directory.GetCurrentDirectory().ToLower().IndexOf("debug") > -1)
{
Log4netPath = Directory.GetCurrentDirectory() + "\\LogFiles";
}
else
{
Log4netPath = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\LogFiles";
}
//刪除舊的.log檔案
int countDeletedFiles = 0;
try
{ //log4net相關的程式碼
//避免正式環境的權限不足造成刪除檔案失敗,因此放在try catch
WriteLog("舊的.log檔案刪除流程開始執行。");
#region 自動刪除log4net的.log檔
//手動刪除 N天內的 舊的.log檔案
//最多刪除1000天內的log
List<string> filePatterns = new List<string>();
//Log4netDeleteOldFileDays變數請自行設定為要刪除幾天內的.log檔資料
//一般是設定在設定檔,例如:app.config(Console, Winform), web.config(asp.net), appSettings.json(asp.net core)
for (int i = 1000; i >= Convert.ToInt16(Log4netDeleteOldFileDays); i--)
{
string pattern = @"*.log";
pattern = DateTime.Now.AddDays(-i).ToString("yyyyMMdd") + pattern;
filePatterns.Add(pattern);
}
foreach (var filePattern in filePatterns)
{
string[] oldLogFiles = Directory.GetFiles(Log4netPath, filePattern);
foreach (var oldFile in oldLogFiles)
{
try
{
File.Delete(oldFile);
countDeletedFiles++;
//log.Debug("檔案:" + oldFile + " 已刪除。");
WriteLog("檔案:" + oldFile + " 已刪除。");
}
catch (Exception ex)
{
//log.Debug("檔案:" + oldFile + " 刪除過程發生錯誤:" + ex.ToString());
WriteLog("檔案:" + oldFile + " 刪除過程發生錯誤:" + ex.ToString());
}
}
}
#endregion
}
catch (Exception ex)
{
WriteLog("舊的.log檔案刪除過程中發生錯誤:" + ex.ToString());
}
WriteLog("舊的.log檔案刪除流程結束,總共刪除" + countDeletedFiles + "個舊的.log檔案。");
static void WriteLog(string msg, DataTable dt = null)
{
if (WriteLogFlag.ToLower() == "true")
{
Console.WriteLine(msg);
log.Debug(msg);
if (dt != null && dt.Rows.Count > 0)
{
string json = JsonConvert.SerializeObject(dt, Formatting.Indented);
Console.WriteLine("DataTable轉換成JSON的字串為:");
Console.WriteLine(json);
log.Debug("DataTable轉換成JSON的字串為:");
log.Debug(json);
}
}
}
3.Form_load加入程式碼:這裡是winform, console例子
ps. 這是傳統的初始化.config的方式,下方的CopyLog4NetConfig()範例有直接提供Console版本的進階的初始化方式,進階的方式可以自動判斷開發環境或是正式環境,並進行初始化.config。
XmlConfigurator.Configure(new System.IO.FileInfo("./log4net.config"));
以上,就設定完成
ps.2024 July 9th補充:
如果利用排程管理員執行.exe且寫入log4net的.log失敗的話,請把上面的寫法改為:
ps. 這是傳統的初始化.config的方式,下方的CopyLog4NetConfig()範例有直接提供Console版本的進階的初始化方式,進階的方式可以自動判斷開發環境或是正式環境,並進行初始化.config。
XmlConfigurator.Configure(new System.IO.FileInfo(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\log4net.config"));
然後在程式碼裡先宣告logger為全域變數:
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
之後只要在程式碼任意處加入debug等級以上的寫log,就會寫入檔案囉!
等級由低到高依序為:Debug, Info, Warn, Error, and Fatal.
log.Debug("程式已開始寫log!");
執行結果:文字檔寫入log
檔案名稱:當天日期.log(在D:\temp路徑, 若是修改此路徑, 執行之前請重建專案喔,相對路徑設定的話,例如:LogFiles\.log)
log內容:日期時間 執行序代號 log等級 寫log的function(行號) log的訊息
大概是這樣
ps. 2022 Nov 25th 補充
1. log4net不會自行建立路徑,因此需自行在程式碼中自動建立寫入.log檔的路徑,範例如下:
static string Log4netPath = Directory.GetCurrentDirectory() + "\\LogFiles";
if (Directory.GetCurrentDirectory().ToLower().IndexOf("debug") > -1)
{
Log4netPath = Directory.GetCurrentDirectory() + "\\LogFiles";
}
else
{
Log4netPath = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\LogFiles";
}
if (Directory.Exists(Log4netPath) == false)
{
Directory.CreateDirectory(Log4netPath);
}
2. log4net的log4net.config檔不會自動複製到執行檔的路徑,可利用以下範例程式的CopyEmailTemplateAndLog4NetConfig()自動複製,只要偵測到是在localhost的debug模式之下,就會開始自動複製。(此範例是Console程式)
//在debug模式之下,自動複製最新的email template html以及log4net.config到debug資料夾
static void CopyLog4NetConfig()
{
if (Directory.GetCurrentDirectory().ToLower().IndexOf("debug") > -1)
{
string parentParentDir = System.IO.Directory.GetParent(
Directory.GetCurrentDirectory()).Parent.FullName;
//複製其他商業邏輯相關的檔案,這邊是TempFiles資料夾裡面會有一些發信的範本的.html檔
Copy(parentParentDir + "\\TempFiles", Directory.GetCurrentDirectory() + "\\TempFiles");
//複製log4net.config檔案
System.IO.File.Copy(parentParentDir + "\\log4net.config",
Directory.GetCurrentDirectory() + "\\log4net.config", true);
XmlConfigurator.Configure(new System.IO.FileInfo("./log4net.config"));
WriteLog(ProjectName + "專案:偵測到在Debug模式,成功複製Log4NetConfig");
}
else
{
XmlConfigurator.Configure(
new System.IO.FileInfo(System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\log4net.config"));
WriteLog(ProjectName + "專案:偵測到不是在Debug模式,因此不複製Log4NetConfig");
}
}
static void Copy(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
CopyAll(diSource, diTarget);
}
static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
Directory.CreateDirectory(target.FullName);
// Copy each file into the new directory.
foreach (FileInfo fi in source.GetFiles())
{
//Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
}
// Copy each subdirectory using recursion.
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir =
target.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir);
}
}
static void WriteLog(string msg, DataTable dt = null)
{
if (WriteLogFlag.ToLower() == "true")
{
Console.WriteLine(msg);
log.Debug(msg);
if (dt != null && dt.Rows.Count > 0)
{
string json = JsonConvert.SerializeObject(dt, Formatting.Indented);
Console.WriteLine("DataTable轉換成JSON的字串為:");
Console.WriteLine(json);
log.Debug("DataTable轉換成JSON的字串為:");
log.Debug(json);
}
}
}
補充一下asp.net mvc的作法:
global.asax裡面加入:
string log4netPath = Server.MapPath("~/log4net.config");
log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(log4netPath));
log4net.config檔案放在bin資料夾裡面,並且順便加入專案裡面,以防以後bin資料夾裡面的檔案被刪除
然後要寫log的網頁在最上面加入這個宣告:
static ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
然後下面這樣就寫log檔案囉:
logger.Debug("進入了LDAP區塊:");
log檔案就會寫入一行:
2019-04-26 13:03:48,960 [15] DEBUG MyMVCWeb.Controllers.AccountController (69) - 進入了登入區塊:
參考資訊:
簡單記錄 log4net 的用法
https://dotblogs.com.tw/yuanlin/2012/05/30/72479