NLog / NLog.Windows.Forms

NLog targets specific for Windows.Forms
BSD 3-Clause "New" or "Revised" License
44 stars 26 forks source link

Form hanging when RichTextBox target nonstop logging #52

Open AlexisChung opened 5 years ago

AlexisChung commented 5 years ago

NLog version: (e.g. 4.6.8)

Platform: .Net 4.0

Current NLog config (xml or C#, if relevant)

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true">

  <targets async="true">
    <target name="TraceTarget" xsi:type="File"
            layout="${level:uppercase=true:padding=-5} ${date:format=yyyy\-MM\-dd\ HH\:mm\:ss\.fff} - ${message}"
            fileName="${basedir}/Log/${date:yyyyMMdd}_Trace.txt"
            maxarchivefiles="1"
            />
  </targets>

  <rules>
    <logger ruleName="Trace" name="Trace" minlevel="Debug" writeTo="TraceTarget" />
  </rules>
</nlog>

code

 RichTextBoxTarget _target = new RichTextBoxTarget
            {
                Name = targetName,
                Layout = "[${level:uppercase=true:padding=-5}]" +
                          "${date:format=yyyy\\-MM\\-dd\\ HH\\:mm\\:ss\\.fff} " +
                          "${message} " +
                          "${exception:innerFormat=tostring:maxInnerExceptionLevel=10:separator=,:format=tostring}",
                ControlName = richTextBoxName,
                FormName = formName,
                AutoScroll = true,
                MaxLines = Convert.ToInt32(CommonFunction.ReadSetting("RichTextBoxMaxLines")),
                UseDefaultRowColoringRules = true,
                CreatedForm = false,
                AllowAccessoryFormCreation = false,
            };
AlexisChung commented 5 years ago

Hi all, i found the problem, in RichTextBoxTarget.cs

textbox.BeginInvoke(new DelSendTheMessageToRichTextBox(SendTheMessageToRichTextBox), logMessage, rule, logEvent);

i changed BeginInvoke--->Invoke. and it work perfectly.

304NotModified commented 5 years ago

Isn't the whole thing much slower with Invoke?

snakefoot commented 5 years ago

Sounds like Control.Invoke becomes a throttle, where the writing waits for the GUI to keep up:

Maybe this change avoids filling up the window message-pump. But I think it should be optional. Think bulk insert would probably also help.

mas-co commented 4 years ago

This is the first time I used the NLog.Windows.Forms in a project and I ran into the same problem with the RichTextBox thread hanging when I barraged the logger.

Can anyone direct me toward a guide to manually compile NLog.Windows.Forms and manually add it to a solution with multiple projects (I am using .NET 4.7.2)? I was going to check the suggestion by AlexisChung).

Using NLog 4.6.8 with NLog.Windows.Forms 4.3.0

Below is a test I made to blast the logger. I am assuming that the RichTextBox target is supposed to be wrapped by an async wrapper like the regular NLog.Targets targets. (Both wrapped and unwrapped hang the UI).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var config = NLog.LogManager.Configuration;
            if(config == null) { config = new NLog.Config.LoggingConfiguration();}

            var rtbTarget = new NLog.Windows.Forms.RichTextBoxTarget()
            {
                Name = "rtbTarget",
                FormName = this.Name,
                ControlName = rtbLogger.Name
            };

            var rtbTargetWrapper = new NLog.Targets.Wrappers.AsyncTargetWrapper()
            {
                Name = "rtbTargetWrapper",
                WrappedTarget = rtbTarget
            };

            NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(rtbTargetWrapper, NLog.LogLevel.Trace);

            logger.Trace("Logger started.");
            KillTheWabbit(1000);
        }

        private void KillTheWabbit(int count)
        {
            for (int i = 0; i < count; i++)
            {
                logger.Trace("Kill the wabbit: {i}", i);
            }
        }
    }
}
mas-co commented 4 years ago

Isn't the whole thing much slower with Invoke?

Yes, but the form doesn't hang. I ran 3 cases with 10000 iterations:

  1. Original the RichTextBox target: base time (140335 ms) (form "hangs")
  2. Invoke Only RichTextBox target: 31% more time (184428 ms) (form is responsive)
  3. Original RichTextBox target with BufferingWrapper 30% more time (182938 ms) (form "hangs")
mpleis commented 1 year ago

Saturating log events to the RichTextBox is consuming all the bandwidth and leaving other 'Main Thread' UI tasks waiting to get it's share.