Log to a channel in Microsoft Teams with NLog - c#

Objective
Log the errors to Microsoft Teams with NLog from a console program.
Issue
Was able to log to the console, but not to Teams
Code
Config:
<targets>
<target xsi:type="Console" name="console"/>
<target xsi:type="WebService"
name="microsoft-teams"
url="https://outlook.office.com/webhook/abcd"
protocol='JsonPost'
encoding='UTF-8'
includeBOM='false' >
</target>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="console" />
<logger name='*' writeTo='microsoft-teams' />
</rules>
Logging code:
private static Logger logger = LogManager.GetCurrentClassLogger();
public static void Main(string[] args) {
logger.Error("{'text':'test'}");
logger.Fatal("Sample fatal error message");
}
The console target displayed the errors very well. But the Teams channel didn't have log, the nlog-internal.log showed
System.Net.WebException: The remote server returned an error: (400) Bad Request.
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
Any insight would be greatly appreciated!

You need at least send some parameters, see example webserver target:
<target type='WebService'
name='ws'
url='http://localhost:1234/logme'
protocol='HttpPost'
encoding='UTF-8' >
<parameter name='param1' type='System.String' layout='${message}'/>
<parameter name='param2' type='System.String' layout='${level}'/>
</target>
If this still isn't working, I would recommend checking the call with Fiddler

I know it is a little bit late, but you can try the NLog extension I created.
https://github.com/jedipi/NLog.Targets.MicrosoftTeams

Related

Missing Details when Unable to resolve service when using NLOG

I just finished struggling with a generic error in AspNetCore3.1. The error simply reads this:
2019-12-11 21:52:46.6862 Microsoft.AspNetCore.Server.Kestrel
Connection id "0HLRUMQV9RNJI", Request id "0HLRUMQV9RNJI:00000001": An
unhandled exception was thrown by the application.
After much pain, I found that it was because I missing a registration of a service:
public void ConfigureServices(IServiceCollection services)
{
// This service was missing
services.AddTransient<IThingService, ThingService>();
services.AddControllers();
}
So, now, I'm on a quest to find out why I didn't get any details on what the problem was. I created a new Core 3.1 app from the Visual Studio templates. I installed NLog.Web.AspNetCore and added an nlog.config file.
<?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"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="true"
internalLogLevel="Off" internalLogFile="c:\temp\logs\nlog-internal.log">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<targets async="true">
<target xsi:type="ColoredConsole" name="c" layout="${longdate} ${logger} ${message}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="c" />
</rules>
</nlog>
AppSettings:
{
// I removed the logging section completely so that NLOG can control log levels
"AllowedHosts": "*"
}
I also added the following code to program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseIISIntegration()
.ConfigureLogging((hostingContext, loggingBuilder) =>
{
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.UseStartup<Startup>()
.UseNLog();
});
}
So then, I removed the registration of IThingService, and I started getting the same generic, useless error:
"2019-12-11 21:52:46.6862 Microsoft.AspNetCore.Server.Kestrel
Connection id "0HLRUMQV9RNJI", Request id "0HLRUMQV9RNJI:00000001": An
unhandled exception was thrown by the application."
After some tinkering, I found that I commented out the line in program.cs, "loggingBuilder.ClearProviders();" I started getting actual useful error messages:
System.InvalidOperationException: Unable to resolve service for type
'WebApplication3.Controllers.IThingService' while attempting to
activate 'WebApplication3.Controllers.WeatherForecastController'.
But, it's not NLog doing the logging, it's the default Core 3 logging.
Setup instructions for NLog can be found here: https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3
So, now for the question, why does ClearProviders() make nlog work, but lose the details in my error messages? It seems to only be logs that would take place down in the pipeline, not at the controller/domain level.
I think the special detail you are looking for is ${exception:format=tostring}. Your current layout is this:
<target xsi:type="ColoredConsole" name="c" layout="${longdate} ${logger} ${message}" />
Try changing into this:
<target xsi:type="ColoredConsole" name="c" layout="${longdate} ${logger} ${message}${exception:format=tostring}" />
You might also consider including ${level} in Layout (Unless coloring is enough).

NLog for asp.net core is not filtering Microsoft logs

