log4net을 이용한 Logging
Apache log4net은 다양한 출력 타겟으로 로깅을 할 수 있는 .NET용 오픈소스 라이브러리이다. log4net은 Java에서 사용하는 log4j 라이브러리를 포팅한 것이다. log4net을 사용하여 로깅을 하기 위해서는 일반적으로 다음과 같은 절차를 따른다.
Apache log4net은 다양한 출력 타겟으로 로깅을 할 수 있는 .NET용 오픈소스 라이브러리이다. log4net은 Java에서 사용하는 log4j 라이브러리를 포팅한 것이다. log4net을 사용하여 로깅을 하기 위해서는 일반적으로 다음과 같은 절차를 따른다.
- log4net을 NuGet에서 다운받는다. 다운로드가 완료되면 자동으로 log4net 이 References 에 추가된다.
PM> Install-Package log4net
log4net는 아래 그림과 같이 여러 네임스페이스들로 나뉘어지고 각 네임스페이스에는 여러 클래스들을 제공하고 있다.
- 두번째로 App.config 파일에 log4net 관련 기본 셋팅들을 설정한다. (주: Desktop Application인 경우는 App.config를 를 사용하고, 웹인 경우는 Web.config를 사용. 하지만, 아래 Step 3의 XmlConfigurator 안에서 임의로 Config 파일을 지정할 수도 있음. 이렇게 Config 파일을 이용하지 않고 물론 C#에서 직접 핸들링할 수도 있음)
App.config 설정은 먼저 configSections 안에 log4net 섹션 (주: 임의명칭 사용가능)을 추가하고 Type을 아래와 같이 Log4NetConfigurationSectionHandler 클래스로 지정한다.
log4net은 콘솔, 파일, 데이타베이스 등 여러 곳에 로그를 출력시킬 수 있는데, 이들 각각의 출력 타겟을 appender로 지정한다. 예를 들어, 아래 샘플은 콘솔 로깅을 위한 ConsoleAppender를 지정하고 있는데, 파일 로깅을 위해서는 FileAppender를, DB 로깅을 위해서는 AdoNetAppender 등을 지정하면 된다. 물론 복수 타겟으로 로깅할 수도 있다. appender에는 임의의 이름 (여기서는 ConsoleLog)을 지정하고 해당 로그 타켓 타입을 지정한다. appender 안에는 여러 옵션들을 지정할 수 있는데, 아래의 경우 layout을 지정하고 있다. 이 layout의 ConversionPattern 옵션은 로그 출력 형식을 지정하게 되는데, 아래 예제에서는 현재 날짜/시간을 나타내는 %date과 로그 메서드 호출로부터 전달되는 메시지인 %message를 사용하여 출력 포맷을 정하고 있다.
마지막으로 이러한 설정들 밑에 root 노드가 있는데, 여기서는 일반적으로 어떤 로깅 레벨까지 출력한 것인가와 어떤 타겟에 출력할 것인가를 지정하게 된다. 로깅 레벨은 ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF 등 7가지가 있는데, ALL은 모두 로깅한다는 것을 의미하며, OFF 는 아무것도 로깅하지 않는다는 것을 의미한다. 따라서 실제 사용되는 로깅 레벨은 나머지 5가지로 봐도 무방하다. 아래 예제에선 DEBUG 레벨 이상을 출력하도록 하고 있는데, DEBUG 레벨이 가장 낮은 레벨이므로 사실상 모두 로깅하는 것과 같다. level 노드 밑에 있는 appender-ref 노드는 실제 타겟으로 사용할 appender 를 지정하게 된다. appender 노드를 설정했다하더라도 appender-ref 노드에서 해당 appender 명을 추가하지 않으면 해당 타겟으로 로깅을 수행하지 않는다.
예제
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<appender name="ConsoleLog" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date : %message %newline"/>
</layout>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="ConsoleLog"/>
</root>
</log4net>
</configuration>
- 다음으로 소스코드에서 아래와 같이 XmlConfigurator()를 실행해야 하는데, 이는 XML Config 파일 (예: App.config)을 사용하여 log4net 관련 기본 셋팅들을 사용하도록 한다. 이 문장은 프로그램 내에서 한번만 실행하면 되며, 특별히 어떤 소스파일에 넣어야 한다는 규칙은 없지만, 주로 AssemblyInfo.cs나 Main() 메서드가 있는 소스 파일(예: Program.cs)에 넣어 준다.
[assembly: log4net.Config.XmlConfigurator(Watch = true)]// 임의의 Config 파일을 지정할 경우:// [assembly: log4net.Config.XmlConfigurator(ConfigFile = "My.config", Watch = true)]
- 위에서 여러 log4net 관련된 기본 셋팅이 완료되면, 이제 C# 코드에서 LogManager.GetLogger()를 통해 ILog 인터페이스를 얻어 log4net 기능을 사용하면 된다.
LogManager.GetLogger()를 매번 사용해도 되지만, 일반적으로 아래 예제와 같이 Static 필드를 만들어 사용하면 편리하다. LogManager.GetLogger()는 ILog를 인터페이스를 리턴하고 ILog는 Debug(), Info(), Warn(), Error(), Fatal() 등과 같은 레벨별 로깅 메서드들을 가지고 있다.
예제
using System;
using log4net; // log4net 네임스페이스
// 프로그램 내 한번만 지정
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace LogApp
{
class Program
{
// 로거 ILog 필드
private static ILog log = LogManager.GetLogger("Program");
static void Main(string[] args)
{
// 로거 사용 : 5가지 레벨의 메서드들
log.Debug("Main() started");
log.Info("My Info");
log.Warn("My Warning");
log.Error("My Error");
log.Fatal("My Fatal Error");
}
}
}
파일 로깅 (File Logging)
로그 데이타를 파일에 출력하기 위해서는 App.config (혹은 Web.config)에 아래와 같이 FileAppender 를 추가하고, root/appender-ref 에 해당 appender를 추가하면 된다. 아래 예제에서는 level을 Info 로 지정하였으므로, INFO 레벨 이상만 로그파일 Logs.txt에 로깅된다.
예제
<log4net>
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<param name="File" value="Logs.txt" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date [%logger] %message %newline" />
</layout>
</appender>
<root>
<level value="Info"/>
<appender-ref ref="LogFileAppender"/>
</root>
</log4net>
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<param name="File" value="Logs.txt" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date [%logger] %message %newline" />
</layout>
</appender>
<root>
<level value="Info"/>
<appender-ref ref="LogFileAppender"/>
</root>
</log4net>
Rolling 파일 로깅 (Rolling File Logging)
로그 데이타를 여러 파일에 나누어 Rolling 파일에 출력하기 위해서는 App.config (혹은 Web.config)에 아래와 같이 RollingFileAppender 를 추가하고, root/appender-ref 에 해당 appender를 추가하면 된다. Rolling 파일은 파일크기가 최대크기 (maximumFileSize 속성에 지정된 크기) 이상이 될 경우 다음 Rolling 파일에 로그를 추가하는 것이다. 예를 들어, mylog.1, mylog.2, mylog.3, ... 같은 방식으로 Rolling 파일들이 만들어 진다. 또한, Rolling 파일의 최대 갯수가 초과될 경우, 처음 시작한 (가장 오래된) Rolling 파일에 덮어쓰기를 시작한다.
아래 예제는 크기가 100KB 를 초과하면 새 Rolling 파일을 만들도록 하고, 최대 100개까지의 Rolling 파일을 만들 수 있도록 한 셋팅이다. 로그 파일이 100개가 넘어가면, 가장 오래된 Rolling 파일 데이타를 지우고 그곳에 로그를 출력하게 된다. 만약 무제한으로 Rolling 파일을 만들도록 하려면, maxSizeRollBackups를 -1 로 설정한다.
countDirection 속성은 1로 설정되면 새로 만들어지는 Rolling 파일 숫자가 1, 2, 3 순으로 증가하며 생성된다.
layout의 header에서 뉴라인을 표시하기 위해서는 아래와 같이 HTML 특수 코드를 사용하여 CRLF를 표시하고, ConversionPattern에서는 %newline 이라는 문자열을 사용한다.
만약 파일 확장자를 유지하고 파일 확장자 앞에 숫자를 증가시키려면, <PreserveLogFileNameExtension value= "true" /> 을 추가한다.
아래 예제는 크기가 100KB 를 초과하면 새 Rolling 파일을 만들도록 하고, 최대 100개까지의 Rolling 파일을 만들 수 있도록 한 셋팅이다. 로그 파일이 100개가 넘어가면, 가장 오래된 Rolling 파일 데이타를 지우고 그곳에 로그를 출력하게 된다. 만약 무제한으로 Rolling 파일을 만들도록 하려면, maxSizeRollBackups를 -1 로 설정한다.
countDirection 속성은 1로 설정되면 새로 만들어지는 Rolling 파일 숫자가 1, 2, 3 순으로 증가하며 생성된다.
layout의 header에서 뉴라인을 표시하기 위해서는 아래와 같이 HTML 특수 코드를 사용하여 CRLF를 표시하고, ConversionPattern에서는 %newline 이라는 문자열을 사용한다.
만약 파일 확장자를 유지하고 파일 확장자 앞에 숫자를 증가시키려면, <PreserveLogFileNameExtension value= "true" /> 을 추가한다.
예제
(App.Config)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!--configSections always should be first-->
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="mylog.txt"/>
<appendToFile value="true" />
<rollingStyle value="Size"/>
<maximumFileSize value="100KB"/>
<maxSizeRollBackups value="100" /> <!-- Use -1 for unlimited-->
<staticLogFileName value="true" />
<countDirection value="1"/>
<!-- PreserveLogFileNameExtension value="true" / -->
<layout type="log4net.Layout.PatternLayout">
<header value="DATE MESSAGE " />
<param name="ConversionPattern" value="%date %message %newline" />
</layout>
</appender>
<root>
<level value="Debug"/>
<appender-ref ref="RollingFileAppender"/>
</root>
</log4net>
</configuration>
(테스트 코드)
using System;
using log4net;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace LogTest
{
class Program
{
private static ILog log = LogManager.GetLogger("Program");
static void Main(string[] args)
{
string msg = "".PadRight(100, 'A');
for (int i = 0; i < 3000; i++)
{
log.Info(msg);
Console.WriteLine(i);
}
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!--configSections always should be first-->
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="mylog.txt"/>
<appendToFile value="true" />
<rollingStyle value="Size"/>
<maximumFileSize value="100KB"/>
<maxSizeRollBackups value="100" /> <!-- Use -1 for unlimited-->
<staticLogFileName value="true" />
<countDirection value="1"/>
<!-- PreserveLogFileNameExtension value="true" / -->
<layout type="log4net.Layout.PatternLayout">
<header value="DATE MESSAGE " />
<param name="ConversionPattern" value="%date %message %newline" />
</layout>
</appender>
<root>
<level value="Debug"/>
<appender-ref ref="RollingFileAppender"/>
</root>
</log4net>
</configuration>
(테스트 코드)
using System;
using log4net;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace LogTest
{
class Program
{
private static ILog log = LogManager.GetLogger("Program");
static void Main(string[] args)
{
string msg = "".PadRight(100, 'A');
for (int i = 0; i < 3000; i++)
{
log.Info(msg);
Console.WriteLine(i);
}
}
}
}
데이타베이스 로깅 (DB Logging)
로그 데이타를 DB에 출력하기 위해서는 App.config (혹은 Web.config)에 아래와 같이 AdoNetAppender 를 추가하고, root/appender-ref 에 해당 appender를 추가하면 된다. DB는 ADO.NET이 지원하는 모든 DB를 지원한다.
DB 로깅을 위해서는 미리 로그 테이블을 생성해 두어야 한다. 또한, App.config 파일에 해당 로그 테이블에 대한 INSERT문과 컬럼 데이타 타입을 appender 노드에 아래 예와 같이 정의해 주어야 한다.
아래 예제의 첫번째 CREATE TABLE문은 SQL Server에 Logs 라는 테이블을 만들 때 사용하는 SQL문 예제이다. 그리고 다음 두번째는 해당 테이블을 사용하는 AdoNetAppender를 App.config에 정의한 예이다.
DB 로깅을 위해서는 미리 로그 테이블을 생성해 두어야 한다. 또한, App.config 파일에 해당 로그 테이블에 대한 INSERT문과 컬럼 데이타 타입을 appender 노드에 아래 예와 같이 정의해 주어야 한다.
아래 예제의 첫번째 CREATE TABLE문은 SQL Server에 Logs 라는 테이블을 만들 때 사용하는 SQL문 예제이다. 그리고 다음 두번째는 해당 테이블을 사용하는 AdoNetAppender를 App.config에 정의한 예이다.
예제
-- Logs 테이블 정의
CREATE TABLE [Logs]
(
[Id] INT IDENTITY(1, 1) NOT NULL,
[CreateDate] DATETIME NOT NULL,
[Level] VARCHAR(50) NULL,
[Message] VARCHAR(max) NULL
)
<log4net>
<appender name="DBAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.50727.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="Data Source=(local);Initial Catalog=MyDB;Integrated Security=SSPI;" />
<commandText value="INSERT Logs(CreateDate,Level,Message) VALUES (@CreateDate,@Level,@Message)" />
<parameter>
<parameterName value="CreateDate" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="@Level" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<parameter>
<parameterName value="@Message" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</parameter>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="DBAppender" />
</root>
</log4net>
CREATE TABLE [Logs]
(
[Id] INT IDENTITY(1, 1) NOT NULL,
[CreateDate] DATETIME NOT NULL,
[Level] VARCHAR(50) NULL,
[Message] VARCHAR(max) NULL
)
<log4net>
<appender name="DBAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="1" />
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.50727.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<connectionString value="Data Source=(local);Initial Catalog=MyDB;Integrated Security=SSPI;" />
<commandText value="INSERT Logs(CreateDate,Level,Message) VALUES (@CreateDate,@Level,@Message)" />
<parameter>
<parameterName value="CreateDate" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="@Level" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<parameter>
<parameterName value="@Message" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</parameter>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="DBAppender" />
</root>
</log4net>