닷넷 프로젝트에서 로깅을... Log4Net - Logging Framework for Microsoft .Net

프로그램을 개발하거나 운영하는데 있어 로깅만큼 중요한 기능도 없을것이다. 시스템이나 어플리케이션에서 발생한 문제 해결의 출발점이
'에러의 재현' 과 '로그 분석'이기 때문이다.

Java 진영에서는, 이미 오픈소스에서 발전해 온, Log4J라는 막강한 로깅 프레임웍이 사실상의 표준(de facto standard)으로 존재하고 있다.
( Commons Logging 도 있지만 Log4J의 Wrapper 정도라고 생각하면 된다. )
그럼, MS진영에는 어떤 로깅 프레임웍이 있을까?
지금 회사에서 진행중인 프로젝트가 .Net 기반의 윈도우즈 어플리케이션이라 MS계열에서 널리쓰이고 있을것 같은(?) 로깅툴을 찾아볼 필요가 있었다.

그런데 아니나 다를까.. 이미 이쪽 진영에까지 Log4J의 영향력이 미치고 있을줄이야..
이미 Log4J를 닷넷용으로 포팅한 Log4Net이 있는 것이다.

Log4J를 사용해 봤다면 Log4Net역시 어렵지 않게 사용 할 수 있다.
이번 포스팅에서는 Log4Net을 활용하여 .Net 어플리케이션을 로깅하는 방법을 살펴보자.

Log4Net 공식 사이트 : http://logging.apache.org/log4net/index.html
라이센스 : Apache License, Ver 2.0 ( 상용어플리케이션에 이용할 수 있다!! )
지원하는 프레임워크 : 2008년 1월 현재
                               MS .Net Framework 1.0 , MS .Net Framework 1.1 , MS .Net Framework 2.0 ,
                               MS .Net Compact Framework 1.0 , Mono 1.2.3, MS Shared Source CLI 1.0, CLI 1.0 Compatible
                               6개의 프레임워크를 지원.
Appenders 일람
Appender .NET Framework 1.0 .NET Framework 1.1 .NET Framework 2.0 .NET CF 1.0 Mono 1.2 Shared Source CLI 1.0 CLI 1.0 Compatible
AdoNetAppender x x x x x x
AnsiColorTerminalAppender x x x x x x x
AspNetTraceAppender x x x x x
BufferingForwardingAppender x x x x x x x
ColoredConsoleAppender x x x
ConsoleAppender x x x x x x x
DebugAppender x x x x x x x
EventLogAppender x x x x x
FileAppender x x x x x x x
ForwardingAppender x x x x x x x
LocalSyslogAppender x x x x x
MemoryAppender x x x x x x x
NetSendAppender x x x
OutputDebugStringAppender x x x x
RemoteSyslogAppender x x x x x x x
RemotingAppender x x x x x x
RollingFileAppender x x x x x x x
SmtpAppender x x x x x
SmtpPickupDirAppender x x x x x x x
TelnetAppender x x x x x x x
TraceAppender x x x x x x x
UdpAppender x x x x x x x

Log4Net은 최종으로 로그가 기록될 장소( 파일, DB, 메일 등..)를 Appender를 이용하여 구분하고 있다.
사용할 Appender를 Log4Net 설정파일에 기술하면 Log4Net이 해당 Appender를 사용하여 로그를 기록하게 된다.  

사용방법은 이렇다.

1. 어플리케이션 소스 코드를 작성한다.

소스코드 보기..

2. 다운로드 받은 Log4Net을 압축해제하여 나온 디렉토리중 개발을 진행하고 있는 닷넷 프레임워크 버전에 해당하는
   log4net.dll을 Reference 한다.
3. Log4Net 설정하기
    - 설정파일을 작성하여 어플리케이션이 빌드되는 디렉토리에 배치한다.

log4net.xml 보기..

4. 어플리케이션을 빌드후 구동하여 로그가 잘 남는것을 확인한다. ^^


Log4NetSample.zip

본 포스팅에 사용된 예제소스를 포함한 비주얼스튜디오용 솔루션파일

