NLog Only shows in RichTextBox the first time - nlog

I have a winform with a Rich Text box on it (LogView) and my configuration is set to log to that text box. When I open the LogView from another form, then start logging everything works wonderful the first time. When I try and open the LogView again, for a second run, nothing shows in the Rich Text box.
caller code
private void button1_Click(object sender, EventArgs e)
{
LogView frm = new LogView();
frm.Show(this);
NLog.Logger logger = NLog.LogManager.GetLogger("");
logger.Info("FORM 2 LOG");
}
LogView code
private void LogView_Load(object sender, EventArgs e)
{
try
{
var logger = LogManager.GetLogger("");
}
catch (Exception exception)
{
Console.WriteLine(exception);
} //logger.Debug("test");
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 100); i++)
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
var logger = LogManager.GetLogger("");
logger.Debug("i = " + i.ToString());
}
}
interestingly, doing it with this approach even the built-in NLog form doesn't work the second time.
I'm using version 2.0.1.0
.NET 4.5
Configuration
<nlog autoReload="true">
<targets>
<target name="richTextBox"
type="RichTextBox"
controlName="richTextBox1"
formName="LogView"
useDefaultRowColoringRules="false"
layout="${date} ${level} ${logger} ${message}"/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="richTextBox"/>
</rules>
</nlog>

This is caused by the way NLog finds the RichTextBox.
When the configuration is first loaded (the first time a Logger is required), the RichTextBox target is initialized, meaning the RichTextBox is located and the target settings are stored in a cache. This means the reference would be wrong the second time the form is loaded.
This is related to the issue described at https://github.com/NLog/NLog/issues/133.
UPDATE:
You should be able to clear the cache by calling:
LogManager.Configuration.ReconfigExistingLoggers();
But remember this is an expensive operation

Related

Accessing BeginRequest/EndRequest events in HTTPModule for IIS 7.5 with ApplicationPool in Managed Pipeline Integrated mode

Environment: IIS 7.5 using an AppPool set to Managed Pipeline Integrated mode
I'm trying to log the certain state of the HTTP Request before it enters the pipeline and the HTTP response as it exists, specifically a few form values and the cookie collection.
I'm doing this in an HTTPModule
public class MyLoggingModule : IHttpModule
{
private static readonly log4net.ILog _logger =
log4net.LogManager.GetLogger(typeof(MyLoggingModule));
public void Init(HttpApplication context)
{
context.BeginRequest += LogRequestState;
context.EndRequest += LogResponseState;
}
}
private void LogRequestState(object sender, EventArgs e)
{
//Invokes...
HttpContext.Current.Server.UrlDecode
HttpContext.Current.Request.Url
HttpContext.Current.Request.Form.AllKeys
HttpContext.Current.Request.Cookies
HttpContext.Current.Response.Cookies.AllKeys
_logger.Debug("...");
}
private void LogResponseState(object sender, EventArgs e)
{
// Invokes ...
FederatedAuthentication.SessionAuthenticationModule.CookieHandler.Name
HttpContext.Current.Response.Cookies.AllKeys
_logger.Debug("...");
}
Web.Config settings
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="false">
<add name="MyLoggingModule" type="MyApp.Api.HttpModules.MyLoggingModule, MyApp.Api"/>
</modules>
<urlCompression doStaticCompression="true" doDynamicCompression="true"/>
</system.webServer>
I will get a runtime error only available in the Application Logs (try/catch does not catch this exception):
Exception information:
Exception type: NullReferenceException
Exception message: Object reference not set to an instance of an object.
at System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error)
at System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb)
at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context)
It seems to be very similar to the issue raised here: HttpModule.Init - safely add HttpApplication.BeginRequest handler in IIS7 integrated mode
That issue has a solution along the lines of
public class MyLoggingModule : IHttpModule
{
public override void Init()
{
base.Init();
lock (_initialisationLockObject)
{
context.BeginRequest -= LogRequestState;
context.BeginRequest += LogRequestState;
context.EndRequest -= LogResponseState;
context.EndRequest += LogResponseState;
}
}
}
Given that the post is over 8 years old and the solution was not accepted and criticized on other posts is there a way to achieve this now?

How to make FluentFTP to log to log4net log?