I have the following nlog.config file in ASP.NET Core 2.1 project. However, it's logging messages from every logger (including Microsoft logger) to console.
<?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"
internalLogLevel="Error"
internalLogFile="${specialfolder:folder=UserProfile}\nlog\internal-nlog.txt">
<variable name="fullLayout" value="${shortdate} [${uppercase:${level}}] ${logger}: ${message} ${exception:format=tostring} url: ${aspnet-request-url}" />
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!-- write to console -->
<target name="console" xsi:type="ColoredConsole" layout="${fullLayout}" />
<!-- write to file -->
<target xsi:type="File"
name="allfile"
fileName="${defaultDirectory}/test-api-all.log"
archiveEvery="Day"
archiveFileName="${defaultDirectory}/test-api-all-${#}.log"
archiveNumbering="Date"
archiveDateFormat="yyyy-MM-dd"
maxArchiveFiles="5"
layout="${fullLayout}" />
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
<target xsi:type="File"
name="ownFile-web"
fileName="${defaultDirectory}/test-api-app.log"
archiveEvery="Day"
archiveFileName="${defaultDirectory}/test-api-app-${#}.log"
archiveNumbering="Date"
archiveDateFormat="yyyy-MM-dd"
maxArchiveFiles="5"
layout="${fullLayout}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="console" />
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxLevel="Info" final="true" />
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>
I do not have any logging settings in appsettings.json files. All logger configuration in in nlog.config. In the Program.cs, I'm registering NLog like:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging(logging => logging.ClearProviders())
.UseNLog()
.Build();
How can I filter out Microsoft logs?
EDIT:
Above configuration started working without a problem the next day without me making any changes :O
Maybe this will work:
<!-- rules to map from logger name to target -->
<rules>
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxLevel="Info" final="true" />
<logger name="*" minlevel="Trace" writeTo="console" />
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
The order of the logging rules are important, as the rules are matched from the top. See also https://github.com/NLog/NLog/wiki/Configuration-file#rules
The order of your logging rules seems to be correct. I suggest trying to double check if your updated nlog.config file is being copied to the build directory (e.g. \bin\Debug\netcoreapp2.1\nlog.config)
I kind of encountered the same issue, but found out that debugging using Visual Studio, it sometimes doesn't really copy the nlog.config file to your build directory. So the solution is to Clean the project first, then Build, and finally Debug.

NLog 2.1 EventId for EventLog not working when not specified

I am using Nlog 2.1 and trying to write errors into Windows Event logger with different eventId. To better distinguish different errors.
If I specify eventId property it's working, but if don't I am not seeing any record in Windows Event Logger.
NLog.config file:
<?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">
<targets>
<target name="console" xsi:type="ColoredConsole"
layout="${date:format=HH\:mm\:ss}|${level:uppercase=true}|${message}" />
<target xsi:type="EventLog"
name="eventlog"
layout="{${newline}
"Logger": "${logger}",${newline}
"StackTrace": "${stacktrace}",${newline}
"Message": "${message}",${newline}
"Exception": "${exception:format=ToString,Data}"${newline}}"
machineName="."
source="CareFusion Analytics Agent Service"
eventId="${event-properties:EventID}"
log="Application" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="console" />
<logger name="*" minlevel="Error" writeTo="eventlog" />
</rules>
</nlog>
Usage:
private static void Main(string[] args)
{
Logger logger = LogManager.GetCurrentClassLogger();
logger.Error("Sample error message"); //This is not working
LogEventInfo logEvent = new LogEventInfo()
{
Level = LogLevel.Error,
Message = "Hello",
LoggerName = logger.Name
};
logEvent.Properties.Add("EventID", 400);
logger.Log(logEvent); //This is working
Console.WriteLine("Press any key....");
Console.ReadKey();
}
The call logger.Error("Sample error message"); goes wrong as the EventLogTarget tries to convert the ${event-properties:EventID} to a integer.
Because layout renders in NLog never return null (but string.Empty), this will give a exception - which will be caught by NLog. In the NLog's internal log you should see a FormatException.
So we need to specify a default value, you could do that with the whenEmpty:
<target xsi:type="EventLog"
...
eventId="${event-properties:EventID:whenEmpty=0}" />
PS: tested it with NLog 4.3.11

Log only to a specific target at runtime