첨부한 프로젝트는 WPF 어플리케이션으로 작성 되었습니다. 비주얼 스튜디오 2008이라면 바로 빌드가 가능하겠지만 VS2005인 경우 실행을 위해서는 http://www.yunsobi.com/blog/280 포스트를 참조하여 WPF개발 환경을 구축 한 후 실행 하셔야 합니다.


좀 더 들여다 볼까요?.

퍼포먼스 ( Perfomance )
로깅에서 퍼포먼스를 이야기할 때 다음 세가지 이슈사항을 고려할 수 있습니다.

1. Logging을 하지 않을 경우( turned off ) 로깅코드에 의한 퍼포먼스
로깅레벨을 OFF 로 설정한 경우라면 메소드 호출 비용이 듭니다. 하지만 메소드 생성에는 파라메터 생성에따르는 숨겨진 비용도 포함되어 있음을 잊지 마세요. 예를들어 다음과 같은 코드
log.Debug("Entry number: " + i + " is " + entry[i].ToString()); 
에서 i 와 entry[i] 를 string으로 치환하는 비용과 각 string을 더하는 비용이 소모됩니다. 파라메터를 생성하는 비용은 파라메터의 갯수와 생성되는 횟수에 큰 영향을 받습니다.

파라메터 생성 비용을 없애고 싶다면 아래와 같은 코드를 작성 하세요.
if(log.IsDebugEnabled)
{
    log.Debug("Entry number: " + i + " is " + entry[i].ToString());
}

디버깅 모드가 꺼져 있다면 위 코드는 파라메터 생성에드는 비용은 0이 될것입니다. 반면에 디버그기능이 켜져 있다면 두배의 비교 시간이 필요합니다. '디버그기능이 켜져있는지(log.IsDebugEnabled)'와 '로거가 디버그 모드인지(log.Debug())' . 하지만 이를 비교하는데 필요한시간은 매우 미미해서 실제 로그를 기록하는데 드는 비용의 1%정도에 해당합니다.
어떤 개발자들은 전처리나 컴파일타임의 테크닉등을 이용하여 로깅에대한 모든 상태를 조절하기도합니다.이는 퍼포먼스 측면에서 보면 완벽하다고 할 수 있겠지만, 로그레벨을 변경하기가 매우 까다로워 집니다. 이렇게 하는것에 대해 많은 사람들이 작은 성능향상을 위해 너무 많은것을 잃고 있다고 조언 합니다.

2. 로깅기능이 켜져 있을 경우 로그레벨에 의한 '로그작성'과 '로그작성 않기'의 판단에 따른 퍼포먼스
이는 기본적으로 로거 Hierarchy의 동작 성능에 따릅니다. 로깅이 켜져(turn on)있다면 log4net은 로그레벨과 로거의레벨을 비교하는 작업이 필요합니다. 하지만 로거 자체에는 레벨이 할당되어 있지 않습니다. 로그레벨은 로거 Hierarch로부터 상속받게 됩니다. 로그레벨을 상속받기 이전에 로거는 상위클래스를 확인 할 필요가 있겠지요.

여기에는 가능하면 짧은시간으로 동작케하기위한 노력이 담겨있습니다. 예를들어, 자식 로거들은 그들의 부모하고만 연결이 됩니다. 이전에 살펴본 BasicConfigurator의 예에서, Com.Foo.Bar 라는 이름의 로거는 루트로거와 직접 연결됩니다. 이것으로 존재하지 않을 Com 이나 Com.Foo 로거를 찾는 수고를 피할 수 있습니다. 이것으로 산재한 (sparse) hierarchies 속에서 상당한 속도향상을 얻을 수 있습니다.

로그레벨 의한 로깅 여부 판단은, IsXXXEnable 프로퍼티를 조사하는것에비해 약 3배정도 느리다고 볼 수 있습니다.

3. 로그메세지를 실제 기록하기
이는 로그메세지를 포메팅하는것과 목표에 실제로 내보내는 비용이 발생합니다. 여기에는 포메터와 Appender가 가능한 빨리 동작하도록 노력을 기울이는 수밖에 없겠지요.


