只有累積,沒有奇蹟

2024年11月28日 星期四

[NET] 如何在 NLog 進行偵錯

發生情境
NLog 是在.NET 開發者中很受歡迎的 Open Source支援 .NET Framework 和 .NET Core安裝上可以很簡單的透過 Nuget 安裝下載使用,透過 NLog 定義的 Configure 定義專案自行紀錄/輸出的 Log Level 格式
,近同事反應在開發新專案使用上遇到離奇的事情,在使用 NLog 寫錯誤訊息時沒有辦法正常寫入到檔案中,NLog 在遇到問題時預設是不會 throw exception 的需要打開 NLog config 的 root element 一些設定
,以下簡單紀錄幫忙偵錯 NLog 的過程及該如何處理

偵錯方法 : 開啟 throwExpections = true
NLog 在遇到問題時預設是不會 throw exception,偵錯時可以開啟 throwExceptions = "true" 的設定,讓我們在除錯過程中知道 NLog 的錯誤訊息可以透過以下兩種方式設定
1. NLog Config
   在 NLog config 的根 element 定義 throwExceptions = true 
2. 在程式中設定
        static void Main()
        {
            LogManager.ThrowExceptions = true;

            Logger logger = LogManager.GetCurrentClassLogger();
            logger.Info("Start");
            logger.Info("This is a book");
        }

偵錯方法 : 開啟 NLog Internal Logging 設定
NLog 有自己的 logging 機制可以提供偵錯及除錯使用一般我們常用的都是 NLog.Logger 類別來記錄訊息,在需要使用內部 Logging 機制時可以用 InternalLogger 類別,使用上與 Logger 有點相似需要定義 LoggerLevel, Logger FileName 等資訊,在偵錯時可以設定以下兩個 attribute 設定紀錄 internalLogger 的錯誤訊息 ( 亦可在 NLog 檔案設定 )
1. internalLogFile : 設定 internalLogging 時檔案寫入的位置
2. internalLogLevel : 設定需要寫入的 Log Rule level
        static void Main()
        {
            InternalLogger.LogFile = "c:\\Logs\\log.txt";            
            InternalLogger.LogLevel = LogLevel.Error;
        }
 

後記
透過以上設定就可以看到 NLog 內部的錯誤訊息,可以更明確的知道程式爆炸的地方
舉例以一開始幫同事 Debug 的案例如下圖,發現他的NLog file copy 別專案的config內容,但未 copy 到 NLog_ELK的config內容導致 LogManager 讀取失敗發生 FileNotFount exception宣告結案 !!!




參考
Logging Troubleshooting
NLog
NLog Tutorial
NLog internalLogging

2024年11月4日 星期一

[Windows] 註冊 Windows Service 服務

前言
最近專案有個需求要將排程透過 Windows Service 服務來執行,在 Windows OS 要註冊 Service 可以用  cmd  與  powershell  兩種方式來建立以及刪除 Service,兩種方式之前都有使用過但要再使用時都會上網查因此決定紀錄一下未來方便查詢,此篇就針對這兩種方式進行基本介紹與說明若有問題歡迎提出一起討論或是給予指導。

使用命令提示字元 cmd
在執行時請先注意開啟 cmd 需要使用 Admin 權限執行,否則會有執行異常或是告知沒權限的錯誤訊息,以下為開啟時應用程式時用 administrator 開啟的畫面,用管理者身分執行 cmd

註冊服務
在 cmd 中可以透過 sc.exe 來建立 windows service,sc 全名為 service control,語法指令格式如下說明
描述:
        在登錄和服務資料庫中建立服務項目。
使用方法:
        sc <server> create [service name] [binPath= ] <option1> <option2>...

選項:
注意: 選項名稱包括等號。
      在等號和值之間必須空一格。
 type= <own|share|interact|kernel|filesys|rec|userown|usershare>
       (預設值 = own)
 start= <boot|system|auto|demand|disabled|delayed-auto>
       (預設值 = demand)
 error= <normal|severe|critical|ignore>
       (預設值 = normal)
 binPath= <.exe 檔案的二進位檔案路徑名稱>
 group= <載入順序群組>
 tag= <yes|no>
 depend= <相依性(以 / (反斜線) 隔開)>
 obj= <帳戶名稱|物件名稱>
       (預設值 = LocalSystem)
 DisplayName= <顯示名稱>
 password= <密碼>
舉例來說,要註冊 Windows Service 名為 TestService,其檔案位置在 D:\Job\TestService\Test.Jobs.exe 位置中,可以透過  create ServiceName  指令建立
D:\>sc create TestService binPath="D:\Job\TestService\Test.Jobs.exe"
[SC] CreateService 成功
建立成功可以看到新增 TestService 成功的訊息,接著我們使用   query ServiceName  查詢目前 service 狀態,剛建立好的 service 狀態會是 STOPPED (1)
D:\>sc query testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 1  STOPPED
        WIN32_EXIT_CODE    : 1077  (0x435)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

