본문 바로가기
인생은 실전/C#

[C#] NLog를 사용한 로그 수집 방법(Logging)

by 나는영하 2022. 7. 26.

 NLog를 사용한 로그 수집 방법(Logging)

시스템을 작동할 때 시스템의 작동 상태의 기록과 보존, 이용자의 습성 조사 및 시스템 동작의 분석 등을 하기 위해 작동중의 각종 정보를 기록하는것을 로깅이라 한다.

 

C#에서 이러한 로그 데이터를 수집하고 관리하기위해 쉽게 사용할 수 있는 라이브러리가 대표적으로 NLogLog4Net 이 있습니다. 오늘은 처음 사용하는 사람들에게는 더 쉽게 설정하고 사용할 수 있다는 NLog에 대해 알아보도록 하겠습니다!! 

 

본 글은 간단한 문제를 해결해나가면서 NLog 사용법을 알아보도록 하겠습니다.

 

문제

  • 사용자가 Console 창에 임의의 숫자 2개를 입력하면 나누기 연산을 수행하고, 그 결과를 Try Catch 문을 통해 Log를 기록한다.
  • 정상적으로 나누기 연산이 수행되면(Try 문) 날짜와 시간, 해당 네임스페이스와 클래스명, Log Level, Log Message 정보를 가지는 LOG를 지정된 경로에 Text 파일로 만들고 Console창에 띄어준다.
  • 비정상적으로 나누기 연산이 수행되면(Catch 문) 날짜와 시간, 네임스페이스와 클래스명, Log Level, 예외처리이유 정보를 가지는 LOG를 지정된 경로에 Text 파일로 만들고 Console창에 띄어준다. 

※ 아래의 풀이 과정에 대한 설명은 대부분 NLog의 정식 홈페이지에 있는 설명을 기준으로 정리하습니다. 자세한 내용은 아래의 홈페이지를 참고하시면 됩니다😁

https://github.com/NLog/NLog/wiki/Tutorial

 

 1. Nuget을 사용해서 NLog 패키지 설치

수동으로 종속성을 주입할 수 있다고는 하지만 더 편한 방법인 Nuget 패키지를 사용해서 관련 라이브러리를 설치하도록 하겠습니다. 

  • NLog
  • NLog.Extensions.Logging

 

 2. NLog 설정 파일 생성

NLog를 설정하는 파일은 XML형태이며 관련 설정파일은 아래 2가지 경우로 만들 수 있습니다. 

  1. Visual Studio Project Config 파일 내에 NLog 설정 부분 추가
  2. nlog.config라는 독립된 어플리케이션 구성 파일 생성 

저는 2번째 방법으로 NLog 설정 파일을 생성해보도록 하였습니다. 

 

- 해당 네임스페이스 내에 "nlog.config" 라는 이름의 애플리케이션 구성 파일 생성 

- nlog.config 파일을 만든 후 파일 속성에서 "출력 디렉터리에 복사"를 복사 안함 → 항상 복사로 변경

(좌) 변경 전                                                                                                       (우) 변경 후

 

 3. nlog.config 파일내 설정값 입력 및 세부설명 

 

🔴 위의 문제를 해결하기 위한 nlog.config 내 설정 코드 

<?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">
 
	<targets>
		<target name="logfile" xsi:type="File" fileName="${gdc:item=LogPath}\${callsite}\${shortdate}_${level}.txt"
				layout="${longdate} - ${logger} - ${message}"  />
		<target name="logconsole" xsi:type="Console" layout="${longdate} - ${logger} - ${message} "/>
	</targets>
 
	<rules>	
		<logger name="*" minlevel="Trace" writeTo="logconsole" />
		<logger name="*" minlevel="Trace" writeTo="logfile" />
	</rules>
</nlog>
 

① 최상위 요소 (Top - Level Elements)

  • Targets : 로깅할 대상이나 출력 정의
  • Rules : 로그 라우팅 규칙 정의 (출력으로 지정한 Target과 매칭하는 설정부 포함)
  • Extensions : dll 파일로 부터 NLog 확장
  • Include : 외부 설정 파일 포함
  • Variable : 구성 변수의 값 설정

최소한 1개의 Target과 1개의 Rule 관련 설정만 있으면 아주 기본적인 구성은 된다.

이해가 안가더라도 아래의 내용을 더 보면 이해가 되니 최상위 요소에는 위와 같은 요소들이 있다는 정도만 알고 넘어가도록 하겠습니다. 

 

② Targets 

로깅할 대상을 정의하는 부분으로 아래 2개의 속성은 필수입니다. 

  1. Name : Target Name / 추후 나올 Rule 태그의 writeTo 속성값과 매칭시킬 부분
  2. Type : Target Type / ex) "File", "DATABASE", "Mail" 등등
    실제로 사용할때는 < xsi:type = "File" > 형태로 사용하게 됩니다. 

Target Type에 따라 Target 태그의 세부적인 속성이 더 추가될 수 있습니다.

Targets 사용 예시

자세한 사항은 아래 NLog 정식 홈페이지를 참고바랍니다!!  

https://nlog-project.org/config/?tab=targets 

 

Config | NLog

 

nlog-project.org

③ Log Levels 

로그에도 레벨이 정해져 있으며 레벨에 따라 특정 수준의 로그 메세지를 포함하거나 무시할 수 있도록 구성되어 있습니다.

Log Levels

