C#에서 NLog 메세지를 WPF의 TextBox에 출력하는 방법
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