Sending log to remote MSMQ - nlog

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;
}
}

Related

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.

how do i get data from azure queue message (service bus)

I need to get data from service bus from my console application, instead of my data I've got System.UnauthorizedAccessException error 401
I've got 2 static readonly string that I don't know how to use
You may require one or both of the services below depending on the level of detail that you need-
//sample usage string briefingDetailsByIdURI = string.Format(Constants.BRIEFING_DETAILS_ID_URI, brfId);
public static readonly string ID_URI = "https://trtrtrtr.servicebus.windows.net/trtrtr/trttrttr/{0}";
//sample usage - string URI = string.Format(Constants.sdgsdgg, sgsgsg, sgsgsg);
public static readonly string DETAIL_ID_URI = "https://trtrtrtr.servicebus.windows.net/trtrtr/trrtrttr/{0}/{1}";
I just went in app.config and put this with the right namespace and password
<appSettings>
<!-- Service Bus specific app setings for messaging connections -->
<add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://<namespace>.servicebus.windows.net/;
SharedAccessKeyName=Root stManageSharedAccessKey;SharedAccessKey=<paasword> />
after that I went in my console program.cs
Console.Title = "Receiver2";
// Creating the topic if it does not exist already using the service bus connection string stored in the app.config file
string connectionString =
CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
//appSettings dans appsettings getSettings()
//connection au service bus
var namespaceManager =
NamespaceManager.CreateFromConnectionString(connectionString);
//verification si queue existe
if (!namespaceManager.QueueExists("CommentaireQueue"))
{
namespaceManager.CreateQueue("CommentaireQueue");
}
QueueClient client = QueueClient.Create("CommentaireQueue");
Console.WriteLine("test console");
//boucle infini pour recevoir tous les messages
while (true)
{
var message = client.Receive();
if (message != null)
{
var comm = message.GetBody<string>();
string myString = comm.Contenu;
try
{
Console.WriteLine(myString);
}
finally
{
//enlever le message de la queue
message.Complete();
}
}
}
}
This is what I use to connect to the queue
QueueClient _client;
var connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
if (!namespaceManager.QueueExists(QueueName))
{
namespaceManager.CreateQueue(QueueName);
}
_client = QueueClient.CreateFromConnectionString(connectionString, QueueName);
Instead of running a while loop. You could be using OnMessage() like this:
_client.OnMessage(message =>
{
try
{
//do logic
}
catch (Exception e)
{
message.Abandon();
throw new Exception(e.Message);
}
});
Make sure the connections string is set correct in your Azure ServiceConfiguration:
<?xml version="1.0" encoding="utf-8"?>
<ServiceConfiguration serviceName="App" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="4" osVersion="*" schemaVersion="2015-04.2.6">
<Role name="App">
<Instances count="1" />
<ConfigurationSettings>
<Setting name="Microsoft.ServiceBus.ConnectionString" value="THE CONNECTION STRING" />
</ConfigurationSettings>
</Role>
</ServiceConfiguration>

Web Api on Azure shows no error detail using 'return InternalServerError(ex)'