啟動服務
可以透過  start ServiceName  指令來啟動服務,要啟動時狀態為 START_PENDING (2)
D:\>sc start testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 26300
        FLAGS              : 
當正常執行,狀態就會更新為 RUN (4) 
SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

停止服務
要停止服務的可以透過  stop ServiceName 指令,執行完指令會進行停止的動作,因此查看當下狀態會是 STOP_PENDING,當停止後再次查詢可以看到狀態改為 STOPPED (1)
D:\>sc stop testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 3  STOP_PENDING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

D:\>sc query testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 1  STOPPED
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

刪除服務
當要刪除服務的話執行  delete ServiceName  指令,其回傳訊息與 create 類似直接回傳刪除服務成功
D:\>sc delete testservice
[SC] DeleteService 成功

使用 Powershell 
同樣的在執行前需要使用管理者身分執行 powershell,否則會有執行異常或是告知沒權限的錯誤訊息,
以下為開啟時應用程式時用 administrator 開啟的畫面

註冊服務
在 cmd 中可以透過 sc.exe 來建立 windows service,sc 全名為 service control,語法指令格式如下說明
PS C:\> New-Service -Name "TestService" -BinaryPathName "D:\Jobs\TestService\Jobs.exe" -DisplayName "Test Service" -StartupType Manual -Description "This is a test service."

Status   Name               DisplayName
------   ----               -----------
Stopped  TestService        Test Service
參數介紹


  • Name : 指定服務的名稱 (唯一)
  • BinaryPathName : 服務的執行檔案路徑位置
  • DisplayName : 顯示的名稱
  • StartupType : 啟動類型
  • Description : 描述
  • 建立成功之後,在 GUI 呈現畫面如下
    要註冊 Windows Service 名為 TestService,其檔案位置在 D:\Job\TestService\Test.Jobs.exe 位置中,可以透過  create ServiceName  指令建立
    D:\>sc create TestService binPath="D:\Job\TestService\Test.Jobs.exe"
    [SC] CreateService 成功
    建立成功,接著我們查詢目前 service 狀態,在 powershell 指令比 cmd 稍微多一滴滴要使用 win32service在透過 filter 來查詢剛建立好的 testservice,與 cmd 建立的相同剛建好的狀態都是 Stopped 
    PS C:\> Get-WmiObject win32_service -Filter "name='testservice'"
    
    ExitCode  : 1077
    Name      : TestService
    ProcessId : 0
    StartMode : Manual
    State     : Stopped
    Status    : OK

    啟動服務
    可以透過  start ServiceName -Name  指令來啟動服務,與 cmd 不同的是會等到 running 在顯示目前狀態
    PS C:\> Start-Service -Name "testservice"
    PS C:\> Get-WmiObject win32_service -Filter "name='testservice'"
    
    ExitCode  : 0
    Name      : TestService
    ProcessId : 18188
    StartMode : Manual
    State     : Running
    Status    : OK              : 

    停止服務
    使用  stop ServiceName  指令停止當下 windows service,執行完指令會進行停止的動作,再次查詢服務狀態就會是已停止 STOPPED
    PS C:\> Stop-Service -Name "testservice"
    PS C:\> Get-WmiObject win32_service -Filter "name='testservice'"
    
    ExitCode  : 0
    Name      : TestService
    ProcessId : 0
    StartMode : Manual
    State     : Stopped
    Status    : OK

    刪除服務
    在 powersehll 中刪除時要特別注意,官方 MSDN 中提供的 Remove service 是 powershell 6 才提供的新功能,如果您的 powershell 版本低於 6 時候執行 remove serv 則會跳出錯誤訊息 : 無法辨識 'Remove-Service' 詞彙是否為 Cmdlet、函數,如果不確定電腦中使用的 powershell 版本為何,可以輸入以下指令
    PS C:\> $PSVersionTable.PSVersion
    
    Major  Minor  Build  Revision
    -----  -----  -----  --------
    5      1      17763  316
    因此,Remove service 會根據版本不同執行不同的指令,如果 powershell 是 6.0 或以上請輸入指令
    Remove-Service someservice 
    powershell 是 6.0 以下
    Stop-Service 'testservice'; Get-CimInstance -ClassName Win32_Service -Filter "Name='testservice'" | Remove-CimInstance
    

    後記
    透過以上簡單的介紹說明了在 cmd 與 powershell 中如何新增、刪除、查詢及修改 windows service 的各種方式與指令,如果有需要更細節的內容說是說明,可以透過 MSDN 官方網站查詢相關指令與更多的應用細節,這裡就針對簡單操作做說明不在琢磨,希望自己的金魚腦寫完這篇之後可以記錄得更清楚些 ! 

    參考
    powershell create windows service

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com