[C#] Log4net 사용법

SIMPLISM·2022년 7월 7일
0

개발.csharp

목록 보기
1/1
post-thumbnail

A. 사용환경

  • IDE : Visual Studio 2017
  • .NET Framework : 4.6.1
  • log4net version : 2.0.14

B. 시작하기 전에..

현재는 개발직군이 아니지만 업무적으로 필요한 툴들은 찾다가 적합한 소프트웨어를 못찾으면 개발해서 사용하곤 한다. 다행이 학교 다닐 적에 배워뒀던 c#이 윈도우 환경에서는 개발이 어렵지 않아서 자주 사용하고 있는데.. 매번 검색하기도 힘들고, 막상 참고할 문서라고 링크를 보관했다가 해당 문서가 사라지는 경우도 다반사... 거기에 참고할 문서에서 나만의 무언가를 적용한 경우에는 또 소스를 뒤지는 일들이 빈번해서 간단하게라도 블로그 형태로 문서를 정리해두려고 이번 포스트를 작성한다.

C. 프로젝트 생성

WPF도 예전에 공부하긴 했지만... 여전히 난 WinForm이 편하더라. WinForm용 프로젝트를 하나 생성한다.

D. log4net 패키지설치

도구 -> NuGet 패키지 관리자 -> 솔루션용 NuGet 패키지 관리...

위 메뉴를 찾아서 NuGet 패키지 관리자를 실행한다.

찾아보기 탭 -> log4net 선택 -> 프로젝트 선택 -> 설치버튼 클릭

log4net 패키지를 검색하고 설치한다.

E. log4net 설정파일 생성

log4net 설정을 위한 설정파일을 프로젝트에 추가한다.

설정파일에는 아래의 내용을 넣는다. 설정은 대략적으로 아래와 같다.

  1. 실행파일 하위에 logs/연도/월/연-월-일.log 파일명으로 로그파일을 생성한다.
  2. 로그패턴은 "[일시][Thread : 쓰레드번호][로그레벨][클래스명] 로그내용" 형태이다.
  3. FATAL 레벨의 로그는 logs 디렉토리 하위에 fatal.log에 따로 적재한다.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <log4net>
    <root>
      <level value="ALL"/>
      <appender-ref ref="file"/>
      <appender-ref ref="fatal_file"/>
    </root>
    <appender name="file" type="log4net.Appender.RollingFileAppender">
      <file value="logs\\" />
      <datepattern value="yyyy\\\\MM\\\\yyyy-MM-dd'.log'"/>
      <appendToFile value="true" />
      <rollingStyle value="Date" />
      <staticLogFileName value="false" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="[%date][Thread : %thread][%level][%logger] %message%newline" />
      </layout>
    </appender>
    <appender name="fatal_file" type="log4net.Appender.RollingFileAppender">
      <file value="logs\fatal.log" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="500" />
      <maximumFileSize value="10MB" />
      <staticLogFileName value="true" />
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMin" value="FATAL" />
        <param name="LevelMax" value="FATAL" />
      </filter>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="[%date][Thread : %thread][%level][%logger] %message%newline" />
      </layout>
    </appender>
  </log4net>
</configuration>

추가한 log4net.config 파일은 솔루션 빌드할 때마다 실행파일과 같이 복사되도록 "항상 복사"옵션을 선택한다.

AssmblyInfo.cs 파일을 열어서 제일 하단에 아래의 내용을 추가해서 log4net 설정파일의 위치를 지정해준다.(실행파일과 동일한 경로에 log4net.config 파일로 저장한 경우는 아래와 같다)

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]

F. 이제 코딩하자!

간단하게 샘플코드를 작성하고 결과물을 확인하자!

using log4net;

상단에 log4net을 선언하고 사용할 준비완료!

private static readonly ILog log = LogManager.GetLogger(typeof(Form1));

로깅을 수행할 Form1 클래스의 클래스 변수로 log 변수를 생성한다.

log.info("Form1 Start!");

info 레벨의 로그를 Form1 클래스 초기화 후에 찍어본다. 이제 프로젝트를 빌드하고 실행해보면...