I couldn't find any information on how to do it. Basically FluentFTP is using System.Diagnostics to log their messages.
FluentFtp expose the following static method:
FtpTrace.AddListener(TraceListener listener);
However I don't know if there is any way to implement (or use existing implementation, which?) TraceListener in the way it relays everything to log4net engine.
Any hints or ideas?
Thanks, Radek
You can attach a listener to the OnLogEvent method that FluentFTP exposes.
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static void UploadFTP(FileInfo localFile, string remoteFileLocation, string remoteServer, NetworkCredential credentials)
{
FtpClient client = new FtpClient(remoteServer, credentials);
client.RetryAttempts = 3;
client.OnLogEvent = OnFTPLogEvent;
client.Connect();
if (!client.UploadFile(localFile.FullName, remoteFileLocation, FtpExists.Overwrite, false, FtpVerify.Retry | FtpVerify.Throw))
{
throw new Exception($"Could not Upload File {localFile.Name}. See Logs for more information");
}
}
private static void OnFTPLogEvent(FtpTraceLevel ftpTraceLevel, string logMessage)
{
switch (ftpTraceLevel)
{
case FtpTraceLevel.Error:
Log.Error(logMessage);
break;
case FtpTraceLevel.Verbose:
Log.Debug(logMessage);
break;
case FtpTraceLevel.Warn:
Log.Warn(logMessage);
break;
case FtpTraceLevel.Info:
default:
Log.Info(logMessage);
break;
}
}
The method OnFTPLogEvent will be called every-time the OnLogEvent action will be called allowing you to extend any logging you have already built into your application.
Basically FluentFTP is using System.Diagnostics.TraceListener so in order to make it logging to your log4net log you need to write your own simple class that would redirect logs to log4net logger. Like the following:
using System.Diagnostics;
using log4net;
namespace YourApp.Logging
{
public class Log4NetTraceListener : TraceListener
{
private readonly ILog _log;
public Log4NetTraceListener(string provider)
{
_log = LogManager.GetLogger(provider);
}
public override void Write(string message)
{
if(_log == null)
return;
if(!string.IsNullOrWhiteSpace(message))
_log.Info(message);
}
public override void WriteLine(string message)
{
if(_log == null)
return;
if (!string.IsNullOrWhiteSpace(message))
_log.Info(message);
}
}
}
Then, in your app.config file add the following entry:
<system.diagnostics>
<trace autoflush="true"></trace>
<sources>
<source name="FluentFTP">
<listeners>
<clear />
<add name="FluentLog" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="FluentLog" type="YourApp.Logging.Log4NetTraceListener, YourApp" initializeData="FluentLog" />
</sharedListeners>
</system.diagnostics>
That should enable FluentFtp logs and merge it with your application log4net log.

SemanticLogging throws Exception in Command Processing for EventSource

On some machines (probably only on Windows 7 and 2008 R2 but not on Windows 10) I have problem using SemanticLogging. When I run it I'm receiving fallowing output:
Event Trace Session prefix: Microsoft-SemanticLogging-Etw
Sink name: ConsoleEventSink
Event sources:
Name: 8943bf09-be18-551a-efe5-612ee62ded5e
Performance, Level: LogAlways, MatchAnyKeyword: None
Sink name: PerformaceSINK
Event sources:
Name: 8943bf09-be18-551a-efe5-612ee62ded5e
Performance, Level: LogAlways, MatchAnyKeyword: None
Service started.
Press enter to end ...
ERROR: Exception in Command Processing for EventSource Performance: Object
reference not set to an instance of an object.;
All that happens in specific scenerio:
I'm starting process which writes Events
then I run SemanticLogging-svc.exe -c
after few moments error occurs
But when I change order and first start SemanticLogging-svc.exe and after that I run "event writer", everything goes how it should.
But when I setup all as described in first scenerio and after error I will try to collect data using PerfView magic happens and SemanticLogging starts collecting data.
Using PerfView I have checked Microsoft-SemanticLogging-Etw source but nothing there.
SemanticLogging-svc.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns="http://schemas.microsoft.com/practices/2013/entlib/semanticlogging/etw"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.microsoft.com/practices/2013/entlib/semanticlogging/etw SemanticLogging-svc.xsd">
<traceEventService />
<sinks>
<consoleSink name="ConsoleEventSink">
<sources>
<eventSource name="PerformanceEventSource" level="LogAlways" />
</sources>
<customEventTextFormatter type="ServiceTelemetry.EventFormatter.CsvEventFormatter, ServiceTelemetry"/>
</consoleSink>
<rollingFlatFileSink
name="PerformanceEventSourceSINK"
fileName=".\logs\%ComputerName%_Performance.log"
rollFileExistsBehavior="Increment"
rollInterval="Midnight"
timeStampPattern="yyyyMMdd">
<sources>
<eventSource name="PerformanceEventSource" level="LogAlways" />
</sources>
<customEventTextFormatter type="ServiceTelemetry.EventFormatter.CsvEventFormatter, ServiceTelemetry"/>
</rollingFlatFileSink>
</sinks>
</configuration>
EventFormatter:
namespace ServiceTelemetry.EventFormatter
{
public class CsvEventFormatter : IEventTextFormatter
{
public void WriteEvent(EventEntry eventEntry, TextWriter writer)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < eventEntry.Payload.Count; i++)
{
sb.AppendFormat("{0};", eventEntry.Payload[i]);
}
writer.WriteLine(sb.ToString());
}
}
}
EventSource:
namespace ServiceTelemetry.EventSources
{
[EventSource(Name = "Performance")]
public sealed class PerformanceEventSource : EventSource
{
[Event(1, Level = EventLevel.LogAlways, Task = TaskCodes.GetResource, Opcode = OperationCodes.Compleated)]
public void GetResourceSuccess(string Session, string ResourceName, long ElapsedMilliseconds)
{
if (IsEnabled())
{
WriteEvent(1, Session, ResourceName, ElapsedMilliseconds);
}
}
public static PerformanceEventSource Log = new PerformanceEventSource();
private PerformanceEventSource()
{
}
}
}
It was necessary to install manifest first, then you can start your EventWriter and you can collect data any time you start SematicLogger.
Unfortunately system throws error but for now I'm good with that.
EventSource .net 4.0 GenerateManifest

