Wpf NLog 显示日志到 RichTextBox
1. 项目中引入库
NLog
2. 引入三个文件:
WpfRichTextBoxTarget.cs
1 // 2 // Copyright (c) 2004-2011 Jaroslaw Kowalski <jaak@jkowalski.net> 3 // 4 // All rights reserved. 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions 8 // are met: 9 // 10 // * Redistributions of source code must retain the above copyright notice, 11 // this list of conditions and the following disclaimer. 12 // 13 // * Redistributions in binary form must reproduce the above copyright notice, 14 // this list of conditions and the following disclaimer in the documentation 15 // and/or other materials provided with the distribution. 16 // 17 // * Neither the name of Jaroslaw Kowalski nor the names of its 18 // contributors may be used to endorse or promote products derived from this 19 // software without specific prior written permission. 20 // 21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 // THE POSSIBILITY OF SUCH DAMAGE. 32 // 33 34 using System.Diagnostics; 35 using System.Windows.Controls; 36 using NLog; 37 using NLog.Targets; 38 39 #if !NET_CF && !MONO && !SILVERLIGHT 40 41 namespace NLog.Targets.Helper 42 { 43 using System; 44 using System.Collections.Generic; 45 using System.Collections.ObjectModel; 46 using System.ComponentModel; 47 using System.Windows; 48 using NLog.Config; 49 using System.Windows.Documents; 50 using System.Windows.Media; 51 using System.Linq; 52 [Target("RichTextBox")] 53 public sealed class WpfRichTextBoxTarget : TargetWithLayout 54 { 55 private int _width = 500; 56 private int _height = 500; 57 private static readonly TypeConverter colorConverter = new ColorConverter(); 58 59 static WpfRichTextBoxTarget() 60 { 61 var rules = new List<WpfRichTextBoxRowColoringRule>() 62 { 63 new WpfRichTextBoxRowColoringRule("level == LogLevel.Fatal", "White", "Red", FontStyles.Normal, FontWeights.Bold), 64 new WpfRichTextBoxRowColoringRule("level == LogLevel.Error", "Red", "Empty", FontStyles.Italic, FontWeights.Bold), 65 new WpfRichTextBoxRowColoringRule("level == LogLevel.Warn", "Orange", "Empty"), 66 new WpfRichTextBoxRowColoringRule("level == LogLevel.Info", "Black", "Empty"), 67 new WpfRichTextBoxRowColoringRule("level == LogLevel.Debug", "Gray", "Empty"), 68 new WpfRichTextBoxRowColoringRule("level == LogLevel.Trace", "DarkGray", "Empty", FontStyles.Italic, FontWeights.Normal), 69 }; 70 71 DefaultRowColoringRules = rules.AsReadOnly(); 72 } 73 74 public WpfRichTextBoxTarget() 75 { 76 WordColoringRules = new List<WpfRichTextBoxWordColoringRule>(); 77 RowColoringRules = new List<WpfRichTextBoxRowColoringRule>(); 78 ToolWindow = true; 79 } 80 81 private delegate void DelSendTheMessageToRichTextBox(string logMessage, WpfRichTextBoxRowColoringRule rule); 82 83 private delegate void FormCloseDelegate(); 84 85 public static ReadOnlyCollection<WpfRichTextBoxRowColoringRule> DefaultRowColoringRules { get; private set; } 86 87 public string ControlName { get; set; } 88 89 public string FormName { get; set; } 90 91 [DefaultValue(false)] 92 public bool UseDefaultRowColoringRules { get; set; } 93 94 [ArrayParameter(typeof(WpfRichTextBoxRowColoringRule), "row-coloring")] 95 public IList<WpfRichTextBoxRowColoringRule> RowColoringRules { get; private set; } 96 97 [ArrayParameter(typeof(WpfRichTextBoxWordColoringRule), "word-coloring")] 98 public IList<WpfRichTextBoxWordColoringRule> WordColoringRules { get; private set; } 99 100 [DefaultValue(true)] 101 public bool ToolWindow { get; set; } 102 103 public bool ShowMinimized { get; set; } 104 105 public int Width 106 { 107 get { return _width; } 108 set { _width = value; } 109 } 110 111 public int Height 112 { 113 get { return _height; } 114 set { _height = value; } 115 } 116 117 public bool AutoScroll { get; set; } 118 119 public int MaxLines { get; set; } 120 121 internal Window TargetForm { get; set; } 122 123 internal System.Windows.Controls.RichTextBox TargetRichTextBox { get; set; } 124 125 internal bool CreatedForm { get; set; } 126 127 protected override void InitializeTarget() 128 { 129 TargetRichTextBox = TargetRichTextBox ?? System.Windows.Application.Current.MainWindow.FindName(ControlName) as System.Windows.Controls.RichTextBox; 130 131 if (TargetRichTextBox != null) return; 132 //this.TargetForm = FormHelper.CreateForm(this.FormName, this.Width, this.Height, false, this.ShowMinimized, this.ToolWindow); 133 //this.CreatedForm = true; 134 135 var openFormByName = System.Windows.Application.Current.Windows.Cast<Window>().FirstOrDefault(x => x.GetType().Name == FormName); 136 if (openFormByName != null) 137 { 138 TargetForm = openFormByName; 139 if (string.IsNullOrEmpty(ControlName)) 140 { 141 // throw new NLogConfigurationException("Rich text box control name must be specified for " + GetType().Name + "."); 142 Trace.WriteLine("Rich text box control name must be specified for " + GetType().Name + "."); 143 } 144 145 CreatedForm = false; 146 TargetRichTextBox = TargetForm.FindName(ControlName) as System.Windows.Controls.RichTextBox; 147 148 if (TargetRichTextBox == null) 149 { 150 // throw new NLogConfigurationException("Rich text box control '" + ControlName + "' cannot be found on form '" + FormName + "'."); 151 Trace.WriteLine("Rich text box control '" + ControlName + "' cannot be found on form '" + FormName + "'."); 152 } 153 } 154 155 if (TargetRichTextBox == null) 156 { 157 TargetForm = new Window 158 { 159 Name = FormName, 160 Width = Width, 161 Height = Height, 162 WindowStyle = ToolWindow ? WindowStyle.ToolWindow : WindowStyle.None, 163 WindowState = ShowMinimized ? WindowState.Minimized : WindowState.Normal, 164 Title = "NLog Messages" 165 }; 166 TargetForm.Show(); 167 168 TargetRichTextBox = new System.Windows.Controls.RichTextBox { Name = ControlName }; 169 var style = new Style(typeof(Paragraph)); 170 TargetRichTextBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; 171 style.Setters.Add(new Setter(Block.MarginProperty, new Thickness(0, 0, 0, 0))); 172 TargetRichTextBox.Resources.Add(typeof(Paragraph), style); 173 TargetForm.Content = TargetRichTextBox; 174 175 CreatedForm = true; 176 } 177 } 178 179 protected override void CloseTarget() 180 { 181 if (CreatedForm) 182 { 183 try 184 { 185 TargetForm.Dispatcher.Invoke(() => 186 { 187 TargetForm.Close(); 188 TargetForm = null; 189 }); 190 } 191 catch 192 { 193 } 194 195 196 197 } 198 } 199 200 protected override void Write(LogEventInfo logEvent) 201 { 202 WpfRichTextBoxRowColoringRule matchingRule = RowColoringRules.FirstOrDefault(rr => rr.CheckCondition(logEvent)); 203 204 if (UseDefaultRowColoringRules && matchingRule == null) 205 { 206 foreach (var rr in DefaultRowColoringRules.Where(rr => rr.CheckCondition(logEvent))) 207 { 208 matchingRule = rr; 209 break; 210 } 211 } 212 213 if (matchingRule == null) 214 { 215 matchingRule = WpfRichTextBoxRowColoringRule.Default; 216 } 217 218 var logMessage = Layout.Render(logEvent); 219 220 if (System.Windows.Application.Current == null) return; 221 222 try 223 { 224 if (System.Windows.Application.Current.Dispatcher.CheckAccess() == false) 225 { 226 System.Windows.Application.Current.Dispatcher.Invoke(() => SendTheMessageToRichTextBox(logMessage, matchingRule)); 227 } 228 else 229 { 230 SendTheMessageToRichTextBox(logMessage, matchingRule); 231 } 232 } 233 catch (Exception ex) 234 { 235 Debug.WriteLine(ex); 236 } 237 238 } 239 240 241 private static Color GetColorFromString(string color, Brush defaultColor) 242 { 243 244 if (color == "Empty") 245 { 246 color = "White"; 247 } 248 249 return (Color)colorConverter.ConvertFromString(color); 250 } 251 252 253 private void SendTheMessageToRichTextBox(string logMessage, WpfRichTextBoxRowColoringRule rule) 254 { 255 System.Windows.Controls.RichTextBox rtbx = TargetRichTextBox; 256 257 var scrolledToEnd = 258 AutoScroll 259 && (TargetRichTextBox.VerticalOffset + TargetRichTextBox.ViewportHeight) >= (TargetRichTextBox.ExtentHeight - .1); 260 261 var tr = new TextRange(rtbx.Document.ContentEnd, rtbx.Document.ContentEnd); 262 tr.Text = logMessage + "\n"; 263 tr.ApplyPropertyValue(TextElement.ForegroundProperty, 264 new SolidColorBrush(GetColorFromString(rule.FontColor, (Brush)tr.GetPropertyValue(TextElement.ForegroundProperty))) 265 ); 266 tr.ApplyPropertyValue(TextElement.BackgroundProperty, 267 new SolidColorBrush(GetColorFromString(rule.BackgroundColor, (Brush)tr.GetPropertyValue(TextElement.BackgroundProperty))) 268 ); 269 tr.ApplyPropertyValue(TextElement.FontStyleProperty, rule.Style); 270 tr.ApplyPropertyValue(TextElement.FontWeightProperty, rule.Weight); 271 272 if (MaxLines > 0) 273 { 274 while (rtbx.Document.Blocks.Count - 1 > MaxLines) 275 { 276 rtbx.Document.Blocks.Remove(rtbx.Document.Blocks.FirstBlock); 277 } 278 } 279 280 if (AutoScroll && scrolledToEnd) 281 { 282 rtbx.ScrollToEnd(); 283 } 284 } 285 } 286 } 287 #endif
WpfRichTextBoxRowColoringRule.cs
1 // 2 // Copyright (c) 2004-2011 Jaroslaw Kowalski <jaak@jkowalski.net> 3 // 4 // All rights reserved. 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions 8 // are met: 9 // 10 // * Redistributions of source code must retain the above copyright notice, 11 // this list of conditions and the following disclaimer. 12 // 13 // * Redistributions in binary form must reproduce the above copyright notice, 14 // this list of conditions and the following disclaimer in the documentation 15 // and/or other materials provided with the distribution. 16 // 17 // * Neither the name of Jaroslaw Kowalski nor the names of its 18 // contributors may be used to endorse or promote products derived from this 19 // software without specific prior written permission. 20 // 21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 // THE POSSIBILITY OF SUCH DAMAGE. 32 // 33 34 using NLog; 35 36 #if !NET_CF && !MONO && !SILVERLIGHT 37 38 namespace NLog.Targets .Helper 39 { 40 using System.ComponentModel; 41 using System.Windows; 42 using NLog.Conditions; 43 using NLog.Config; 44 45 [NLogConfigurationItem] 46 public class WpfRichTextBoxRowColoringRule 47 { 48 static WpfRichTextBoxRowColoringRule() 49 { 50 Default = new WpfRichTextBoxRowColoringRule(); 51 } 52 53 public WpfRichTextBoxRowColoringRule() 54 : this(null, "Empty", "Empty", FontStyles.Normal, FontWeights.Normal) 55 { 56 } 57 58 public WpfRichTextBoxRowColoringRule(string condition, string fontColor, string backColor, FontStyle fontStyle, FontWeight fontWeight) 59 { 60 Condition = condition; 61 FontColor = fontColor; 62 BackgroundColor = backColor; 63 Style = fontStyle; 64 Weight = fontWeight; 65 } 66 67 public WpfRichTextBoxRowColoringRule(string condition, string fontColor, string backColor) 68 { 69 Condition = condition; 70 FontColor = fontColor; 71 BackgroundColor = backColor; 72 Style = FontStyles.Normal; 73 Weight = FontWeights.Normal; 74 } 75 76 public static WpfRichTextBoxRowColoringRule Default { get; private set; } 77 78 [RequiredParameter] 79 public ConditionExpression Condition { get; set; } 80 81 [DefaultValue("Empty")] 82 public string FontColor { get; set; } 83 84 [DefaultValue("Empty")] 85 public string BackgroundColor { get; set; } 86 87 public FontStyle Style { get; set; } 88 89 public FontWeight Weight { get; set; } 90 91 public bool CheckCondition(LogEventInfo logEvent) 92 { 93 return true.Equals(Condition.Evaluate(logEvent)); 94 } 95 } 96 } 97 #endif
WpfRichTextBoxWordColoringRule.cs
// // Copyright (c) 2004-2011 Jaroslaw Kowalski <jaak@jkowalski.net> // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * Neither the name of Jaroslaw Kowalski nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. // using System.ComponentModel; using System.Text.RegularExpressions; using System.Windows; using NLog.Config; #if !NET_CF && !MONO && !SILVERLIGHT namespace NLog.Targets .Helper { [NLogConfigurationItem] public class WpfRichTextBoxWordColoringRule { private Regex compiledRegex; public WpfRichTextBoxWordColoringRule() { FontColor = "Empty"; BackgroundColor = "Empty"; } public WpfRichTextBoxWordColoringRule(string text, string fontColor, string backgroundColor) { Text = text; FontColor = fontColor; BackgroundColor = backgroundColor; Style = FontStyles.Normal; Weight = FontWeights.Normal; } public WpfRichTextBoxWordColoringRule(string text, string textColor, string backgroundColor, FontStyle fontStyle, FontWeight fontWeight) { Text = text; FontColor = textColor; BackgroundColor = backgroundColor; Style = fontStyle; Weight = fontWeight; } public string Regex { get; set; } public string Text { get; set; } [DefaultValue(false)] public bool WholeWords { get; set; } [DefaultValue(false)] public bool IgnoreCase { get; set; } public FontStyle Style { get; set; } public FontWeight Weight { get; set; } public Regex CompiledRegex { get { if (compiledRegex == null) { string regexpression = Regex; if (regexpression == null && Text != null) { regexpression = System.Text.RegularExpressions.Regex.Escape(Text); if (WholeWords) { regexpression = "\b" + regexpression + "\b"; } } RegexOptions regexOptions = RegexOptions.Compiled; if (IgnoreCase) { regexOptions |= RegexOptions.IgnoreCase; } compiledRegex = new Regex(regexpression, regexOptions); } return compiledRegex; } } [DefaultValue("Empty")] public string FontColor { get; set; } [DefaultValue("Empty")] public string BackgroundColor { get; set; } } } #endif
3. xaml 定义控件
1 <RichTextBox 2 x:Name="loggerTextBox" 3 Grid.RowSpan="5" 4 Grid.Column="1" 5 Grid.ColumnSpan="2" 6 Margin="5" 7 HorizontalScrollBarVisibility="Visible" 8 VerticalScrollBarVisibility="Visible"> 9 <!--<FlowDocument PageWidth="2000"> 10 <Paragraph 11 Margin="0" 12 FontFamily="Lucida Console" 13 FontSize="11" /> 14 </FlowDocument>--> 15 </RichTextBox>
4. 初始化Logger
static public Logger? logger;
1 private void Window_Loaded(object sender, RoutedEventArgs e) 2 { 3 InitUILogging(LogLevel.Info); 4 logger = LogManager.GetCurrentClassLogger(); 5 } 6 private void InitUILogging(LogLevel logLevel) 7 { 8 // log layout format 9 //var layout = "${message} ${exception:separator=\r\n:format=message,type,method,stackTrace:maxInnerExceptionLevel=10:innerExceptionSeparator=\r\n:innerFormat=message,type,method,stackTrace}"; 10 //var layout = "${date:format=HH\\:MM\\:ss} ${logger} ${message}"; 11 //var layout = "${time:}|${threadid:padding=3}|${level:uppercase=true:padding=-5}|${logger:padding=-15}|${message}|${exception}"; 12 var layout = "${time:}|${threadid:padding=3}|${level:uppercase=true:padding=-5}|${message}|${exception}"; 13 // create rich text box target 14 var uiTarget = new WpfRichTextBoxTarget 15 { 16 Layout = layout, 17 TargetRichTextBox = loggerTextBox, 18 UseDefaultRowColoringRules = true, 19 AutoScroll = true, 20 MaxLines = 250, 21 }; 22 var asyncWrapper = new AsyncTargetWrapper 23 { 24 Name = "logEidt", 25 WrappedTarget = uiTarget 26 }; 27 var config = LogManager.Configuration ?? new LoggingConfiguration(); 28 29 config.AddTarget("UI", uiTarget); 30 config.LoggingRules.Add(new LoggingRule("*", logLevel, asyncWrapper)); 31 32 LogManager.Configuration = config; 33 }
5. 如果需要输出日志到文件加可添加输出到文件夹的配置(第五步32行前即可)
// 创建文件输出目标 var fileTarget = new FileTarget(); fileTarget.FileName = "${basedir}/log/${date:format=yyyy-MM-dd-HH}.txt"; fileTarget.Layout = "${longdate} ${level} ${message}"; // 添加文件输出目标到配置中 config.AddTarget("file", fileTarget); config.LoggingRules.Add(new LoggingRule("*", logLevel, fileTarget));
6. 输出日志
1 MainWindow.logger?.Info(msg);
参考来源:
WpfRichTextBoxTarget, NLog.Targets C# (CSharp) Code Examples - HotExamples
BitSharp/BitSharp.Client at master · hoangduit/BitSharp (github.com)