It’s no secret I’m a huge fan of NuGet. I used it from its early days in commercial projects, helped in the effort to get the first packages from the .NET BCL onto it, and now work on Octopus Deploy with NuGet close to the heart of the product.

So, with that it shouldn’t be any surprise that my own company’s product Seq now has a strong NuGet tie-in.

Seq’s an effort to realise more of the benefits of structured logging. I kicked off Serilog because I wanted structured logging to be as cheap, easy and natural to use in an app as traditional text logging is.

The promise of structured logs however is in the consumption of the log events. Unlike traditional logging, where events are viewed primarily as messages (perhaps with a selection of properties tacked on to identify the context – thread ID, etc.), structured log events are first and foremost – (ta-da!) – structured events. They carry the same contextual properties and friendly message, but the data specific to the event itself is also preserved in a structured form.

Log.Information("Response time from {0} was {1} milliseconds, serviceName, elapsedMs);

The interesting things here, and the reason the event was recorded in the first place, are the service name and its response time. By preserving this interesting data in a structured event, we can still render it out to a text file, but using Seq we can also write queries like:

"Response time" && @0 == "Fulfilment" && @1 > 300

A quick breakdown of how this query works:

  • "Response time" narrows down the events we’re looking for (you could include the whole format string if you needed to)

  • && is the classic logical AND

  • @0 and @1 are the properties from the message (Serilog supports named properties, but if you’re migrating from log4net or elsewhere, classic index-based format strings are fine - just use @ to prefix the index or it will be interpreted as a numeric constant!)

  • @0 == "Fulfilment" finds events regarding the fulfilment service

  • @1 > 300 finds responses that took more than 300 ms

There are increasingly sophisticated log consumption tools out there, but Seq really makes this stuff effortless. If better queries were the only reason to use Seq I’d still that a compelling one.

Hang on, what about NuGet?

Finding events is really only half the picture…

Since structured events are so easy to process, you naturally want to start reacting to them. It might be as simple as sending an email to operations when an integration point is slow, or to the support team if an app crashes during a particular transaction.

Sometimes the destination for an event might not be email – it can be useful to pull temporal data back into Excel, SQL or BI tools for reporting, pass events to other systems like issue trackers, or interact with different kinds of devices like pagers.

On top of that, we developers are a crafty bunch. With a little more code, perhaps with help from Rx, we can find patterns, detect trends or track an event’s frequency.

You can quickly see how adding all these capabilities to Seq would be a stretch, and the resulting product would probably be a bloated mess.

That’s where Seq apps come into the picture. Apps are simple event handlers that can be plugged into Seq and configured by the user. How are they plugged in? Well, from NuGet of course!

Install Seq apps from nuget

But, I’m getting ahead of myself.

Let’s look at a simple Seq app; this one supports simple formatted email with `` style templates for rendering event properties into the subject and body.

Here’s how configuration for an instance of the event looks, in Seq:

Email app configuration

The top section is where the instance is given a name, and the source of events is configured. Un-ticking the “Only send events manually…” box will let you choose a query or view to run the app on.

Further down, in the Settings section, you see some pretty typical fields for configuring email.

The interesting thing is how all this is surfaced: the properties in the UI are just simple properties on the app’s “Reactor” class:

[SeqApp("Formatted Email",
    Description = "Uses a provided template to send events as formatted email.")]
public class EmailReactor : Reactor, ISubscribeTo
        DisplayName = "From address",
        HelpText = "The account from which the email is being sent.")]
    public string From { get; set; }

(Complete example…)

The rest of the plugin is just as straight-forward: a few more fields, an On method, and some formatting helpers:

    public void On(Event evt)
        var body = string.IsNullOrWhiteSpace(BodyTemplate) ?
            FormatDefaultBody(evt) :
            FormatTemplate(BodyTemplate, evt);

        var subject = FormatTemplate(SubjectTemplate, evt);

        var client = new SmtpClient(Host, Port ?? 25);
        if (!string.IsNullOrWhiteSpace(Username))
            client.Credentials = new NetworkCredential(Username, Password);

        client.Send(From, To, subject, body);

The evt object has a dictionary attached that contains the log event’s properties.

By packaging up the compiled version of the class in a standard library-style NuGet package (with all dependencies included!) anyone can install it from the Seq ‘settings’ page and start using it to process their log stream:

Configured app instance

As of today you can get a few apps via the [seq-app] NuGet tag, but most of the fun is in writing your own. There’s an open source repository on GitHub with several examples to learn from – the easiest way to get going is to clone it and experiment with your own modifications.

Hey, also - in case you missed it, pricing for Seq is now also posted on the homepage. We’re not offering purchases until we’re out of beta, but announcing our great **free edition** should give you one more reason to give Seq a try! (And since migration from format-string based logging is so easy, what are you waiting for?)

Join the brand spanking new Seq mailing list if you’d like to stay in the loop.