搜尋此網誌

2025年3月17日 星期一

dotnet NLog 與 Docker Container

緣起:


    我現在自己獨立弄一個專案,上個禮拜有成功把所需的服務都用 docker container 跑起來,彼此間的溝通與協作也正常,所以接下來要開始正式開發後台的程式了,但在這之前,我想先弄好專案 app 的記錄 log 功能。之前還在哈瑪星工作時,有看到我們產品內有用到 NLog 套件來記錄各種程式執行時的 log,用檔案系統紀錄。當程式發生問題時,有 log 檔能看,會比較有頭緒,不然之後正式上線,程式怎麼爆的都不知道,會很可怕。

    這篇文章主要記錄 Nlog 的設定與使用,還有在 Container 上執行時有碰到些雷。

圖跟本文無關,只是想分享一下最近畫的圖


NLog 的安裝與設定:


    文章主要是參考這篇,我開個 Asp Net Web 專案來測試,專案有啟用 Docker 支援。

先安裝這兩個 Nlog 套件

    
再裝這個

    接著是修改 Program.cs

using NLog;
using NLog.Extensions.Logging;

namespace NLogTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Configuration
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", false, true)
                .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", true, true)
                .AddEnvironmentVariables();

            builder.Services.AddRazorPages();
            builder.Services.AddLogging(logging =>
            {
                logging.ClearProviders();
                logging.AddNLog();
            });

            LogManager.GlobalThreshold = builder.Configuration.GetValue<NLog.LogLevel>("Logging:LogLevel:Default");

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapRazorPages();

            app.Run();
        }
    }
}

    13~17 行 : 加入 appsettings.json 檔,還有環境變數 (有點忘了是在哪抄來的)。

    20~24 行 : service 加入 Nlog,記得 using NLog.Extensions.Logging。

    26 行 : 這邊看到的,讀取 appsetting 來設定 Nlog 的全域 LogLevel。

    接著在專案目錄新增一個 Nlog.config 檔,之後去研究 log 檔的檔案路徑跟參數那些怎麼做調整,主要是參考這篇文章,我想要的 log 檔階層格式為,{專案當前執行目錄}/logs/{日期}/{LogLevel}/{完整 Class 名稱}.log,至於 log 紀錄的格式,我只有改 \n 的部份,我自己在測試的時候發現,如果想換行的話,要寫 ${newline} 才對,整個 Nlog.config 檔寫下來會長這樣。然後 Fatal 的 log ,單獨放在一個 fatal.log 檔中。 

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <!--設定 log 輸出目標-->
    <targets>
        <target name="FatalFile"
        xsi:type="File"
        fileName="${basedir}/logs/${processinfo:StartTime:format=yyyy-MM-dd:cached=true}/fatal.log"
        layout="[${longdate}]  ${processname:fullName=false} ${callsite:includeNamespace=true}: ${callsite-linenumber}${newline}${message}${newline}" />
        <target name="TraceFile"
        xsi:type="File"
        fileName="${basedir}/logs/${processinfo:StartTime:format=yyyy-MM-dd:cached=true}/${level:uppercase=true}/${callsite:includeNamespace=true}.log"
        layout="[${longdate}] ${processname:fullName=false} ${callsite:includeNamespace=true}: ${callsite-linenumber}${newline}${message}${newline}" />
        <target name="Console" xsi:type="Console" />
    </targets>

    <!--設定不同 log level 的 routing 路徑-->
    <rules>
        <logger name="*" minlevel ="Trace" maxlevel="Error" writeTo="TraceFile"/>
        <logger name="*" level="Fatal" writeTo="FatalFile" />
        <logger name="*" minlevel="Info" maxlevel="Error" writeTo="Console"/>
    </rules>
</nlog>

    15 跟 22 行,原本沒加這兩行,跑程式時發現 Console 沒輸出,後來翻完文章後,就把 Console 部份加上去了。 

    接著執行程式,沒問題的話,你可以在 /bin/Debug/{dotnet版本}下看到 logs 的資料夾

 

Docker 跑程式:


    有寫一個 docker-compose 來設定

services:
 web:
  build:
   context: . 
   dockerfile: ./NlogTest/Dockerfile
  image: nlog-test
  ports:
   - "8080:8080" 
  container_name: nlog-test
  volumes:
  - nlog-test:/app/logs

volumes:
 nlog-test:
  driver: local 

    container 跑起來後,我到 Desktop 的 volumes,查看 nlog-test 的內容,空空的


    實在是不知道問題出在哪,最後是去問了 chatgpt,有看到一個建議是說,Program.cs 程式可以加上這些,把 nlog 的錯誤打印出來。

NLog.Common.InternalLogger.LogToConsole = true;
NLog.Common.InternalLogger.LogFile = "internal-nlog.log";
NLog.Common.InternalLogger.LogLevel = NLog.LogLevel.Trace;

    加完後,我再跑 container,發現確實目錄下會多個 internal-nlog.log,打開檔案,發現


    重點是第 7 跟第 8,它說找不到 "NLog.config"、"nlog.config",我這才意識到,把檔名取成 "Nlog.config",大寫 N,小寫 l,它檔名看起來是要跟預設清單完全匹配才能找到 config 檔,所以我就修改 config 檔的名字,全部都小寫,然後再 build 跟 run container,跑起來後,我還是沒看到 logs 資料夾裡有東西,所以再去看那個 nlog.config,這次發現它內容多了不少,注意到有 Warn 


    哦 ~~ 原來是權限的問題,用 ls -l 看一下 logs 資料夾


    哦 ~~,原來在 docker-compose 中 mount volume 時,如果資料夾不存在,雖然它會自動幫你建立,但預設是用 root 權限建立的,所以我就想到要去改 dotnet 預設建立的 Dockerfile,在最後的 ENTRYPOINT 前加一行

RUN mkdir /app/logs

    再次 build 後 run container,可以看到,那個資料夾的所屬使用者跟群組都是 app


    接著去查看 Container 的 Files,logs 下有東西了,完美



沒有留言:

張貼留言