Like many other libraries for .NET, LogMagic provides diagnostic logging into files, the console, and elsewhere. It's probably the easiest framework to setup, has a clean API, extremely extensible.
LogMagic also supports a relatively new paradigm of structured logging.
- Installation
- Setup
- Example Application
- Configuration Basics
- Writing Log Events
- Known Writers and Enrichers
- Visual Studio Integration (snippets)
The core logging package is LogMagic. Supported frameworks are:
- .NET 4.5
- .NET Standard 1.6. By supporting .NET Standard we ensure that LogMagic works on .NET Core, Android, iOS or anything else.
Note to ASP.NET Core users: LogMagic does not integrate with ASP.NET Core logging system, it's a separate logging framework you can use in conjunction with or instead of it. Both system have their pros and cons.
PM> Install-Package LogMagic
Types are in LogMagic
namespace
using LogMagic;
An ILog
instance is the one used to log events and can be created in one of three ways by calling to global static L
class.
Create using current class type name
ILog _log = L.G();
Or by specifying type explicitly
ILog _log = L.G<T>; //using generics
ILog _log = L.G(typeof(T)); //passing type
Or by specifying name explicitly
ILog _log = L.G("instance name");
By default LogMagic doesn't write events anywhere and you need to configure it:
L.Config.WriteTo.Console();
This is typically done once at application startup.
The complete example below shows logging in a simple console application, with events sent to the console as well as to file on disk.
- Create a new Console Application project
- Install the core LogMagic package
In Visual Studio, open Package Manager Console and type:
Install-Package LogMagic
- Add the following code to
Program.cs
using System;
using LogMagic;
namespace LogMagicExample
{
public class Program
{
private readonly ILog _log = L.G();
public static void Main(string[] args)
{
L.Config
.WriteTo.Console()
.EnrichWith.ThreadId();
new Program().Run();
Console.ReadLine();
}
private void Run()
{
_log.Trace("hello, LogMagic!");
int a = 10, b = 0;
try
{
_log.Trace("dividing {a} by {b}", a, b);
Console.WriteLine(a / b);
}
catch(Exception ex)
{
_log.Trace("unexpected error", ex);
}
_log.Trace("attempting to divide by zero");
}
}
}
- Run the program
LogMagic always check last parameter of Trace()
arguments whether it's an exception class and eliminates from the argument list.
LogMagic uses C# API to configure logging.
Log event writers generally record log events to some external representation, typically console, file, or external data store. LogMagic has a few built-in writers to get you started. More writers are redistributed via NuGet.
A curated list of available packages are listed below on this page.
Writers are configure using WriteTo
congiguration object.
L.Config.WriteTo.PoshConsole();
Multiple writers can be active at the same time.
L.Config
.WriteTo.PoshConsole()
.WriteTo.Trace();
Text-based writers support message formatting. Whenever a format
parameter appears in writer configuration you can specify your own one:
L.Config
.EnrichWith.ThreadId()
.WriteTo.Console("{time:H:mm:ss,fff}|{threadId}{level,-7}|{source}|{message}{error}");
Enriched properties can also appear in the output. In the example above a threadId
comes from thread ID ennricher.
Built-in property names:
{time}
- used to reference event time{level}
- prints message severity{source}
- logging source name{message}
- log message{error}
- error
All of the properties support standard .NET formatting used in string.Format()
.
Enrichers are simple components that add properties to a log event. This can be used for the purpose of attaching a thread id, machine IP address etc. for example.
L.Config.EnrichWith.ThreadId();
Sometimes it's useful to add context information to a logging session during a call duration scope. LogMagic achieves it by dynamically adding and removing propeties from the ambient "execution context". For example, all messages during a transaction might carry the id of that transaction, and so on.
The feature does not need any special configuration, and properties can be added an removed using L.Context()
:
log.Trace("no properties");
using(L.Context("A", "id1"))
{
log.Trace("carries property A=id1");
using(L.Context("B", "id2"))
{
log.Trace("carries A=id1 and B=id2");
}
log.Trace("carries property A=id1");
using(L.Context("A", "id3"))
{
log.Trace("carries property A=id3");
}
log.Trace("carries property A=id1");
}
log.Trace("no properties");
Pushing property onto the context will override any existing properties with the same name, until the object returned from L.Context()
is disposed, as the property A in the example demonstrates.
L.Context()
accepts multiple properties at once if you need to:
using(L.Context("A", "id1", "B", "id2"))
{
//...
}
Logging context is used extensively by LogMagic plugins to carry execution context information across the application. For instance, web applications need to known which request the code is related to etc.
Important: properties must be popped from the context in the precise order in which they were added. Behavior otherwise is undefined.
You can also get context property value by name at any time in any place in your code by calling to L.GetContextValue(propertyName)
which returns null if property doesn't exist.
Important: Log context is not available when you target for .NET Framework 4.5 due to the reason that this version doesn't have any options to track execution. If you are still using .NET 4.5 or earlier it's time to upgrade to .NET 4.6.
Log events are written to writers using the ILog
interface. Typically you will instantiate it on top of your class definition you want to log from.
private readonly ILog _log = L.G<Class>();
// ...
_log.Trace("application v{version} started on {date}", "1.0", DateTime.UtcNow);
the string above "application v{version} started on {date}"
is a message template. Message templates are superset of standard .NET format string, so any format string acceptable to string.Format()
will also be correctly processed by LogMagic.
- Property names are written between
{
and}
brackets - Formats that use numeric property names, like
{0}
and{1}
exclusively, will be matched with the log method's parameters by treating the property names as indexes; this is identical tostring.Format()
's behavior - If any of the property names are non-numeric, then all property names will be matched from left-to-right with the log method's parameters
- Property names, both numeric and named, may be suffixed with an optional format, e.g.
:000
to control how the property is rendered; these format strings behave exactly as their counterparts within thestring.Format()
syntax
LogMagic doesn't have the classic logging levels (i.e. debug, info, warn etc.) as this is proven to be rarely used. Instead you only need one single Trace()
method. Due to the fact that structured logging is supported and promoted there is no need to have logging levels as you can always filter based on a custom property if you ever need to.
Name | Description |
---|---|
System Console | Simplest logger that outputs events to the system console. |
Posh Console | Simplest logger that outputs events to the system console, but also supports colorisation. |
System Trace | Writes to the system trace. |
File on disk | A really simple writer that outputs to a file on disk. |
Azure Application Insights | Emits telemetry into Azure Application Insights. |
Azure Service Fabric | Integrates with Azure Service Fabric by building correlating proxies, enrichers, and emitting cluster health events |
ASP.NET Core | Provides a custom middleware that automatically logs requests. |
Writer Syntax | Meaning |
---|---|
.Constant() |
adds a constant value to every log event |
.MachineIp() |
current machine IP address |
.MachineName() |
current machine DNS name |
.MethodName() |
caller's method name |
.ThreadId() |
managed thread id |
Note that external packages may add more enrichers which are not listed here but documented on specific package page.