어찌돼었든, log4net의 많은 기능들은 '속도'의 관점에서 디자인되었습니다. 몇몇 log4net 컴포넌트들은 성능향상을 위해 몇번이고 재작성 되었으며, 여전히 옵티마이즈되고 있으며 자주 갱신되고 있습니다. 이것만은 알아주세요.
SimpleLayout의 퍼포먼스 테스트에서 log4net은 System.Console.WriteLine 만큼의 성능을 보여주고 있다는 것을. 


PatterLayout 수정하기
log4net의 출력형식을 조정하기위해서는 PatternLayout과  Conversion Character를 이용할 수 있습니다.
예를들어 log4net.xml에 아래와 같은 패턴을 정의하여 남긴 로그를 보면
[code]
   <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
       <file value="C:\Window1.log" />
       <appendToFile value="true" />
       <datePattern value="-yyyy-MM-dd" />
       <rollingStyle value="Date" />
       <layout type="log4net.Layout.PatternLayout"> <!-- 레이아웃으로 패턴레이아웃을 지정했습니다.  -->
           <conversionPattern value="%d [%t] %-5p %c - %m%n" /> <!-- conversion 캐릭터를 기술했습니다. -->
       </layout>
   </appender>
[/code]
아래와 같은 형식으로 남습니다.
[code]
2008-01-08 13:30:18,938 [10] DEBUG Log4NetSample.Window1 - Log4NetSample.Window1인스턴스 생성 됨
2008-01-08 13:30:24,792 [10] DEBUG Log4NetSample.Window1 - OnButtonMouseDown 이벤트 발생!!
2008-01-08 13:30:24,856 [10] ERROR Log4NetSample.Window1 - 에러 발생!!!
System.FormatException: 입력 문자열의 형식이 잘못되었습니다.
   위치: System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number,
 NumberFormatInfo info, Boolean parseDecimal)
   위치: System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   위치: System.Int32.Parse(String s)
   위치: Log4NetSample.Window1.button_Click(Object sender, RoutedEventArgs e) 파일 D:\Project2007

\emodioroot\Samples\Log4NetSample\Log4NetSample\Window1.xaml.cs:줄 68

[/code]

출력 결과물 형식을 변경하기위해선 Conversion Character를 변경할 필요가 있는데요.
이용가능한 Conversion Character 일람은 아래와 같습니다.

Conversion Character Effect
a Used to output the frienly name of the AppDomain where the logging event was generated.
c

Used to output the logger of the logging event. The logger conversion specifier can be optionally followed by precision specifier, that is a decimal constant in brackets.

If a precision specifier is given, then only the corresponding number of right most components of the logger name will be printed. By default the logger name is printed in full.

For example, for the logger name "a.b.c" the pattern %c{2} will output "b.c".

C

Used to output the fully qualified class name of the caller issuing the logging request. This conversion specifier can be optionally followed by precision specifier, that is a decimal constant in brackets.

If a precision specifier is given, then only the corresponding number of right most components of the class name will be printed. By default the class name is output in fully qualified form.

For example, for the class name "log4net.Layout.PatternLayout", the pattern %C{1} will output "PatternLayout".

WARNING Generating the caller class information is slow. Thus, it's use should be avoided unless execution speed is not an issue.

d

Used to output the date of the logging event. The date conversion specifier may be followed by a date format specifier enclosed between braces. For example, %d{HH:mm:ss,fff} or %d{dd MMM yyyy HH:mm:ss,fff}. If no date format specifier is given then ISO8601 format is assumed (ISO8601DateFormatter).

The date format specifier admits the same syntax as the time pattern string of the ToString.

For better results it is recommended to use the log4net date formatters. These can be specified using one of the strings "ABSOLUTE", "DATE" and "ISO8601" for specifying AbsoluteTimeDateFormatter, and respectively ISO8601DateFormatter. For example, %d{ISO8601} or %d{ABSOLUTE}.

These dedicated date formatters perform significantly better than ToString.

F

Used to output the file name where the logging request was issued.

WARNING Generating caller location information is extremely slow. It's use should be avoided unless execution speed is not an issue.

l

Used to output location information of the caller which generated the logging event.

