C#에서 NLog 메세지를 WPF의 TextBox에 출력하는 방법

2019-08-30

WPF는 C#으로 윈도우즈 애플리케이션을 개발하는데 있어 꽤 훌륭한 UI프레임워크이다. 그런데 NLog에서 기본으로 제공되는 타겟은 Winform의 RichTextBox에 대해서만 제공하고 있으며 WPF에 대해서는 기본적으로 제공하는 도구가 없다.

NuGet의 WpgRichTextBox라는 wrapper라이브러리가 있으나 정작 사용해보면 잘 동작하지 않는다. 물론 윈도우의 시스템이벤트를 이용해서 로그를 전파하고 이를 수신해서 표시 할 수도 있으나, 시스템이벤트는 응용프로그램을 관리자권한으로 실행해야 하므로 보안상 고려하지 않는 것으로 하자.

이 글에서는 NLog의 Target을 하나 만들어서 WPF 컴포넌트에 로그정보를 출력하는 방법을 소개하겟다.

이 방법은 NLog에서 기본적으로 제공하지 않는 Target에 대해 출력하는 모듈을 작성할 때 활용 할 수 있을 것으로 생각한다.

1. 사전준비

C#/WPF 프로젝트를 만들어서 버튼을 클릭하면 주기적으로 로그를 콘솔에 출력하는 프로그램을 작성한다.

1.1 WPF 프로젝트 생성

C# WPF 프로젝트를 생성하고 다음과 같이 TextBox와 Button을 하나씩 만들어본다. 각 컴포넌트의 이름은 TbLog와 BtnStart로 지정한다.

1.2 NLog 설치 및 환경설정

개발하고 있는 프로젝트에서 NuGet 패키지를 검색해서 설치하면 된다.

이후 App.config 파일을 열어서 다음과 같이 콘솔에 로그를 출력할 수 있도록 타겟과 룰을 하나 지정해 둔다.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
      <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
    </configSections>
  
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>

    <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <targets>
      <target name="logconsole" xsi:type="Console" />
    </targets>
    <rules>
      <logger name="*" minlevel="Trace" writeTo="logconsole" />
    </rules>
  </nlog>
</configuration>

1.3 버튼 이벤트 지정

디자인 편집기에서 버튼을 더블클릭해서 클릭이벤트 코드를 생성후 아래와 같이 3초마다 한번씩 반복적으로 로그를 출력하는 코드를 추가한다.

이제 프로그램을 실행 후 버튼을 클릭하면, 주기적으로 콘솔에 로그가 출력될 것이다.

using System;
using System.Threading.Tasks;
using System.Windows;

namespace WpfNLog
{
    /// <summary>
    /// MainWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainWindow : Window
    {
        private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void BtnStart_Click(object sender, RoutedEventArgs e)
        {
            Task.Run(async ()=> {
                for(int i = 0; ; i++)
                {
                    await Task.Delay(TimeSpan.FromSeconds(3)).ConfigureAwait(false);
                    Logger.Debug("Log {0}", i);
                }
            });
        }
    }
}

2. TextBox에 로그 출력

Custom Target를 만들고, 이를 이용해서 TextBox에 로그메세지를 출력하는 것을 구현 해 본다.

2.1 로그이벤트를 전파할 Custom Target 생성

프로젝트에 NLogCustomTarget.cs파일을 새로 만든다음 다음과 같이 내용을 기록한다.

  • TargetWithLayout을 상속받는 클래스를 생성하며, Target attribute에 해당 타겟의 이름을 적어준다.
  • Write함수는 rule로 필터링 된 다음 해당 타겟에 로그를 기록할 때마다 불리는 함수이다. 이 안에서 로그이벤트를 전파하는 코드를 작성하면된다.
  • delegate 호출시 null인 경우를 고려해서 ?.Invoke 와 같은 형식으로 호출하는 편이 안전하다.
using NLog;
using NLog.Targets;

namespace WpfNLog
{
    [Target("WpfTarget")]
    public class NLogCustomTarget : TargetWithLayout
    {
        // 로그 이벤트를 전파할 델리게이트 
        public delegate void LogEventDelegate(string message);
        public LogEventDelegate LogEventListener;

        // 생성자
        public NLogCustomTarget() { }

        // 로그 이벤트 기록
        protected override void Write(LogEventInfo logEvent)
        {
            // 레이아웃 형식에 맞게 로그 포맷
            string logMessage = this.Layout.Render(logEvent);
            // 로그이벤트를 수신할 델리게이트가 하나이상 지정된 경우에만 함수 호출
            LogEventListener?.Invoke(logMessage);
        }
    }
}

2.2 Custom Target 초기화 및 등록

Wpf의 Window Loaded 이벤트에 앞서 생성한 Target를 등록하는 코드를 작성한다.

그리고 다시 프로그램을 실행해서 BtnStart 버튼을 누르면 로그가 콘솔이 표시됨과 동시에 TextBox에도 출력되는 것을 볼 수 있다.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    var config = new NLog.Config.LoggingConfiguration();

    // Targets where to log to: custom target
    var wpfLogger = new NLogCustomTarget();
    wpfLogger.LogEventListener += WriteLogToTextBox;

    // Rules for mapping loggers to targets
    config.AddRule(NLog.LogLevel.Debug, NLog.LogLevel.Fatal, wpfLogger);

    // Apply config           
    NLog.LogManager.Configuration = config;
}

private void WriteLogToTextBox(string log) {
    TbLog.Dispatcher.BeginInvoke(new Action(()=> {
        TbLog.AppendText(string.Format("\n{0}", log));
    }));
}

3. 참고 링크

  • https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target
  • https://github.com/NLog/NLog/wiki/Tutorial