Sending log to remote MSMQ

I installed NLog version 2 and sending to a remote MSMQ is not working. Do I have the config setup properly?
<nlog autoReload="true" xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target xsi:type="MSMQ" name="MSMQLog" useXmlEncoding="true" queue="FormatName:DIRECT=OS:server01\private$\test_log" recoverable="true" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="MSMQLog" />
</rules>
</nlog>
I installed MSMQ on my local box and the server I'm sending the message too. NLog doesn't throw any exceptions (they are turned on). I don't see anything in the outgoing mailbox on my local machine.
I am able to send to the queue by using the following code.
using (var queue = new MessageQueue(#"FormatName:DIRECT=OS:server01\private$\tasi_log"))
{
var message = new Message("TEST");
message.Formatter = new BinaryMessageFormatter();
queue.Send(message);
}
Does NLog work with remote queues?
So I tried sending to a public queue and it still didn't work using NLog. So, I looked at the NLog.Extended source code and I found this method.
protected override void Write(LogEventInfo logEvent)
{
if (this.Queue == null)
{
return;
}
string queue = this.Queue.Render(logEvent);
if (!MessageQueue.Exists(queue))
{
if (this.CreateQueueIfNotExists)
{
MessageQueue.Create(queue);
}
else
{
return;
}
}
using (MessageQueue mq = new MessageQueue(queue))
{
Message msg = this.PrepareMessage(logEvent);
if (msg != null)
{
mq.Send(msg);
}
}
}
I commented out the following if statement and it now sends to remote queues. Can someone verify this? Is this a bug, or am I missing something?
if (!MessageQueue.Exists(queue))
{
if (this.CreateQueueIfNotExists)
{
MessageQueue.Create(queue);
}
else
{
return;
}
}

How can I use log4net in WF4?

I've created a simple WF4 console app and set up log4net identically to my other apps. However, when I fire up the console and use the ILog object inside WF4 (I actually pass it into the workflow), no information is presented using my ColoredConsoleAppender. What am I doing wrong?
Workflow trace output is written to trace listeners and as far as I am aware log4net doesn't log the output written to a trace listener by default. I am no expert on log4net so there might be an easier way but creating a TraceListener that just passes all data on to log4net is not hard, the following code worked just fine in a quick test.
public class Log4netTraceListener : TraceListener
{
private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, params object[] data)
{
base.TraceData(eventCache, source, eventType, id, data);
}
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
{
var logger = LogManager.GetLogger(source);
switch (eventType)
{
case TraceEventType.Critical:
logger.Fatal(data);
break;
case TraceEventType.Error:
logger.Error(data);
break;
case TraceEventType.Information:
logger.Info(data);
break;
case TraceEventType.Verbose:
logger.Debug(data);
break;
case TraceEventType.Warning:
logger.Warn(data);
break;
default:
base.TraceData(eventCache, source, eventType, id, data);
break;
}
}
public override void Write(string message)
{
_log.Info(message);
}
public override void WriteLine(string message)
{
_log.Info(message);
}
Next you need to make sure the activity trace information is send to this TraceListener using the following code in you app.config.
<system.diagnostics>
<sources>
<source name="System.Activities"
switchValue="Verbose">
<listeners>
<add name="Test"
type="WorkflowConsoleApplication17.Log4netTraceListener, WorkflowConsoleApplication17"/>
</listeners>
</source>
</sources>
</system.diagnostics>
Create an Extension for your workflow that your activities can get from the context.
var wf = new WorkflowApplication(myActivity);
var log = new MyLogForNetExtensionLol();
wf.Extensions.Add(log);
then, within the activity:
var log = context.GetExtension<ILog>();
log.Write("Worked!");

Resources