The location information depends on the CLI implementation but usually consists of the fully qualified name of the calling method followed by the callers source the file name and line number between parentheses.

The location information can be very useful. However, it's generation is extremely slow. It's use should be avoided unless execution speed is not an issue.

L

Used to output the line number from where the logging request was issued.

WARNING Generating caller location information is extremely slow. It's use should be avoided unless execution speed is not an issue.

m

Used to output the application supplied message associated with the logging event.

M

Used to output the method name where the logging request was issued.

WARNING Generating caller location information is extremely slow. It's use should be avoided unless execution speed is not an issue.

n

Outputs the platform dependent line separator character or characters.

This conversion character offers practically the same performance as using non-portable line separator strings such as "\n", or "\r\n". Thus, it is the preferred way of specifying a line separator.

p

Used to output the level of the logging event.

P

Used to output the an event specific property. The key to lookup must be specified within braces and directly following the pattern specifier, e.g. %X{user} would include the value from the property that is keyed by the string 'user'. Each property value that is to be included in the log must be specified separately. Properties are added to events by loggers or appenders. By default no properties are defined.

r

Used to output the number of milliseconds elapsed since the start of the application until the creation of the logging event.

t

Used to output the name of the thread that generated the logging event. Uses the thread number if no name is available.

u

Used to output the user name for the currently active user (Principal.Identity.Name).

WARNING Generating caller information is extremely slow. It's use should be avoided unless execution speed is not an issue.

W

Used to output the WindowsIdentity for the currently active user.

WARNING Generating caller WindowsIdentity information is extremely slow. It's use should be avoided unless execution speed is not an issue.

x

Used to output the NDC (nested diagnostic context) associated with the thread that generated the logging event.

X

Used to output the MDC (mapped diagnostic context) associated with the thread that generated the logging event. The key to lookup must be specified within braces and directly following the pattern specifier, e.g. %X{user} would include the value from the MDC that is keyed by the string 'user'. Each MDC value that is to be included in the log must be specified separately.

%

The sequence %% outputs a single percent sign.

2008/01/08 13:42 2008/01/08 13:42
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. 2010/08/01 18:33
    item 36, 닷넷 런타임의 진단기능을 활용하라 Tracked from 최익필의 이름없는 블로그
  1. 좋은 정보 감사합니다.

  2. Blog Icon
    서비

    도움이 되셨다니 고맙습니다.

  3. Blog Icon
    kkurrung

    좋은 정보 얻어 갑니다. ^^

WPF 폰트 버그 언제나 해결될까..

MS Expression Blend 툴에서는 잘 구분되는 폰트가 왜 빌드하고 실행하면 안 먹히는 거냐?
Windows XP도 닷넷 프레임웤이 돌아가는 MS OS 아니더냐... 뭐가 불만인게야...
폰트 안티앨리어싱 끄고 켜는 옵션이 없는 것도 답답해 죽겠는데..
동작하지도 않는 폰트 임베딩 기능을 뭐하러 넣어 놓은거야.. 앙?
아직 프리뷰니 정식 버전에 고쳐지길 기다리는 수밖에 없는 거니?

Blend 에서 폰트 적용

두 폰트 차이가 보이시죠?

실행시 폰트 적용 안됨

XP에서 실행하면 그냥 시스템 폰트가 적용되어 버립니다. T.T


폰트 안티앨리어싱을 제어할 수 없어 폰트로 어떻게 회피해 보려고 했는데.. Vista에서는 멀쩡히
동작하는 기능이 XP에서는 동작하고 있지 않습니다.( 현재까지는요.. 다음번 프리뷰에서는 이 부분도
수정되었으면 좋겠군요..)

2007/12/13 16:49 2007/12/13 16:49
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. XAML에서 폰트의 안티 앨리어싱은 벡터 렌더링 특성상 켜고 끄지는 못할 것 같아요. 혹시 WPF에서는 폰트를 기본적인 벡터렌더링을 사용하지 않고 비트맵 렌더링 하는 방법도 있을지는 모르겠지만요.

  2. XAML을 완전히 C#코드로 대치할 수 있는걸로 미루어보아 WPF의 C#코드로도 조절할 수 없을 것 같습니다.
    이런 문제 때문에 MS 비스타의 기본 폰트를 맑은고딕으로 잡은것 같기도해요.