I am using NLog with two targets:
<?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">
<targets async="true">
<target name="logfile" xsi:type="File" fileName="my.log"/>
<target name="console" xsi:type="Console"/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile"/>
<logger name="*" minlevel="Info" writeTo="console"/>
</rules>
</nlog>
Is it possible to log a message only to the "logfile" target, without having the message written to the "console" target as well?
EDIT
To clarify: I want to direct messages from the same class to different loggers at run time (w/o having to change the XML). Something like:
class Program
{
static Logger _logger = LogManager.GetCurrentClassLogger();
static void Main(string[] args)
{
// Note - _logger.InfoToTarget() does not really exist
_logger.InfoToTarget("logfile", "This is my very detailed message, possibly with object dumps");
_logger.InfoToTarget("console", "Short message");
}
}
I'm aware that this couples my code with the NLlog.config file.
One way to accomplish the functionality you are looking for is to name your logger
_logger = LogManager.GetLogger("MyConsoleLogger")
_logger.Info("This will log to the console...");
_logger = LogManager.GetLogger("MyFileLogger")
_logger.Trace("This will log to a file...");
rather than using
LogManager.GetCurrentClassLogger().
In your config file you could then list in the rules
<rules>
<logger name="MyFileLogger" minlevel="Trace" writeTo="logfile"/>
<logger name="MyConsoleLogger" minlevel="Info" writeTo="console"/>
</rules>
This is by far not the most pretty solution to look at, but it does give you the functionality that you are looking for.
There are a few ways to do this, and the correct method depends on your situation.
Keep in mind that you typically want to avoid having your app know too much about the inner-workings of logging. If possible, it's best to configure nlog to decide where things should get logged.
Is there a specific namespace that should not be logged to console? That's easy to configure. Also, you can use the "When" filter (https://github.com/nlog/nlog/wiki/When-filter) or conditions (https://github.com/nlog/nlog/wiki/Conditions)
It may also be best to have multiple logger instances, so you can call the one that is appropriate for each situation (logger per class) (Why do loggers recommend using a logger per class?).
Absolutely, however I am assuming you mean at release you no longer wish to log to the console. You can do this very easily by removing or commenting out the listener that writes to the console target. Now it will only write to the logfile target.
<?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">
<targets async="true">
<target name="logfile" xsi:type="File" fileName="my.log"/>
<target name="console" xsi:type="Console"/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile"/>
<!--<logger name="*" minlevel="Info" writeTo="console"/>-->
</rules>
</nlog>
The rule that writes to the console is now deactivated, but the log file is active. If this is during release you probably want to change your rule to not process your trace logging as the min level for the log file since it will slow down your app with excessive IO. I have asked this question in the past and it appears that best practice is to do this via the XML configuration files. (Logging in Release Build of Application (C#))

NLog fails for certain project configurations - throws for 127.0.0.2 but fails silently for 127.0.0.1

I'm using NLog to log to network. From my understanding, the code and configuration outlined below should always work, no matter other parts of the project. And it indeed works fine for some configurations of my project, but for others it fails, and I can't really figure out the exact reason.
I noticed that (in a non-working configuration) NLog throws when logging to 127.0.0.2 but fails silently (no error in log, but apparently no message is sent) for 127.0.0.1. Since both addresses ought to behave identically I'm left stumped. I'm using Sentinel as log viewer.
How is it possible that NLog fails despite having the exact same code and configuration, and how come it behaves differently for 127.0.0.1 and .2?
Code:
internal class Program
{
protected static Logger logger = LogManager.GetLogger ("Console.Main");
private static void Main (string[] args)
{
logger.Trace ("Initializing platform");
NLog.config:
<targets>
<target name="viewer"
xsi:type="NLogViewer"
address="udp://127.0.0.2:9999"/>
<target name="file"
xsi:type="File"
layout="${longdate} ${logger} ${message} ${exception}"
fileName="${basedir}/logs/logfile.txt"
keepFileOpen="false"
encoding="iso-8859-2" />
<target xsi:type="ColoredConsole"
layout="${logger} ${message} ${exception}"
name="console"/>
</targets>
<rules>
<logger name="*"
minlevel="Trace"
writeTo="viewer" />
<logger name="*"
minlevel="Trace"
writeTo="file" />
<logger name="*"
minlevel="Info"
writeTo="console" />
</rules>
Internal logfile for 127.0.0.2:
2011-10-11 12:05:08.4364 Error Target exception: System.Net.Sockets.SocketException (0x80004005): No such host is known
at System.Net.Dns.InternalGetHostByAddress(IPAddress address, Boolean includeIPv6)
at System.Net.Dns.GetHostEntry(String hostNameOrAddress)
at NLog.Internal.NetworkSenders.UdpNetworkSender..ctor(String url)
at NLog.Internal.NetworkSenders.NetworkSender.Create(String url)
at NLog.Targets.NetworkTarget.NetworkSend(String address, Byte[] bytes)
at NLog.Targets.NetworkTarget.Write(LogEventInfo logEvent)
at NLog.LoggerImpl.Write(Type loggerType, TargetWithFilterChain targets, LogEventInfo logEvent, LogFactory factory)
Found it. All configurations were referencing the same DLL, but some of them used one from GAC instead of the one hinted at. Manually copying the DLLs and rewriting project references fixed the issue.
Why doesn't VS give a highest level warning on missing DLLs ...

Categories

Resources