My Web Api when run locally (in Release mode) will return any errors in this format:
{
"Message": "An error has occurred.",
"ExceptionMessage": "No text specified",
"ExceptionType": "System.Exception",
"StackTrace": null
}
But after deployment/publish to an Azure VM, only this remains:
{
"Message": "An error has occurred."
}
API code:
try
{
var msg = ...
new MessageService().SaveMessage(msg)); // <-- does some checks; may throw.
return Ok();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
I'd like it to be more detailed on Azure, like the local result.
Can this be achieved, and if so, how?
I already (temporarily) removed <compilation xdt:Transform="RemoveAttributes(debug)" /> from the <system.web> part of Web.Release.config, and then re-deployed, but that made no difference.
Or am I using the wrong approach/pattern?
Obviously technical details should be limited, but right now we get no details at all.
You could try adding the following to your Global.asax:
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
Note: I wouldn't recommend that you keep this setting on in a production environment.
If instead you use
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Default;
then you can use the system.webServer error switch e.g.
<system.webServer>
<httpErrors errorMode="Detailed" existingResponse="PassThrough">
</httpErrors>
</system.webServer>
Note the existingResponse attribute to preserve the error message.
I had the same problem, the post is three years old, things have changed a little. If you setup a new Azure Mobile App with Visual Studio 2017 there is no longer a Global.asax.cs. I searched for hours, where to put this IncludeErrorDetailPolicy. It won't work without that setting.
You do it in your Startup.MobileApp.cs:
public partial class Startup
{
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
Don't forget, in your Web.config you also need to set:
<system.webServer>
<httpErrors errorMode="Detailed" existingResponse="PassThrough">
</httpErrors>
</system.webServer>
Don't use that for production environment!
For Web API 2, you can implement a custom IExceptionLogger that utilizes Azure Application Insights. Something like this:
using Microsoft.ApplicationInsights;
using System.Web.Http.ExceptionHandling;
namespace Your.Namespace.Here
{
public class TelemetryExceptionLogger : ExceptionLogger
{
private readonly TelemetryClient telemetryClient;
public TelemetryExceptionLogger(TelemetryClient telemetryClient)
{
this.telemetryClient = telemetryClient;
}
public override void Log(ExceptionLoggerContext context)
{
if (context != null && context.Exception != null)
{
telemetryClient.TrackException(context.Exception);
}
base.Log(context);
}
}
}
Then you need to register it with Web API:
using Microsoft.ApplicationInsights;
using System.Web.Http;
using System.Web.Http.ExceptionHandling;
using Your.Namespace.Here;
namespace Some.Other.Namespace.Or.The.Same
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// --- Normal Web API configuration here ---
config.Services.Add(typeof(IExceptionLogger), new TelemetryExceptionLogger(new TelemetryClient()));
}
}
}
For this to work, you will need to have set up Application Insight in Azure and for your VS project, but that is a story for another time :)
For more information, see Application Insights: Exception Telemetry
I have a scenario with the same error, and the problem was a copy&paste in the route header attribute of a method. I have the same route for two methods
[Route("test/Method1")]
public IHttpActionResult Method1(){...}
[Route("test/Method1")]
public IHttpActionResult Method2(){...}
Check the new methods and Routes added.

Azure diagnostics multiple AppDomain issues

I've bumped into a following problem with Azure Diagnostic Monitor:
When I create a new AppDomain in OnStart() event in WorkerRole entry point the diagnostics works only in the parent AppDomain. I've tried initializing Diagnostics Monitor in the child AppDomain but it doesn't help. (Traces are collected only from the parent domain)
Example repro code:
public class WorkerRole : RoleEntryPoint
{
public override void Run()
{
// This is a sample worker implementation. Replace with your logic.
InitializeDiagnostics();
Trace.TraceInformation("WorkerRole1 entry point called", "Information");
while (true)
{
Thread.Sleep(10000);
Trace.TraceInformation("Parent domain working", "Information");
}
}
public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;
InitializeDiagnostics();
var setup = new AppDomainSetup();
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
setup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
var newDomain = System.AppDomain.CreateDomain("NewApplicationDomain",null, setup);
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.GlobalAssemblyCache))
{
newDomain.Load(assembly.GetName());
}
newDomain.Load(typeof (Worker).Assembly.FullName);
var worker = newDomain.CreateInstanceAndUnwrap(this.GetType().Assembly.FullName, typeof (Worker).FullName) as Worker;
worker.DoWork();
return base.OnStart();
}
public void InitializeDiagnostics()
{
var roleInstanceDiagnosticManager = new RoleInstanceDiagnosticManager(RoleEnvironment.GetConfigurationSettingValue("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"),
RoleEnvironment.DeploymentId,
RoleEnvironment.CurrentRoleInstance
.Role.Name,
RoleEnvironment.CurrentRoleInstance.Id);
var dmc = roleInstanceDiagnosticManager.GetCurrentConfiguration();
var dictionaryConfiguration = new DirectoryConfiguration();
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", dmc);
}
}
public class Worker : MarshalByRefObject
{
public void DoWork()
{
new Task(() =>
{
while (true)
{
Thread.Sleep(1000);
Trace.TraceInformation(AppDomain.CurrentDomain.FriendlyName + " Worker working...", "Information");
}
}).Start();
}
}
}
App config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace>
<listeners>
<add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="AzureDiagnostics">
<filter type="" />
</add>
</listeners>
</trace>
</system.diagnostics>
</configuration>
Expected output:
Lots of logged messages:
"{Domain Name} Wokrer working..."
Some
"Parent domain working"
Actual output:
"Parent domain working"
I'm using Azure SDK 2.0. Have any of you came across a similar issue ?
Ok, finally solved it. Upgrading Azure SDK to 2.3 did the thing... It's interesting that messages still doesn't appear in Compute emulator console, but after upgrade they are correctly logged to WADLog table.

NLog Only shows in RichTextBox the first time

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

Resources