Get information out of your code at runtime to send it to log files, perf counters, consoles, services, sensors. Maximum flexibility and maintainability, minimum code.


Keywords
logging, tracing, instrumentation, sensors, diagnostics
License
Other
Install
Install-Package Its.Log -Version 2.10.1

Documentation

Its.Log

Join the chat at https://gitter.im/jonsequitur/Its.Log

Build Status NuGet Status

Code instrumentation

Its.Log helps you instrument your code. It doesn't know anything about logging libraries, performance counters, or tracing, but it can be used to send a lot of information to all of them easily.

Log any object:

Log.Write(() => ren);

...or several at once:

Log.Write(() => new { ren, stimpy } );
          // writes this to the log:
          // { Information: CallingType = Characters | CallingMethod = Dinner | Subject = { ren = { FirstName = Ren, LastName = Hoek, Species = chihuahua, Weight = 1 } | stimpy = { FirstName = Stimpy, LastName = [null], Species = cat, Weight = 5 } } | TimeStamp = 2014-12-18 16:11:00Z }

Send the output anywhere. Here's how you send it to the console:

Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToString());

Log boundaries:

using (Log.Enter(() => new { ren, stimpy } ))
       // writes: { Start: CallingType = Characters | CallingMethod = Dinner | Subject = { ren = { FirstName = Ren, LastName = Hoek, Species = chihuahua, Weight = 1 } | stimpy = { FirstName = Stimpy, LastName = [null], Species = cat, Weight = 5 } } | TimeStamp = 2014-12-18 16:11:00Z }
{
     ren.Starve();
     stimpy.Feed();  

} // writes:
  // { Stop: CallingType = Characters | CallingMethod = Dinner | ElapsedMilliseconds = 1402 | Subject = { ren = { FirstName = Ren, LastName = Hoek, Species = chihuahua, Weight = .5 } | stimpy = { FirstName = Stimpy, LastName = [null], Species = cat, Weight = 9 } } | TimeStamp = 2014-12-18 16:11:02Z }

Confirm checkpoints along the way:

using (var activity = Log.Enter(() => new { ren, stimpy } ))
{
    stimpy.Feed();  
    activity.Confirm(() => "Stimpy fed");

    ren.Starve();
    activity.Confirm(() => "Ren starved");

    throw new Exception("I want real food!"); 
    
    activity.Confirm(() => "Done!");
} // writes:
  // { Stop: CallingType = Characters | CallingMethod = Dinner | ElapsedMilliseconds = 1402 | Subject = { ren = { FirstName = Ren, LastName = Hoek, Species = chihuahua, Weight = .5 } | stimpy = { FirstName = Stimpy, LastName = [null], Species = cat, Weight = 9 } } | TimeStamp = 2014-12-18 16:11:02Z | Params = { { Confirmed = "Stimpy fed", "Ren starved"} } }

Format anything with the ToLogString extension method:

Console.WriteLine(episodes.ToLogString());
// if episodes is null, it writes: [null]
// otherwise it writes using ToString: { SampleApp.Episode,  SampleApp.Episode,  SampleApp.Episode, (...12 more) }

Tell it to be more informative:

Formatter<Episode>.RegisterForAllProperties();
Console.WriteLine(episodes.ToLogString());  
// now writes: { { Id = 4234, Title = Space Madness, Characters = { SampleApp.Character, SampleApp.Character } } }, (...12 more) }

Customize the output:

Formatter<Episode>.RegisterForAllMembers(e => string.Format("Episode #{0}, Title: {1}", e.Number, e.Title));

Use The Reactive Extensions to query the log event stream...

var errors = Log.Events().Where(e => e.Subject is Exception);

...or buffer and aggregate them...

var countOf404s = logEvents
                    .Where(e => e.SubjectIs<HttpException>())
                    .Where(e => e.GetSubject<HttpException>().GetHttpCode() == 404)
                    .Buffer(TimeSpan.FromSeconds(1))
                    .Select(es => es.Count());

Objects, not strings

Its.Log works best when you pass objects rather than strings.

This works:

  Log.Write(() => "An error occurred: " + anException.ToString());

But this is better:

  Log.Write(() => anException);

Knowing the type lets you decide at a policy level how to route them, format them, or react to them. It also helps keep your log output more consistent and your logging code more terse.

Log levels - Info, Error, Warning, Verbose

While log levels can be specified, Its.Log also sets them by convention based on the type of the object you pass. For example, if the subject of a log entry is an exception:

  Log.Write(() => anException);

then the output log level will default to Error:

  { Error: CallingType = Demo | CallingMethod = Formatting3_Exception_formatting_is_very_descriptive_by_default | ExceptionId = 75743ddd-824f-4c1b-954c-09b249d00e90 | Subject = { ReflectionTypeLoadException: Types = { Demo } | LoaderExceptions = { { DataException: Message = oops! | Data = { [why?, because] } | InnerException = [null] | TargetSite = [null] | StackTrace = [null] | HelpLink = [null] | Source = [null] | HResult = -2146232032 } } | Message = Exception of type 'System.Reflection.ReflectionTypeLoadException' was thrown. | Data = {  } | InnerException = [null] | TargetSite = Void Formatting3_Exception_formatting_is_very_descriptive_by_default() | StackTrace =    at Its.Log.Instrumentation.UnitTests.Demo.Formatting3_Exception_formatting_is_very_descriptive_by_default() in c:\dev\github\Its.Log\Its.Log.UnitTests\Demo.cs:line 93 | HelpLink = [null] | Source = Its.Log.UnitTests | HResult = -2146232830 } | TimeStamp = 2014-12-18 16:23:28Z | Params = {  } }

JSON

Outputting logs as JSON rather than Its.Log's default format is simple. Here's an example using NewtonSoft.Json:

  var serializer = new JsonSerializer
                        {
                           TypeNameHandling = TypeNameHandling.All,
                           ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                        };
                      
                        Formatter<LogEntry>.Register((entry, writer) =>
                        {
                          serializer.Serialize(writer, entry);
                          writer.WriteLine();
                        });