Trace는 가장 낮고 자세한 수준이며 개발에 주로 사용되는 단계입니다.

즉, Rule 설정부분에서 minlevel 속성 값을 Trace로 하면 모든 수준의 Logger를 볼 수 있습니다. 

 

④ Rules 

- Rule Name : Rule의 name과Logger 객체의 이름과 동일해야 합니다. 
Logger를 생성하는 방법은 크게 2가지로 나뉠수 있습니다.  

 

(1)  NLog.LogManager.GetLogger("RuleName") 메서드 
GetLogger를 사용하기 위해서는 RuleName의 속성값과 동일한 값을 해당 메서드의 파라메타로 입력해야 합니다. 

 

아래와 같은 Rule을 작성 했다면

<logger name="EquipmentExceptionLogger" minlevel="Trace" writeTo="EquipmentExceptionLogger" />

아래와 같이 GetLogger 메서드를 사용해야 합니다. (메서드 파라메타가 logger name과 같음을 확인하자!!)

LogManager.GetLogger("EquipmentExceptionLogger"));


(2) NLog.LogManager.GetCurrentClassLogger() 메서드 

GetCurrentClassLogger를 사용하기 위해서는 Logger name이 "NameSpace.ClassName"이 되는 곳을 지정해서 사용할 수 있습니다. 

 

아래와 같은 Rule을 작성 했다면

<logger name="*" minlevel="Info" writeTo="logconsole" />

아래와 같이 GetCurrentClassLogger 메서드를 사용해야 합니다. (별도의 파라메타는 없음) 

private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
// 앞부분(NLog)은 네임스페이스

 

- minlevel : 기록할 수 있는 로그의 최소 레벨 

 

- writeTo : 해당 로그를 저장하고자 하는 Target과 매칭지어서 해당 Target Name을 속성값으로 입력해주면 된다. 

 

⑤ Logger Name Filter 

Rule Name과 Logger Name을 매핑시키기 위해서 와일드 카드 문자(하나 또는 많은 문자를 나타내는데 사용할 수 있는 키보드 문자)를 사용할 수 있다. 

  1. * : 임의의 문자열 (0개 이상의 문자와 일치)
  2. ? : 임의의 한 문자를 대신한다. (정확히 1개의 문자와 일치)  

이를 이용해서 특정한 네임스페이스의 모든 클래스에서 사용하고 싶다면 아래와 같이 Rule Name을 설정해주면됩니다. 

<logger name="Name.Space.*" writeTo="target1" />

 

⑥ Variables 

NLog 구성 변수를 사용하면 반복되는 텍스트를 줄여 구성을 단순화할 수 있습니다. 

이는 백마디 말보다 한번의 예제가 더 이해하기 쉽기 때문에 아래의 예제를 참고해 주세요!! 

 

<Variable> 태그 부분

<variable name="layout" value = "[${date:format=yyyy-MM-dd HH\:mm\:ss.fff}] ${mdc:item=Time} ${level} ${message}" />

 

<Target> 태그 부분

 <target name="SmartEquipmentFrameworkLogger" xsi:type="AsyncWrapper" queueLimit="2147483647" overflowAction="Discard">
      <target xsi:type="File"
              fileName="${gdc:item=LogPath}/${gdc:item=ApplicationID}/SmartEquipment-${date:format=yyyy-MM-dd}.log"
              layout="${layout}" 
              concurrentWrites="true"
              keepFileOpen="true"/>

 

여기서 Target의 Layout 속성은 File형태로 로그를 출력할때 어떠한 문장 구성으로 문구를 출력할지를 정의해두는 부분입니다. 따라서 이 부분을 variable을 통해 정의했고, 이를 동일하게 다른 Target에 사용하면 동일한 문장 구성으로 출력할 수 있습니다. 

 

⑦ Layouts and layout renderers

현재 시간과 날짜, 로그메세지를 생성한 클래스와 네임스페이스, 로그 레벨, 메세지 텍스트를 출력하기 위해서는 아래와 같이 ${, } 구분 문자를 통해 레이아웃 랜더링을 구성하면 됩니다. 

<target name="c" xsi:type="Console"  layout="${longdate} ${callsite} ${level} ${message}"/>

※ 홈페이지 참고!! 

https://nlog-project.org/config/?tab=layout-renderers

 

Config | NLog

 

nlog-project.org

 

 

 4. 로그 메세지 작성 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NLog;

namespace NLogYhyou
{
    internal class Program
    {
        private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
        private static readonly NLog.Logger Logger1 = NLog.LogManager.GetLogger("Test");

        static void Main(string[] args)
        {
            GlobalDiagnosticsContext.Set("LogPath", "c:\\log");

            string a = System.Console.ReadLine();
            string b = System.Console.ReadLine();
            try
            {
                int c = int.Parse(a) / int.Parse(b);
                System.Console.WriteLine("정상");
                Logger.Trace("value : {0}", c);
            }
            catch (Exception e)
            {
                System.Console.WriteLine("오류");
                Logger.Error(e, e.Message);
            }
        }
    }
}

 

LOG 확인

🟢 정상적인 나누기 연산을 수행했을 경우 

(좌) 폴더 경로 내 로그파일 생성 / (중) Text 파일 내용 / (우) 콘솔창

🟡 비정상적인 나누기 연산을 수행했을 경우 (0으로 나눈 경우)

(좌) 폴더 경로 내 로그파일 생성 / (중) Text 파일 내용 / (우) 콘솔창

댓글