MS Visual Studio 2005로 WPF 개발환경 구축하기

MS 비주얼 스튜디오 2008에는 기본적으로 WPF개발 환경이 포함되어 있지만 비주얼 스튜디오 2005환경에서
WPF 개발을 하기 위해선 몇 가지 프로그램을 추가 설치해야 합니다.

MS 비주얼 스튜디오 2005 개발환경에서 WPF(Windows Presentation Foundation) 개발 환경을 구축하기 위해
설치 해야 하는 프로그램의 일람을 정리해 둡니다.

우선 OS와 비주얼 스튜디오 2005까지 설치가 된 상태에서 시작하겠습니다.

1. Visual Studio 2005 SP1 설치 ( 다운로드 )
2. .Net Framework 3.5 설치(3.0 unInstall을 우선 수행 )  ( 다운로드 )
3. Windows SDK for Windows Vista 설치  ( 다운로드 )
4. Visual Studio 2005 Extensions for WPF and WCF 설치  ( 다운로드 )
5. Visual Studio 2005 Extensions for WWF 설치  ( 다운로드 )
6. (Option)Microsoft Expression Design 설치  ( 다운로드 )
7. (Option)Microsoft Expression Blend 설치  ( 다운로드 )
8. (Option)Microsoft Expression Web 설치  ( 다운로드 )
9. (Option)XmlNotepad 설치  ( 다운로드 )
10. (Option)WPF Performance Tool 설치  ( 다운로드 )

개발 환경을 구축하는데 위에 열거한 모든 프로그램을 추가 설치 해야 하는 것은 아닙니다.
4번 항목까지만 설치하면 기본적인 개발환경은 갖추어지며, 그 나머지는 필요에 따라 선택적으로
설치 하시면 됩니다.
2007/12/07 15:07 2007/12/07 15:07
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
  1. 2008/10/23 01:47
    Visual Studio 2005에서 WPF환경 구축 Tracked from 배고픈 공돌이네
  1. 너무너무 잘보고갑니다^^

WPF (Winsows Presentation Foundation) Application 개발 협업

wpf 어플리케이션 개발 협업도
전통적인 윈도우즈 어플리케이션 개발과 비교해 큰 차이점이라면 디자이너와 개발자 사이에
Integrator 역할이 추가되었다는 것.
관련 자료를 찾아보면 Integrator는 디자인 감각이 있는 senior 개발자가 맡는 것이 적당하다고 한다.

개발자와 디자이너가 서로의 영역을 더 잘 이해할 때 퀄리티 있는 어플리케이션 개발이 가능하다고 할까?
지난 몇 주간 디자이너들과 샘플 프로그램 작업을 하면서 절실히 느꼈던 부분이기도 하다.


2007/10/24 11:21 2007/10/24 11:21
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

WPF 어플리케이션 트레이 아이콘으로 실행하기

2007/10/16 22:34

서비 .NET & WPF ,

아래는 WPF로 작성된 윈도우즈 어플리케이션을 트레이 아이콘으로 실행 할 수 있는 코드.

public partial class Window1 : System.Windows.Window
{

    public Window1()
    {
        InitializeComponent();

        System.Windows.Forms.NotifyIcon ni = new System.Windows.Forms.NotifyIcon();
        ni.Icon = new System.Drawing.Icon("Main.ico");
        ni.Visible = true;
        ni.DoubleClick += 
            delegate(object sender, EventArgs args)
            {
                this.Show();
                this.WindowState = WindowState.Normal;
            };
    }

    protected override void OnStateChanged(EventArgs e)
    {
        if (WindowState == WindowState.Minimized)
            this.Hide();

        base.OnStateChanged(e);
    }
}


트레이 아이콘에서 제거 하고 싶으면 NotifyIcon 클래스의 Visible 프로퍼티를 조작한다.
ni.Visible = false;





2007/10/16 22:34 2007/10/16 22:34
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