실행파일인 Log4NetSample.exe와 동일한 위치에 logs 폴더가 생성되고

logs 폴더 하위에는 연도 폴더와 fatal.log 파일이 생성

연도 폴더 하위에 월단위의 폴더가 생겼고

월단위의 폴더 하위에는 일단위 로그파일이 생성되었다! 그리고 그 파일을 열어보면...

Form1 클래스 초기화 후에 찍은 info 레벨의 로그가 찍혀있는 것을 확인!

G. 로그레벨별 테스트코드

이제 로그레벨별로 로그가 잘 작성되는지 확인을 하기 위해서 버튼을 5개 만들어본다.

버튼을 클릭했을 때, 로그레벨별로 로그가 작성되도록 코드를 간단하게 추가한다.

정상적으로 로그레벨별로 로그가 작성되었다면, 테스트완료!

H. 공통화?

코딩의 시작은 Copy & Paste & Modify 작성된 코드를 복사 붙여넣기하고, 수정하기의 반복이라고 생각한다. 근데 Copy & Paste까지는 잘 해놓고... 가끔 Modify를 안해서 로직이 꼬이거나 하는 경우가 빈번하다. 그리고 그런 코드는... 디버깅이 더 어렵더라..ㅠ 그래서 Modify를 덜 할 수 있도록 약간의 공통화(재사용성 강화?)를 해보자.

private static readonly ILog log = LogManager.GetLogger(typeof(Form1))

위 코드에서는 Form1이라는 부분은 저 라인을 복사 & 붙여넣기하고 각 로깅을 하기 위한 클래스명을 넣어줘야한다.(즉, 귀찮다..)

안바꿔줘도 되게 바꿔보자.

using System.Reflection;

System.Reflection이라는 참조를 추가한다. 그리고 아래처럼 위 log 변수 선언을 아래처럼 바꾼다.

private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

Form1이라는 고정된 값을 현재 실행 중인 메소드의 Class명을 반환받도록 코드를 변경해준것이다.(즉, 붙여넣기까지만 하자)

그리고 로그파일에서 오류가 발생한 Method 추적을 위해서 각 메소드 시작부분, 종료부분, Exception부분에 debug 레벨로 로그를 찍는 경우가 있다.

즉, 이렇게...

private void button6_Click(object sender, EventArgs e)
{
	log.Debug("button6_Click() Start");

	try
	{
		if (true == true)
		{
			throw new Exception("Method Error catched!");
		}
	}
	catch (Exception ex)
	{
		log.Error("button6_Click() - " + ex.Message);
	}
	finally
	{
		// nothing to do
	}

	log.Debug("button6_Click() End");
}

위 코드처럼 시작, 종료, Exception 시에 로그를 각각 찍어주면, 오류가 발생했을 시에 어느 Method에서 오류가 났고, Exception이 발생했는지 확인이 된다. 근데 가끔 복사 붙여넣기하고 저 상수로 되어있는 Method 부분을 안바꾸면... 디버깅 시에 더 헷갈린다.

그래서 복사 & 붙여넣기까지만 해도 잘 동작하게 아래처럼 바꿔보자.

private void button6_Click(object sender, EventArgs e)
{
	log.Debug(MethodBase.GetCurrentMethod().Name + "() Start");

	try
	{
		if (true == true)
		{
			throw new Exception("Method Error catched!");
		}
	}
	catch (Exception ex)
	{
		log.Error(MethodBase.GetCurrentMethod().Name + "() - " + ex.Message);
	}
	finally
	{
		// nothing to do
	}

	log.Debug(MethodBase.GetCurrentMethod().Name + "() End");
}

이렇게 해두면... 최소한 실수는 덜 하겠지.. 저렇게 약간의 재사용성을 높인 코드를 만들어두고, 메소드 생성할 때마다 붙여넣기하고 메소드 내용을 채우면 코드 일관성도 높아지고, 디버깅에도 용이하다.

이제 프로젝트를 빌드하고 테스트를 해보면..

짜잔! 정상적으로 메소드명을 가져와서 로그를 찍어준다. 이제 즐거운 코딩을 해보자!

I. 참고문서

profile
go to simple.

0개의 댓글