[.Net]快速設定寫log檔方法(log4net)

  • 16989
  • 0
  • 2024-07-22

[.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