WPF (Winsows Presentation Foundation) Class Hierarchy

2007/10/05 23:35

서비 .NET & WPF ,

WPF (Winsows Presentation Foundation) Class Hierarchy

WPF (Winsows Presentation Foundation) Class Hierarchy

Windows Presentation Foundation 의 Class 계층도
2007/10/05 23:35 2007/10/05 23:35
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

WPF (Winsows Presentation Foundation) LifeTime :: WPF 어플리케이션 생명주기

WPF (Winsows Presentation Foundation) LifeCycle

출처 : MSDN Library


MSDN에 있는 WPF의 라이프 사이클에 대한 그림이다.
가운데 Application Object 상자 안이 코어 부분인데, 하나의 Application은
Run 매서드로 시작해서 Shutdown 매서드의 호출로 끝나게 된다.
Shoutdown 매서드의 호출은 ShutdownMode의 값에 따라서 Application이 자동으로 호출해주는 경우와  
사용자가 반드시 호출해주는 경우로 구분된다. 그리고 Activated, Deactivated,
DispatcherUnhandledException, SessionEnding, Exit는 Application에 발생하는 이벤트이다.
SessionEnding의 경우는 사용자가 OS를 Shutdown하거나 Logoff 시에 호출되는데 이 이벤트 안에서
OS의 종료를 취소시킬 수 있다. 또한 DispatcherUnhandledException 이벤트는 Application에서
처리되지 않는 예외가 발생했을 경우에 발생하는 범용 예외 처리가 가능한 곳으로 지정하지
않았을 경우 예외가 발생하면 Application은 자동으로 종료된다. 
2007/10/01 12:01 2007/10/01 12:01
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

C# 레지스트리에 값을 쓰고, 읽고, 삭제하기

2007/09/28 19:31

서비 .NET & WPF ,

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;  // 레지스트리관련 클래스를 쓰기위해서 추가

namespace regiEx1
{
    class Program
    {
        static void Main(string[] args)
        {
            string regSubkey = "Software\\myTestKey";
            // 서브키를 얻어온다. 없으면 null
            RegistryKey rk = Registry.LocalMachine.OpenSubKey(regSubkey, true);
            // 없으면 서브키를 만든다.
            if (rk == null)                                                         
            {
                // 해당이름으로 서브키 생성
                rk = Registry.LocalMachine.CreateSubKey(regSubkey);                 
            }
            string[] strData = new string[] {"aaa","bbb","ccc"};
            // 서브키 아래 값 쓰기
            rk.SetValue("test", strData);
            // 서브키 아래 값 읽기
            string[] regStr = rk.GetValue("test") as string[];                     
           
            Console.WriteLine(regStr[1]);
            Console.ReadLine();

            // 서브키 삭제
            Registry.LocalMachine.DeleteSubKey(regSubkey);                         
        }
    }
}
2007/09/28 19:31 2007/09/28 19:31
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

[STAThread] 어트리뷰트는 뭘 의미하는거지?

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace Petzold.DisplaySomeText
{
    public class DisplaySomeText : Window
    {
        [STAThread]
        public static void Main()
        {
            Application app = new Application();
            app.Run(new DisplaySomeText());
        }
        public DisplaySomeText()
        {
            Title = "Display Some Text";
            Content = "Content can be simple text!";
        }
    }
}

와 같은 C# 코드에서 [STAThread] 가 의미하는 바는 :
기본적으로, VS .NET에 의해 만들어진 응용 프로그램의 Main()메소드에는 [STAThread] 어트리뷰트가 첨가되어 있다.
이 어트리뷰트는 해당 응용 프로그램이 COM형식을 이용하는 경우에 (단지 이 경우에만 해당하는 것인데) 해당 응용 프로그램이
단일 스레드 아파트(single threaded apartment, STA) 모델로 설정되어야 한다는 것을 런타임에게 알려주는 역할을 한다.
해당 응용 프로그램에서 COM 형식을 이용하지 않는다면, [STAThread] 어트리뷰트는 무시되기 때문에 삭제해도 무방하다.
2007/09/28 16:41 2007/09/28 16:41
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다