Quantcast
Channel: Nicholas Blumhardt
Viewing all 102 articles
Browse latest View live

Getting started with the Seq preview

$
0
0

A year ago, I started itching. Having accumulated a bunch of great logs on an online payments project, I wondered – just what useful information I could extract from them?

Working on a consulting gig, I wanted a quick way to get something useful in front of my client. “Quick” then meant minutes – not days or weeks. It had to be Windows-friendly, on premises and useful even if the only installation went onto my development laptop.

Coming away from that search unsatisfied, I started thinking about the fundamentals of the problem. Did I really need a log parsing and storage tool at all? Most of the challenge seemed to be in taking ugly log data and reliably extracting information. If the log data could be made less ugly, perhaps I could just use one of the tools I was already familiar with, like CouchDB or even SQL Server?

Somewhere down that line Serilog was born. It’s been out in the wild for a little while, and feedback has been very positive. Serilog solves the problem of log-ugliness for me, while maintaining the low up-front investment characteristic of successful logging tools. I’ve been less satisfied by the available back-ends, though. NoSQL databases are a good fit for storing structured events and have a gentle learning curve, but don’t really satisfy my second “enterprise consultant” requirement: being general-purpose, it is still necessary to invest time in learning their APIs and actually writing some code before getting at the juicy data inside.

Seq (as in ‘sequence’), released today as a very early preview, is my effort to scratch the itch for myself.

Seq web  UI

Seq is just getting started, but it assembles what I think are the right ingredients to build on:

  • Runs on-premises, as a console app without installation, or as a Windows service (no IIS to configure)
  • Provides queries over structured data using a simple C#-like syntax, with full-text search when it is needed
  • Takes exactly one line of code to configure via Serilog, which we’ll see below
  • Is built with .NET from the ground up

What can you do with Seq today?

Seq is a fun way to centralize logs from networked systems. You can set up views (“The eCommerce Site”, “Intranet Apps”, …) based on properties embedded in log events. You can write and save queries with multiple structured- and text-based filters, and you can export the results in CSV format for analysis in Excel (my preference!) or your traditional BI tools.

Export

And where is Seq going?

In the short term, there are a lot of performance, usability and feature requirements to implement before it will be ready for prime time. I hope Seq will stay simple as it grows, perhaps one day into the “developer’s tool of choice” for managing application events in .NET. First-class support for .NET programmability, and integration with the tools we use every day, are high on my list.

What Seq is not:

To clarify where Seq fits in, Seq is not an analytics tool. There are plenty of those out there, and chances are you can find one that meets your needs already. Seq is better described as a ‘staging ground’ for feeding those tools with quality data generated by applications.

Seq is also not “big data”. Big data capabilities come with trade-offs and usability issues that don’t make sense for everybody. Seq can theoretically handle terabytes of event storage, but takes a while to search more than a few hundred thousand events at a time (especially when ‘cold’). The number of useful information-level events emitted by an enterprise app in a week are generally orders of magnitude less than that; if you have big data you probably already know it, and have the motives and the resources to invest your time in learning something else.

Setting up – the client

To use Seq, you need to log events with Serilog. If you’re not using it already, you’re missing out! The Serilog site has some useful material to help you set up.

Seq provides a “sink” for Serilog, which we release via NuGet. It currently targets .NET 4.5 but support for .NET 4.0 is planned.

At the Visual Studio Package Manager console type:

PM> Install-Package Seq.Client.FullNetFx

Then, configure the logger and write some events:

using System;
using Serilog;
using Seq;

namespace SeqSimpleExample
{
    class Program
    {
        public static void Main()
        {
            Log.Logger = new LoggerConfiguration()
                .WriteTo.ColoredConsole()
                .WriteTo.Seq("http://localhost:5341")
                .CreateLogger();

            Log.Information("Hello, {Name}!", Environment.UserName);
            Console.ReadKey(true);
        }
    }
}

Run it now – there’s no Seq server listening, but the app will behave: as much as I love event data, collecting it can’t come at the expense of app stability.

SeqSimpleExample

Setting up – the server

The server requires .NET 4.5 and is tested on Windows 8. You should be fine with any .NET 4.5 machine. The web UI is tested with the latest Chrome, Firefox and IE10.

After downloading the Seq zip file from the site, extract it to a handy location – e.g. C:\Seq – and double-click the included Run.bat file. That’s it!

Seq - first run

Open a web browser at http://localhost:5341 to see your (empty) event stream.

Seq - Empty

By default Seq will create a Data folder next to the executable. You can change where data is stored and the URL that Seq will listen on, as well as install Seq as a Windows service, by running seq.exe from the command-line. Type seq help for a list of options.

Now, go back and run your client application again. Refresh the event stream with the little ‘o’ button in the toolbar, and voila! If everything went well your event will be displayed.

Hello

(If nothing showed up, make sure you didn’t terminate the console app “hard” with the close button before the event was published – the Windows console kills apps hard.)

Using Seq

The Seq interface is pretty simple and should be self-explanatory in most places. The best way to get started is to type some text in the Filter box and hit ‘Enter’ or press the ‘Filter’ button. Once you’ve found an event of interest, click on it. If you hover over a property value, you’ll see a selection of actions like the ‘Mark’, ‘Filter’ and ‘Exclude’ buttons shown here.

Hover

(If you haven’t used Serilog before, look closely – not just static properties tacked onto the events, but the tokens from the message itself like CustomerName are available as properties for searching on.)

Chances are the first thing you’ll use on “real” event data is the ‘Exclude’ button – there’s always more signal than noise! In this case though, press ‘Filter’ to drill down to events with matching property values.

Seq queries use a syntax that should feel comfortable to C# developers and natural to almost everyone else. For example, big checkouts might be found with a query like:

    "checkout" && TotalSpend > 500

To keep typing to a minimum, strings floating around on their own like "checkout" are treated as if they’re part of a Contains() function call. Pop over to the ‘doc’ tab in Seq to see some examples and a bit more information.

To export tabular data like one of the early screenshots show, use the ‘Mark’ button to pick out properties from events of interest.

Limitations

There are a lot! For example, event retention is fixed at 15 days (a proper retention policy implementation is in the works) and only one user account is supported (it is called ‘admin’). But, it is more fun to iterate in the open. Depending on the time available I hope to put an increment out every 2-4 weeks.

Licensing and all that stuff…

Unlike many of my other software obsessions, Seq is not an open source project. To follow through with the project is going to take time, so make that a reality a commercial release is more realistic.

The each preview build can be used for 30 days, after which I hope you’ll grab an updated preview build. At ‘RTW’ there will be a variety of licensing options that I hope will fit everyone who wants to use the app.

Feedback

I’d love to hear from you if you try Seq! Experiences good and bad are best shared on the issue tracker.

Thanks for help reaching this milestone go out to Jesse Beard, Jaben Cargman, and David Pfeffer who kindly provided feedback on the very first usable builds.


Seq 0.6 – auto-refresh, retention policies

$
0
0

Just a little update to Seq to follow the preview released last week.

Auto-refresh

Watch events roll by with your feet on the desk – just drop down the little arrow beside the Refresh button and select Auto-refresh on.

Auto-refresh

The button lights up and new events matching the current query and view will show up as they arrive. Click the button again, or any event, to turn it off.

Retention policies

Less exciting to watch, but worthwhile nonetheless, ‘retention’ rules let you selectively delete events once they reach a specified age.

First, close any active views or queries by clicking the × in their blue title-bars. Then, build a view that selects the events you want deleted:

Debug View

Drop-down the Require button and select Require → View. Give the view a name, e.g. ‘Debug events’.

Once that’s done, go to the settings area. Retention is the only setting available right now, so you can’t miss it!

Retention

Click on the existing policy to edit or delete it. Each policy comprises a view, and the number of days before matching events will be deleted. Policies aren’t applied immediately – it may take 10 minutes or so before you see any effect.

(The inverse approach to this, keeping matched events for a time, would be useful too and is on the radar for a future version.)

Downloads

Grab the new version from this site.

That’s all for this week!

Server-side event handling with Seq apps

$
0
0

Seq

Happy New Year! The festive season did put a bit of a dent in Seq progress, but here we are in 2014 and I think
we have the most interesting release yet to kick it off.

Structured log events are often nicer to work with than plain text, and Seq 0.7 takes full advantage of this to
provide a plug-in “app” model for processing events with C# code.

Events can be sent to an app manually via the Seq web UI, or, events that match a particular query or view can
be processed automatically as they arrive. This makes a huge range of interesting scenarios possible – here are some
that are implemented or being discussed:

  • Sending the details of an event by email: we think that this is such a common use case that a simple version comes built-in
  • Pushing events to a web service or API: errors might be posted to an issue tracker (or pager!), interesting data might be pushed to a dashboard
  • Archiving or mirroring: as events come in they might be written off to text files or an alternative log archive
  • Feeding BI tools: many businesses report off of relational databases; by writing event properties back to SQL Server, operational data can more easily be brought into the mix
  • Looking for patterns: here things get fun, since apps are implemented in regular C#, tools like Rx or state machines can be applied in any way you please

So what does an app look like?

Sending email with the built-in SMTP app

Here’s one we prepared earlier

In this simple example our goal’s to motivate the Sales team with an email whenever a big cart is checked out.

1. Create a query to match events of interest

Early days – any cart with more than two items is worth celebrating:

FindingBigCheckouts

Save the query with a good descriptive name; we’ll this one “Big Checkouts” :)

2. Settings → Apps

BuiltInApps

Click Start new instance:

ConfiguredBigSalesInstanceManual

Each app can have a set of associated settings – in the case of the SMTP Email app these are pretty standard. Each running instance gets its own title and values for each setting.

Currently we’ll format the event as text containing the message and a list of properties. A more complete implementation accepting a template for the email body would be nice to do at some point (but given how easy it is to write your own apps, you might beat me to it!).

So that we can try out the app’s configuration before spamming everyone, we’ll leave the Manually send events to the app checkbox ticked; to start processing the results of the query as they arrive we can return here and untick the box to select a query.

3. Sending an event manually

Back at the query we set up in the events screen, hovering over the ID of a matched event (click the event first to expand) reveals the name of the app instance we just set up.

ManuallySendBigCheckouts

That’s it for the basics – our email should have been sent by now, or if not there will eventually be an error raised back to the event stream by the app. We’ll see how to pipe events from a query or view to an app when we build our own from the ground up.

Writing an app to run in Seq

Event handling code is fun, and there’s hardly enough of it! Seq makes it easy to write event handlers and upload them to run on the event stream.

This example is going to watch for timeouts errors – System.TimeouException and the like. From time to time these happen in healthy systems – connections are dropped, machines restarted, etc. An increase in the frequency of timeouts from a system is a good warning sign though that something is going south.

The app itself is going to be a little bit generic: if it sees more than a set number of events in a certain window, it will write a new event back to the stream. (Another app might be configured to pick this up and let someone know, e.g. by sending email.) We’ll use a query to choose timeouts specifically, and pipe these to the app.

1. Create a new class library

The app’s going to live in a regular .NET 4.5 assembly. If you’re feeling rigorous you might create a unit test project here too!

CreateNewProject

2. Install Seq.Apps from NuGet

PM> Install-Package Seq.Apps

3. Write the reactor class

Events come in, we tally them up in a sliding window, and if our threshold is met we write the warning. Simple to describe, should be simple to implement!

The class that will handle all of this follows the same basic pattern that all Seq apps use:

public class SlidingWindowReactor : Reactor, ISubscribeTo<LogEventData>
{
    public void On(Event<LogEventData> evt)
    {
    }
}

There’s a little bit of ceremony here – a base class, an interface and so-on, but nothing too scary.

The fun part is our sliding window check. First, where do we keep the tally? Simple – only a single instance of the reactor is used for the life of the app, so we can just keep this in a few private fields:

  int[] _buckets;
  int _currentBucket;
  int _eventsInWindowThreshold;
  …

Rather than clutter up this post with nitty-gritty I’ve posted the complete example in this Gist. You might find it worthwhile to go and skim through it before continuing on below.

The “resolution” of our check will be one second, and we’ll count the events observed each second in an array used as a circular buffer.

Reactors are single-threaded, so we don’t need any locks or other synchronisation.

When the first matching event arrives, the buffer is initialised (yes, an Init() method would be nice to support here).

  public void On(Event<LogEventData> evt)
  {
    if (_buckets == null)
    {
        var window = App.GetSetting<int>("WindowInSeconds");
        _buckets = new int[window];
        _eventsInWindowThreshold = App.GetSetting<int>("EventsInWindowThreshold");
        _currentBucket = 0;
       …
    }

One thing to notice here is App.GetSetting<T>() – just like the To address, Subject and so-on supported by the SMTP app, custom apps can be configured with their own settings.

The next thing we’ll do is check that the event (which might have come out-of-order) fits within our window, and slide the window if necessary:

    int eventBucket;
    if (!TrySlideWindow(evt, out eventBucket))
        return;

The implementation of TrySlideWindow() not so pretty – it is much nicer to view in long form so I’ll just show the method call here. (Fork the Gist if you can improve it ;).)

Next we increment the count for the second that the event being observed falls in:

    _buckets[eventBucket]++;

Is the sum greater than the threshold?

      if (_buckets.Sum() >= _eventsInWindowThreshold)
      {
        var message = App.GetSetting<string>("ThresholdName");
        Log.Information("Threshold {ThresholdName} reached: {EventCount} " +
            "events observed within {WindowSize} sec.", _thresholdName, sum, _buckets.Length);
      }

The complete example is a bit more efficient, and suppresses the message for a set time after it has been triggered.

(The name Reactor is a literal description of ‘something that reacts’ by the way – not an implementation of the Reactor Pattern, which is entirely different.)

4. Upload the app

Hopefully the sample complies; when it does, pop over to Seq’s settings screen and Upload an app:

SlidingWindowUpload

Seq.Apps.dll and Serilog.dll are already loaded into the container that runs the app, so only the single assembly containing the app itself needs to be selected in this screen.

Here at the moment there is a bit of manual effort that some future conventions should alleviate – the namespace-qualified name of the reactor type needs to be entered, as do each of the setting names supported by the app.

Once that’s done, the app will appear in the main Apps page, where instances can be started as we did in the first example.

SlidingWindowOnAppsPage

5. Create a query to match timeouts

We’ll write and save a very simple query to find timeouts – any event that has an Exception property containing the word "timeout" will be matched.

MatchedTimeoutExceptions

6. Send the results of the query to a new app instance

This time when we start an instance of our new app, we’ll choose the query matching timeouts:

TimeoutAppInstance

With our app chugging away in the background, we can query using the TimeoutName property that we set to see when the threshold was reached:

Timeouts

Hopefully this post has started to paint a picture of how apps fit in with Seq’s other features to make it an interesting platform not just for storing and searching events, but also for integrating them back into more sophisticated workflows.

If you build something fun I’d love to hear about it!

The ‘Nu’-est entry into the NuGet ecosystem

$
0
0

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 {{Mustache}} 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<LogEventData>
{
    [SeqAppSetting(
        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<LogEventData> 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.

Easy ASP.NET log correlation with Serilog and Seq

$
0
0

It has been a while since I did any ASP.NET development, I have to admit. Lately I’ve had to dust off my MVC to transform http://getseq.net from a static site into an ASP.NET-based one with a few more smarts.

In the process I rediscovered a little Serilog gem that I’d completely forgotten about.

I’ve been working on a staging copy of the site, and I keep a little Seq query handy called “Exceptions” – it is a simple query: Has(@Exception), that finds events with an exception property attached.

Here’s one such nasty:

Exception

Yikes, where did this come from, and what’s wrong with my password?

Now, the exception is interesting and there are even a few properties attached that give a clue about where it is from. The apple of my eye however is HttpRequestId.

This is added by a little-known helper in the Serilog.Extras.Web package. Install it into your ASP.NET project, and you get access to some useful features for ASP.NET logging.

To attach a unique identifier for the current ASP.NET web request to your log events, add the enricher to your log configuration:

Log.Logger = new LoggerConfiguration()
    .Enrich.With(new HttpRequestIdEnricher())
    .WriteTo.RollingFile("C:\Logs\my-site-{Date}.txt")
    .WriteTo.Seq("http://my-seq")
    .CreateLogger();

(Naturally I’m also using the Seq.Client.Serilog package as well to push events to my Seq server.)

With that bit of configuration in place, any log events generated during a request will have a unique request ID attached.

Back to my exception; by filtering to the HTTP request ID (and closing the “Exceptions” query, if it is open):

2014-02-19 08_07_51-Seq

I get the events logged during the request, showing the lead-up to the colossal crash, while generating a new trial key for myself :)

2014-02-19 08_09_20-Seq

In this sorry case, the error message being generated by the Crypto API is completely wrong, and in fact I needed to enable user profile loading on the ASP.NET app pool. Ah, so many ways to shoot oneself in the foot!

Hopefully this little sample has convinced you to try Serilog and Seq, if you haven’t already!

There’s some documentation on setting up Serilog – it is a 5-minute task and even supports standard .NET format string logging if you want to convert from log4net or NLog.

There’s also now some documentation on the Seq setup process (another 5-minute job) and integrating Seq with various logging frameworks.

We’re closing in on a 1.0 release of Seq – there are still tweaks to be made and some final details of the editions and pricing to nail down, but the free developer edition will definitely make the cut and is perfect for many uses.

If you’re already on your way to log correlation nirvana I’d love to hear from you.

Seq “Log Query of the Day” #1 – Text

$
0
0

Seq has a fairly minimal interface. Pride of place, front and centre, is taken by a large filter box that looks like this:

Filter

A lot of work has gone in to such a simple-looking control. Over the coming weeks I’m going to post a few tips like this one, showing just what’s possible with Seq’s built-in query language.

Searching for plain text

The first jumping off point for any log-finding mission is usually a snippet of text. Often this is an error message grabbed from the app itself (or a user’s email) but it might also just be a general tag like a user’s login or web URL.
Let’s imagine we’ve had a report with one of my least favourites to see: “Sequence contains more than one element” – a LINQ staple.

This one’s simple: just paste in the text we’re looking for, and Seq will find events that contain it:

Text

For such a subtle feature, there are a surprising number of choices to make in the implementation to be aware of as a user.

  • Case insensitivity – first, the text is matched without regard to case, so we could just as easily used “sequence” and we’d still match the target event
  • Locality – the search includes all text in the event, not just the message; property names as well as values can be matched
  • Tokenization – it might come as a surprise after using Google-style searches that by default Seq will search for the whole string, not “Sequence” and “contains” and “more” … etc.
  • Ranking – results always come back in (reverse) chronological order, not relevance

For most defaults, there’s a query that can be used to alter Seq’s behaviour.

Case-sensitive matching

If we need to make a case-sensitive search for text, we pinch C#’s as-I-said-it-is syntax for literal strings:

Sensitive

Text in more complex Seq queries uses the C#-like "double-quoted" syntax, and wherever a case-insensitive text literal is accepted, you can use an @-prefixed case-insensitive one instead (or a regular expression, but we’ll get to those further into this little series.)

Localized searching

Sometimes we only want to match text in one part of the event payload. Let’s say we’re searching for a customer’s name: the way to do this is using the Contains() function:

Contains

(Here’s one of those examples where using a case-insensitive text expression would also work.)

CustomerName is a property in this example. There are a lot more ways to query properties than using Contains() – we’ll see more of those in this series, too.

Tokenization

Finally, though I believe it’s actually quite rare when searching diagnostic logs, sometimes we may wish to match events with a combination of text fragments.

There’s nothing special in Seq for these kinds of queries – we just combine some text expressions and the classic and/or logical operators:

Tokenize

And, or and not all match their C# counterparts.

Fuzzy query parsing

A final note – given that Seq makes an intelligent guess whether a filter is free text or a query, how do you tell which interpretation it chose, and how can this be changed?

To see whether Seq interpreted a query as an expression or free text, look for the “free text” indicator in the Query/Filter box:

Free Text

If Seq interpreted your query as plain text, you can hover over this to get a description of why – either because of some syntax error in the query, or because of an heuristic being applied.

To force Seq to interpret an expression as text, quote it:

Quoted

(I love building little languages like this one – they really open up new ways of interacting with apps that can’t be expressed well using typical GUI components. If you’re wondering how Seq’s query language is parsed, interpreted and executed, let me know – I’ve been considering a post on it but there’s a lot of work in putting a good one together!)

Seq “Log Query of the Day” #2 – Properties

$
0
0

In the last post we looked at how to find log events based on text that they contain.

Searching for some text is often the first step in a log analysis session, but when it comes to navigating logs, structured data is far easier to work with.

In Seq, events have fully-structured, typed properties that can be used in queries. If you search for an event and expand it like the one below, you’ll see them:

Properties

This event was produced with a Serilog statement like:

log.Information("{CustomerName} removing {@CartItem} from cart", name, item);

You might have noticed that Seq highlights elements of the log message like the customer’s name and cart item object in this example. This shows that the highlighted part of the message is actually structured data that can be queried on; hovering over one of these will show the property name that holds the value.

In this case we can see that the customer’s name is stored in a property called CustomerName. Clicking the green tick beside the property name allows you to move on to find all events with that property value:

Equality

This is an easy way to get acquainted with Seq’s query syntax; you now see how a property name can be compared with a value using the == operator.

Text properties like CustomerName support ==, !=, as well as the Contains() function, case-sensitive comparisons and regular expression matching we talked about in the last post.

If your events contain numeric data, you get a few more operators: >, >=, <, <=, +, -, *, / and %, to be exact!

I’ll spare you the exhaustive list of Boolean operators, but they’re pretty much what you expect.

The CartItem property in this event appears as JSON in the log view. The original object passed in to the log method looked like:

class CartItem
{
    public string Description { get; set; }
    public decimal Total { get; set; }
}

(The example, in case you’re wondering, is from an old load-testing harness, so there are no points to be won for realism.)

It is natural to expect to query on these nested properties too, and in fact you can in the obvious way with the dot operator:

CartItem.Description == "Toothpaste"

Numeric property names

There’s one last thing to know when you’re working with properties in Seq. If you’re using the classic .NET format string placeholders 0, 1 and so on, either with Serilog:

Log.Information("New customer {0} logging on", _name);

Or, with NLog (which Seq now supports rather well) you can refer to the property by escaping the numeric index with an @, like:

"logging on" && @0 == "Nick”

(It’s often necessary to scope down a query like this with a fragment of text like "logging on" is used above, since the same numeric property names have wildly different meanings on each different event type.)

Properties are one of the central reasons Seq feels so different from text-based log handling tools; I hope you’ll give them a try and agree!

Seq is ready for prime-time

$
0
0

Last December I took the lid off of Seq, a log server for .NET that is built with first-class support for structured events. Well, in a great example of agile planning, the “Release Seq 1.0” ticket came in a long way ahead of “Announce Seq 1.0” so here, three weeks after our official release, is the post to let you know it has happened.

2014-03-23 11_23_35-Seq - .NET Structured Event Server

What does this mean?

It’s now safe to say that Seq is “going to fly” – we’ve had enough feedback to be confident that Seq is ready for use in anger, and commercially we’ve had enough interest, enquiries and sales to know that it is worth our while to keep driving the product forward.

What’s Seq and where’s it going?

Fundamentally, Seq helps you, your team and your stakeholders gain visibility into your applications – even when they’re distributed across multiple machines or locations.

Releasing our preview started a lot of conversations that ultimately shape how we see Seq going forward. This is the picture today:

  • Seq is driven by structured data – structured logging is an opportunity to get the visbility benefits of techniques like messaging or event sourcing, but without the deep architectural commitment. The time for structured logging is here, with better tools available than ever before, and Seq is going to be the #1 complement to those tools in the .NET world.
  • Seq is for development and operations – while there are great opportunities to use structured log data for analytics and data mining, Seq is primarily about removing friction when developing and operating systems. Integration points like Seq apps, email and tabular export make analytics and BI easy, when you need to surface data to other stakeholders.
  • Seq is on-premises – vendors love to provide cloud services, because recurring revenue and customer data retention make for a nice stable business model. This works well for many things, but when it comes to log data developers and the companies they work for value choice, and the right choice is often to store data on-premises or in fully-owned data centres. We feel so strongly about this that we’ve adopted it as a tag-line: “Visibility, your way.”
  • Seq is quick to set up – the foundations of good instrumentation are laid when the very first line of code in an app is written. At that point in the development cycle, YAGNI reigns supreme, so Seq comes with an MSI installer, a client on NuGet, and fits in to development with no more overhead than writing to traditional log files. 0-to-go in literally 5 minutes, and we’ll stay that way.
  • Seq is .NET – one of the great things about the diversity of software available these days is that there’s usually a solution that works well with your chosen tools. If you use .NET, Seq will be transparent, friendly and unsurprising for you.

These are going to be our guiding principles as Seq evolves, but we’re not limited by them. Seq already supports NLog and log4net as clients, despite neither of them having the deep structured logging support provided by Serilog. Similarly, we want to keep setup and maintenance of Seq simple, but an elastic storage/archival subsystem is definitely on the cards for a future release.

Who are “we”?

Continuous IT is a “micro ISV” based in Brisbane, Australia. Our team of two comprises me, covering product/technology, while Suzi (formerly an animation producer, and in all things manager extraordinaire) covers business development and keeps the wheels turning.

We’re in no small part influenced by the “sustainable start-up” model epitomised for me by Octopus Deploy where I spend most days. (Octopus’s founder Paul is a source of inspiration without which Seq may never have seen the light of day – thanks, Paul!) Our focus is on building a great product that we support wholeheartedly, rather than clocking up numbers the way many start-ups seem prone to doing.

Want to know more?

You can’t have a 1.0 without a Twitter stream, and as of last week we have this one. Head on over and follow us to stay in the loop! If you visit the home page you can find a few more contact options, including an email newsletter that we’ll send out as new features land.

We’ve been busy – the “Release Seq 1.1” ticket has been and gone, and a preview of Seq 1.2 is already on the site; there are some strong usability improvements in there – make sure you check it out!


Logging “levels” in a structured world

$
0
0

Every logging framework I’ve ever used has a notion of “levels” like “information”, “warning” and “error” that assign importance to events.

This is necessary because logging produces a lot of data – most of which is less exciting than trimming your dog’s toenails.

Except… when things go wrong, and suddenly that mundane note about successfully starting up saves months of debugging pain.

So, the essential idea of levels is to make it possible to turn “up” and “down” the amount of information being written to a log.

Serilog defines the following levels:

        // Anything and everything you might want to know about
        // a running block of code.
        Verbose,

        // Internal system events that aren't necessarily
        // observable from the outside.
        Debug,

        // "Things happen."
        Information,

        // Service is degraded or endangered.
        Warning,

        // Functionality is unavailable, invariants are broken
        // or data is lost.
        Error,

        // If you have a pager, it goes off when one of these
        // occurs.
        Fatal

Many logging frameworks have quite complex support for custom levels, and add on other features like categories and keywords so that control over logging output can be controlled at a finer grain.

In Serilog the levels are fixed – internally they’re represented in an enum, and there are no other built-in notions of categorization. Why is that?

You can create your own

Serilog events can carry any key/value property data that you like. If you want to introduce additional logging categories, you can just add those properties to loggers in your app, and do whatever you like with the data.

Let’s say in our architecture we’re interested in “subsystems” and want to categorize events that way:

var log = Log.ForContext("Subsystem", "Ordering");
log.Information("Checking status of {OrderNumber}…", num);
// More logging via ‘log’

This code sets up a logger that will tag all events with a value of "Ordering" for the Subsystem property.

Depending on your log store, you should be able to write a filter like this one to include (or exclude) events from the ordering subsystem:

Subsystem == "Ordering"

The nice thing about this approach is that you can create as many kinds of catgory as you need – subsystems, regions, organizational departments, whatever you like – and use all of them together.

Structured events make tighter control possible

Look at a verbose log. What do you see? Well, most of the verbosity will come from low-level events, but more than that: most of the verbosity will be due to a handful of events.

Classic text logging doesn’t deal well with this; since events are just blobs of text, there’s a lot of effort involved in turning off just one or two.

Serilog doesn’t have this issue. Each event has a MessageTemplate that uniquely identifies the event. Turning off a single event is just a matter of filtering out those with a specific template. Of course, you need a structured log store to make this work (Seq’s Event Types provide this for example) but it’s natural that if you’re using a structured logger you’ll be using one of the many storage options that maintain this ability.

With the most verbose offenders out of the way, even low-level logs can be quite pleasant to browse.

So what’s the point here?

Categorizing events up-front is less important when writing structured logs, and if you’re going to bother with anything finer that than the simple Information, Warning, Error and so-on provided out of the box by Serilog, you’ll be better served with a categorization system (or systems) of your own, than anything Serilog could define for you.

What’s to love about Seq 1.2?

$
0
0

The latest version of Seq is now ready to download from the site. It’s the best one yet, with a much smoother navigation experience and a bunch of small refinements that you’ll appreciate if you spend a lot of time with Seq each day.

Navigation

Digging into a lump of log data involves a lot of filtering – changing, adding and removing little search expressions to get a different cut of the events.

In Seq 1.0 we were at a bit of an impasse as to how this tied in with the browser’s history stack, so (I’m a bit ashamed to say!) we left it alone.

With the new version we’ve nailed this, I think.

First, filters typed into the filter box are included in browser history, so searching for “Alice”, then searching for “Bob”, then pressing back will again leave you at “Alice”. Simple win!

Bob

Furthermore, if you change views, this also is included in history. If you’re searching for an error message in Production and then switch to look for the same message in QA, pressing back will land you at Production again.
The final piece of the puzzle is the query, which remains as a saveable scratch area for filters that stay active regardless of browser navigation.

Viewport

In Seq 1.0 the initial result set returned for each query was 30 events regardless of your monitor size, and you had to click to see each additional page of 30 events.

No more in 1.2! The new version makes better use of the available space by filling the browser window with events.
Once you’ve requested more events with the “Older” button, Seq will start “infinitely scrolling” so events appear as you go further down the page.

The new version is ready and waiting at http://getseq.net.

XML configuration for Serilog

$
0
0

Some issues hang on! Serilog’s #3 — XML configuration support — was still grimacing at us from the backlog alongside issues with three digits, until it was unceremoniously closed a few minutes ago.

If you’ve spent any time with Serilog you’ll have noticed the absence of built-in XML configuration support. Something about the project just wanted – and wants – to be a code-centric thing. I guess it is part of lifting the status of logging a bit – ORMs for example moved on from XML configuration to code-first/fluent configuration many moons ago. Instrumenting your app with an event stream is an important thing that deserves a modern configuration API.

XML’s not at all bad though. File paths change, servers have different addresses in different environments, logging levels go up and down. XML’s a great fit for this and we’ve known that all along with Serilog; we’ve just chosen to keep it in its place and write code like:

var logFile = ConfigurationManager.AppSettings["LogFilePath"];

var logger = new LoggerConfiguration()
  .WriteTo.File(logFile)
  .CreateLogger();

Over time this gets boring and repetitive I admit. Thats why we’ve now got the Serilog.Extras.AppSettings package.

XML configuration with a twist

As I said – we like XML configuration in its place, but that’s a pretty small place. When I open up App.config or Web.config in a modern .NET project the amount of gunk in there can be pretty confronting.

Most XML config implementations provide their own <configurationSection>s, and those need to be declared, and then the XML syntax used within them generally gets copied from a long list of arcana like the one in the log4net documentation that I’ve visited for years on end.

Serilog’s “app settings” support is different; we use a technique I first saw suggested for Autofac module configuration several years ago.

This revolves around the <appSettings> collection, an element that’s present in almost every .NET configuration file. Here’s an example to illustrate what we’re doing:

<appSettings>
  <add key="serilog:minimum-level" value="Verbose" />

Instead of our own custom <serilog> element, we cheat a bit and just look for <appSettings> elements that match a certain pattern (their names start with serilog:). Simple idea, but effective: I’m going to write this whole article without checking the documentation or sample code even once! Remembering a setting name or pattern is much easier for me than remembering a whole pile of custom XML syntax.

Going a bit further, here’s the configuation for two sinks, the console and a log file:

  <add key="serilog:write-to:ColoredConsole" />
  <add key="serilog:write-to:File.path" value="C:\Logs\myapp.txt" />

(Notice you don’t actually have to provide a value for an app setting? I didn’t until recently, but works nicely!)

Where do the magic names ColoredConsole and File.path come from? They’re just encodings of the following method calls:

  .WiteTo.ColoredConsole()
  .WriteTo.File(path: @"C:\Logs\myapp.txt")

The name of the path parameter obviously wouldn’t be specified in normal usage, but you can see how the XML and code-based configuration match up 1:1.

The extension methods are looked up dynamically using reflection, so you can configure your own sinks this way. You can include multiple parameters for the same sink using multiple keys – for both features check out the wiki page linked below.

Enabling the package

Two steps: 1. Install the Serilog.Extas.AppSettings package from NuGet; 2. add a call to ReadAppSettings() when you’re configuring your logger:

var logger = new LoggerConfiguration()
  .ReadAppSettings()
  ... // Other configuration here, then
  .CreateLogger()

You can mix and match app settings with code-driven config, but for each sink you need to use one or the other.

There’s much more detailed documentation here, on the wiki.

But isn’t this super-limited?

I’m happy to trade configuation power for something I can actually remember how to use. Someday we might have a more sophisiticated XML configuration option than this, but unless you can write the PR – I’m served pretty well by this one and not in a hurry.

But this is totally not my style!

That’s fine; it’s an ‘extra’, meaning we ship it separately from Serilog’s core and you’re free to install it if you like it and ignore it otherwise.

Still, I’ll be happy to hear if you do find it useful!

One year of Serilog

$
0
0

I just pushed the first build of Serilog 1.3 to NuGet. This one includes a more flexible JsonFormatter and support for width specifiers in format strings. Not huge changes, but both called for obsoleting some existing methods, and with 1.2 at the venerable age of “.53″ it was time to rev and get back to the nice simple version “1.3.1″.

It occurred to me that we’ve now hit the one year mark in the Serilog project. Version 0.1 was announced on March 29th, 2013 actually!

If there was any doubt back then of the viability of “yet another logger” in the .NET ecosystem, it’s been well and truly dispelled in my mind. Sixteen of us have now contributed to the project, and what’s most exciting to me is that the developers extending, using and discussing Serilog are some of the best and brightest I know. It’s the kind of momentum that makes me confident we’ll see more use and growth in the next year.

For those who follow it, the mention of structured logging as a technology to “trial” in the January 2014 ThoughtWorks Technology Radar is also a good indicator that Serilog is aligned with the facilities .NET developers will come to expect and use widely.

Our 1.0 came only six months ago, so these are still early days, but there are now 38 packages tagged “serilog” on NuGet, and despite the more-than-occasional glitches in the counter ;) the core Serilog package has over 7,400 downloads.

Visits to http://serilog.net are also looking healthy:

Serilog-One-Year

April’s been a visibly good month, with a lot of interest stirred up by a discussion on .NET Rocks! where I did my best to “get the word out” about how Serilog has totally transformed my thinking about logging.

If you’re using Serilog it would be great to hear more about how it has worked for you, and where you think the project should be aiming next – get in touch here or on our discussion list!

Seq 1.3 – what’s new?

$
0
0

Seq 1.3 is now available at getseq.net. Since version 1.2 we’ve introduced the ‘dash’ – a hub for your most-used queries and bookmarked events – and made refinements to many other parts of the app.

Watches

Seq is designed for keeping tabs on your running applications. For the really important events, you’ll probably use email alerts to be notified when something needs attention, but if you’re like me you’ll have your finger on the pulse of quite a few things, not all of them critical.

For that, Seq 1.3 introduces watches – a saved view over the event stream that Seq will monitor for new events. When an event matches a watch, after a short delay you’ll see that on the shiny new dash:

Dash 1.3

Clicking through to view the matching events will reset the watch counter to zero.

To start watching a view, query, filter or combination of those things, drop down the Refresh button and select Add watch.

AddWatch

Bookmarks

Alongside watches on the dash you’ll also find bookmarks. A bookmark is just a link to an individual event, but for as long as the event is bookmarked it won’t be cleaned up by any retention policies you might have set.

Bookmarks are added from the new Pin menu that appears on the detail view of each event.

AddBookmark

Assignments

You can also bookmark an event for another member of your team by choosing the Assign to another user item under Pin.

Assign

This is called an assignment and will show up, with some notes if you include them, on the other person’s dash.

Permalinks

If you need to link an event to send it by email etc. you can still create a permalink – these are what they imply, and now also save the event from any applicable retention policy.

Causal ordering

Hiding away in Seq 1.3 is a new killer feature: the “arrival ordering” queries in Seq 1.3 make debugging causality easier and more fun.

Let’s say we’ve hit on an interesting event: a crash or something out of the ordinary that we need to investigate.

FindWithPredecessors

In the event details, the new Id drop-down includes Find with predecessors. This will restrict the event stream so that the event we’re viewing is at the top.

Now, by requiring this filter in the query (press Require) we can slice the predecessors of the event along whatever axes we like. What was happening on the machine before this event?

OnMachine

Whatever filter we add, the results will be shown up to and including the event we chose. We can find the last error logged anywhere before this one; the last event logged for the same customer; the last events in the same web request…

Tedious questions to try to answer with a flat log file, but now an absolute breeze with Seq.

Offline compaction

A side effect of the current storage engine Seq uses is that sometimes fragmentation may cause the event store to take more space on disk than it needs. We’re digging deeply into the storage subsystem in coming releases and will ideally provide full online compaction, but in the interim if you find your event store is disproportionately large, you can run:

seq.exe stop
seq.exe compact
seq.exe start

…to reclaim some space.

Virtual directory support

Seq has always allowed listen URLs with deep paths, however in past versions some bugs in the UI meant these didn’t fully work.

In Seq 1.3 you can now set up multiple instances on the same server at nested paths, e.g.

http://my-seq/dev
http://my-seq/qa

To find out more about setting up instances see the documentation.

Help has moved

We’re putting all of our documentation efforts into improving docs.getseq.net so it feels only right that help in the application will take you there.

But there’s more!

  • Boolean queries and comparisons with null now match their documented behaviour
  • The installer handles reconfiguring service paths
  • The filter ‘clear’ button is now clickable even with focus on the input field
  • IE ‘compatibility mode’ is now avoided when Seq is run as an intranet site
  • @Id can now be used in filters
  • The ‘single event view’ now correctly supports further filtering on properties of the displayed event
  • Filter history is auto-completed within a session
  • JSON formatting is improved

Actually, it’s hard to capture just how much has changed in the last short month of development. Give Seq 1.3 a try – we think you’ll enjoy the improvements!

Download Seq 1.3 from the project site. The very handy Developer edition is free, and you can request a 30-day trial of Seq Enterprise if you would like to try out multi-user support.

Logging and locked files

$
0
0

Serilog 1.3.5 includes some important improvements to the rolling file sink.

No doubt the least pleasant aspect of dealing with log files is concurrency. When multiple processes write to the same log file, either:

  1. They need to coordinate writes using system-wide mutual exclusion (unreliable)
  2. They need to wait for the file to become “free” and close it immediately after writing (slow)
  3. One of them must fail (inconvenient)

Serilog takes a bit of a stand on this and chooses option 3), requiring that each process uses its own log file, or set of rolling files.

This has the advantages of predictability and performance, and I’m happy with the choice.

But, there’s a catch, and it is a nasty one.

IIS and overlapped application pool recycling

When an IIS app pool is “recycled” (shut down and re-started to clean up any leaky resources) the IIS pipeline’s pretty smart about it. Rather than leave a site offline between unloading the old worker process and loading the new one, IIS starts the new process first and only unloads the old one once the new one is serving requests.

Unfortunately, this means the new app pool needs to open a log file still locked by the outgoing process. No logs for you!

Disabling overlapped recycling

For typical intranet sites, or web-facing sites behind a load balancer, disabling overlapped recycling isn’t a huge issue. To date this has been the solution for using Serilog with IIS. But, it isn’t an option for some, and happily those “some” were loud and determined enough to get a better solution into Serilog 1.3.5.

The old rolling scheme

Serilog still doesn’t handle locked single-file logs. Generally these kinds of logs aren’t used in e.g. IIS sites, so the old caveats of a file-per-process still apply if you use WriteTo.File().

Rolling files however already provide a model that maps quite naturally to the “rolling over” of IIS worker processes.

A sequence of files written by the rolling file sink might look like:

myapp-20140501.txt
myapp-20140502.txt
myapp-20140503.txt

As the days pass a new file is created, and eventually the old one is removed.

The fix: rolling when a file is locked

Now, assuming that it’s the third of May, if the IIS app pool is recycled you’ll see a sequence like:

myapp-20140501.txt
myapp-20140502.txt
myapp-20140503.txt
myapp-20140503_001.txt

The _001 file will be used by the new worker process, while the old one hangs on to the non-prefixed file.

Some guarantees

What happens when the new process, using the suffixed file, is recycled?

Well, the simplistic response would be for the replacement to open the non-suffixed file, since that is now in all likelihood unlocked. But this would make reading the files a pretty awful experience, as date/time ranges jumped around between them.

Instead, the new process will create:

myapp-20140503_002.txt

Important notes

The only caveat to note here is that the implementation is robust to the scenarios that commonly occur when restarting/recycling processes. It doesn’t address long-lived concurrent processes, for that you’ll need to decide on an alternative scheme (or use a more concurrency-friendly sink than what the file sinks can provide).

You should also be aware that the retained file count provided by the rolling file sink is per-file, not per-day, so if you expect frequent overlapped log file recycling, you should allow a few more retained files to ensure you keep the required date range. We’ll probably implement a retained-days count in the future.

Is this the long-term plan?

This is the long-term solution to locked files in Serilog, at least as far as any current plans cover. How does it work for you? Serilog’s a community project and always open to constructive feedback and good ideas.

seq-422px

Serilog output template formatting changes

$
0
0

The latest Serilog version 1.3.7 fixes some long-standing text formatting quirks.

Serilog has a bit of an idiosyncratic take on formatting text. You could probably say the same about its designer: before Serilog, I used to write a lot of log statements like this:

Log.Information("Could not find documents matching '{0}'", searchTerm);

If you look carefully you’ll note an extra couple of single quotes (') in there around the the {0} token that will be replaced with the search term being logged. I got into this habit because when reading logs I find it less jarring to see random string data quoted, so that my brain doesn’t try to join the whole lot up in a single bewildering sentence:

Could not find any documents matching my pants

vs.:

Could not find any documents matching 'my pants'

String formatting in Serilog message templates

When I started writing Serilog I thought I’d save a few characters, and encourage consistency, by making this little idiom part of its string formatting engine:

Log.Information("Could not find documents matching {Term}", searchTerm);

yields:

Could not find any documents matching "my pants"

I’m sure there are plenty of people who’d rather a different default, but so far no one’s raised it as particularly problematic. If you need to match a specific output format you can use a special “formatting” specifier to turn this off – just append :l (yes, a lowercase letter L) to the token:

Log.Information("Could not find documents matching {Term:l}", searchTerm);

yields:

Could not find any documents matching my pants

Reusing message templates for output

The formatting above is applied when rendering log messages into text. A bit later in the design process, I think some time around when Serilog became a public project, we needed another formatting mechanism, this time to control how the different parts of the log event including the message get laid out e.g. for display at the console.

Log.Logger = new LoggerConfiguration()
  .WriteTo.Console("{Timestamp:G} [{Level}] {Message}{NewLine:l}{Exception:l}")
  .CreateLogger();

You’ll notice the same style of format string appears in there. The engine used to write the text is the same; there were (and are) a lot of advantages to reusing this code for “output templates” like this.

Unfortunately, some of the decisions that work well when rendering a structured message, get in the way when presenting text like this. You’ll see above one of the oddities – the pre-defined NewLine and Exception properties are represented as strings under the hood, so the :l literal specifier has to appear in there to avoid unsightly quotation marks appearing in the output. Not a big deal really, but too much to have to grasp as a new user.

The other issue is that in message templates, i.e. when calling Log.Information() and friends, it’s an error to provide less arguments than the number of tokens that appear in the string. Serilog helpfully prints out the whole token as text – {Token} so that these issues are obvious when reading the rendered message.

In output templates this isn’t so desirable. Using {HttpRequestId} in the format string should just be ignored if the event doesn’t have such a property attached.

Serilog 1.3.7 changes

I almost versioned this one “1.4″, given there’s quite a change being made, but since the API is binary-identical this one’s out as a patch.

Put simply, Serilog now has slightly different handling of message templates vs. output templates. Message templates keep their current behaviour – quotation marks and all. When used to configure a sink as an output template however:

  • The :l specifier is now “on by default”
  • Missing tokens will simply be ignored

The default output templates all used :l formatting anyway, and so will almost all custom ones, so for 99% of current users I’d say this change will go unnoticed. If you define your own output templates though you can now clean up a bit of clutter and remove the literal specifier from fields like {Exception} and {NewLine}.

If you’re coming to Serilog as a new user, hopefully these changes give you one less reason to go looking for explanatory posts like this one!


Serilog gets a mention at NDC Oslo 2014

$
0
0

It was really cool to see this session from NDC about Elasticsearch/Logstash/Kibana featuring Serilog from just after 16:00.

Monitoring your app with Logstash and Elasticsearch from NDC Conferences on Vimeo.

It’s fun watching Serilog presented by people who obviously “get it”. And, compared with the alternatives shown earlier in the session, it’s great to see just how easy Serilog is for generating quality structured logs.

(If you’re not using Serilog and interested in the demo code, it’s on GitHub, but please check out this tiny PR if it hasn’t made it through yet.)

Seq 1.4 preview is out!

$
0
0

The features planned for Seq 1.4 are starting to come together. There’s a new preview available to download on the Seq website (direct download).

While 1.4 is still a little way off “done”, many of the improvements we’ve made are usable today; here’s a run through what you can expect from the new version.

What’s Seq? Seq is the fastest way for .NET teams to improve visibility using structured application logs. If you’re using Serilog, you’ll find Seq is the perfect partner – trivially easy to set up, and built for structured log events so querying them is simple from “day 1” of a project. You already use CI and automated deployment: Seq is the next piece of the puzzle, closing the loop to show your team what’s happening in development, testing and production.

Charts

New in 1.4:

Counters-Downloads

Charts show the count of matching events in a time period. You can view the last day (by hour), last week (by day), last month (by day) or last year (by week). It’s a simple feature that I’m sure we’ll expand on, but already a great way to keep a “finger on the pulse”.

To add a chart, filter the “events” view to show just what you want to chart, then drop down the “refresh” button and select “Add to dash”. By default, queries are shown on the dash as counters; to toggle between the counter and chart display, there’s a little icon in the top left that appears on hover.

HowToChart

(If you’re already using counters with Seq 1.3, you’ll need to recreate them before historical data will be indexed by the 1.4 preview.)

Quick(er) switching between views

A much-requested improvement: when no view is selected, the first 10 views will be shown in a list (saving a few clicks when switching between views).

ViewSwitching

Extended text operators

A bunch of work has gone into better string comparison functions in this version. Seq queries now support:

  • Contains(text, pattern)
  • StartsWith(text, pattern) and EndsWith(text, pattern)
  • IndexOf(text, pattern), and
  • Length(text)

In each of these, “pattern” can be case-insensitive, case-sensitive, or a regular expression. There’s some more information and examples in the documentation.

Date functions and @Timestamp

Events in Seq now carry a queryable @Timestamp property, and we’ve added a DateTime() function that can be used with it. Check out the documentation for some examples here.

Keep the feedback coming!

Download the preview, try it out, and let us know what you think! You know where to find me :) – and there’s also a Seq discussion group we’d love you to join, and a Seq Twitter account if you’d like to keep in the loop.

Can we change the face of JavaScript logging, too?

$
0
0

If you use Serilog in .NET and write code for the web, chances are you’ve wondered whether similar hybrid “structured/text” loggers exist for the browser and Node.js

serilog-js

If you haven’t tried Serilog, here’s a one-minute primer (using JavaScript syntax) so you can follow the rest of this post. Using Serilog is similar to every logging framework, ever:

var name = "Nick";
var wait = 45;
log("Hi {name}, your flight leaves in {wait} minutes", name, wait);

Look carefully at the string formatting though – particularly the way we give names to each of the parameters. The “message” part isn’t destructively rendered into text, but preserved as an object with property values. It can be formatted to the console etc. as usual:

[inf] Hi Nick, your flight leaves in 45 minutes

But more interestingly, it can be serialized in a format like JSON for processing and querying:

{
  "message": "Hi Nick, your flight leaves in 45 minutes",
  "messageTemplate": "Hi {name}, your flight leaves in {wait} minutes",
  "properties":  {
    "name": "Nick",
    "wait": 45
  }
}

Preserving the structure of log data, and making it trivially easy to enrich that data with more contextual information, makes it possible to do some really clever things with the resulting logs.

Try finding all messages where the user’s name starts with “N”, or where the wait time is greater than one hour in a traditional log file if you’re skeptical here. In some trivial cases a regular expression might handle this cleanly, but it quickly becomes complex and cumbersome when more data is involved. Using Serilog it can be as simple as startswith(name, "N") or wait > 60.

Preserving the message template itself ("Hi, {name}...") also has some interesting and very useful results: the message template becomes a kind of “message type” that can be used to find – or exclude – messages of a specific type. Again very hard, often impossible with traditional loggers.

What’s going on in JavaScript?

In JavaScript as elsewhere, there are already some sophisticated logging frameworks, with varied levels of structured data support. A fairly new project called node-bunyan interests me the most, with its clever DTRACE integration and support for serializing objects instead of messages, but even here the new hybrid-style API and DSL provided by Serilog is missing. With node-bunyan it’s possible to log an object and/or a message, but messages are still formatted out in a lossy manner like the earlier loggers on .NET did.

Whether it grows into a standalone project in its own right, or encourages more established frameworks to take a look at this new style of logging, bringing Serilog’s API to JavaScript seems like a fun and worthwhile thing to try.

serilog.js

Tinkering away over a month of so I’ve sketched out what I think serilog.js should look like.

serilog.js-example

Yes, serilog.js terminal output is type-aware, just like syntax highlighting in an IDE! It’s useful when dealing with structured events to see the difference between a number 5 (magenta for numbers) and a string "5" (cyan for strings). In itself this feature is a nice trimming, but it’s not just a party-trick; below the surface, the event is carrying all of this information.

Getting started – installing from NPM

All the code is currently published to NPM. At the command line:

npm install serilog

Then in your node application:

var serilog = require('serilog');
var terminal = require('serilog/src/serilog-terminal-sink');
var file = require('serilog/src/serilog-file-sink');

Serilog “sinks” are destinations for log events. This example uses the terminal sink, which writes to the system terminal/console. At the moment, the sinks are all zipped up directly in the serilog package, but they need to be split out.

Tour of the API

At present, with very little of the back end ‘sinks’ built, serilog.js is all about the API.

Configuring the pipeline

One of the first things to notice is that the logger is a pipeline. Thinking of log events as structured data leads naturally to an API more like what you’d find in event processing frameworks than the typical logging library.

var log = serilog.configuration()
  .writeTo(terminal())
  .minimumLevel('WARNING')
  .writeTo(file({path: 'log.txt'}))
  .createLogger();

Each element is applied in turn:

  • serilog.configuration() kicks things off by creating a new configuration object
  • .writeTo(terminal()) emits all events to the terminal sink
  • .minimumLevel('WARNING') filters out any that are below the WARNING level (we’ll cover levels below)
  • .writeTo(file({path: 'log.txt'}) emits the events that reach it to a log file, and
  • .createLogger() takes all of these pipeline stages and bundles them up into an efficient logger object

The result is log, which we’ll use below. (As in Serilog for .NET, there’s no “global” logger – multiple loggers can be created and are completely independent, unless you link them together with something like .writeTo(log2)).

You might be wondering – why not Node.js streams or RxJS etc.? Simple really – generic APIs are great for glueing together solutions, but for something that’s used extensively in a codebase, maintaining complete control over dependencies, performance and semantics is a good idea long-term, and that’s the path taken here.

Logging levels

serilog.js has four simple logging levels, listed here in order of importance:

log.trace('This is a lot of detail');
log.information('Something of note happened');
log.warning('Look out, wheels are coming off!');
log.error('Something's broken');

If log.information() looks like a bit of a mouthful, don’t worry: the log object itself is a function that can be called as a synonym for information:

log('This is an information-level event');

The default level for a new logger is INFORMATION; using .minimumLevel('TRACE') at the start of the pipeline will bump this up.

Message templates

The syntax of message templates is carried over directly from Serilog. Event properties are named by including them in braces, and the values are provided as positional arguments:

log('{a} + {b} = {c}', 1, 2, 3);
// -> {properties: {a: 1, b: 2, c: 3}}

I’ve left the message/template/timestamp out of the example output (// -> ...) to keep the focus on the structured part of the event, but these other parts of the event exist too.

If you pass an object to the log, e.g.:

var mail = {to: 'nblumhardt', subject: 'Is the post finished?'};
log('Sending {mail}', mail);
// -> {properties: {mail: '[Object] object'}}

You might be surprised that it’s stored using toString(). We haven’t missed a great opportunity to serialize here – the problem is that it is very easy to serialize too much data by mistake this way.

If you know that what you’re passing into a log method is a nice little chunk of data to work with later, prefix the property name with @ to say “I know what I’m doing – serialize this!”:

var mail = {to: 'nblumhardt', subject: 'Is the post finished?'};
log('Sending {@mail}', mail);
// -> {properties: {mail: {to: 'nblumhardt', subject: 'Is the post finished?'}}}

serilog.js can handle structured storage of objects and arrays nicely, and these can support some really cool processing scenarios – just don’t forget the @ (if you watch the events roll by on the terminal, you’ll notice the difference too – objects are colored green, while strings render in cyan).

Context and enrichment

The strength of structured logs is in correlation; this is one of the great challenges when instrumenting distributed apps, and probably the #1 reason that interest in structured logs has picked up again in recent times.

When searching a large number of events produced across many machines, it’s vital to have properties to filter on so that a single location or thread of interaction can be followed.

In a simple case, you might want to instrument all events with the name of the machine producing them. This can be done when configuring the logger:

var log = serilog.configuration()
  .enrich({machine: env.HOSTNAME})
  ...

Events created by the logger will all carry the specified properties:

log('Hello, {name}', 'Nick');
// -> {properties: {name: 'Nick', machine: 'NBLUMHARDT-RMBP'}}

More often though, it’s desirable to tag events produced within a part of the application, or in a transaction. For this the using() method is provided on the logger itself:

var smtpLog = log.using({subsystem: 'SMTP'});
smtpLog('About to send mail to {name}', 'Nick');
// -> { properties: {subsystem: 'SMTP', name: 'Nick'}}
Filtering

A final part of the API to check out is filtering. While most filtering will happen server-side wherever you collect your logs, it can be useful to apply some filters in the logging pipeline if you have a component producing a lot of noise, or if you need to avoid passing events with sensitive data to logs.

This fits in as you’d expect:

var log = serilog.configuration()
  .filter(function(evt){
    return evt.messageTemplate.raw !== 'Making {noise}' &&
      evt.properties.subsystem !== 'SMTP';
  })
  .writeTo(terminal())
Building serilog.js

If you want to tinker with the project, you can grab the source from GitHub. The build process uses gulp to drive some mocha tests. Just type:

npm install
gulp

…and you’re away!

The browser

serilog.js also runs in the browser (though version support will be patchy and is completely untested!). There are some notes on the GitHub site linked above on how to do this, and a test.html page in the repository that sets some of it up.

The catch

Serilog in .NET is out there in production use today. It’s being extended and refined by a core group of contributors and takes what time I have available to coordinate.

I’ll be using serilog.js in a few small JavaScript projects and extending it for my own needs, and I’ll be building a sink that writes to Seq at some point. But, I don’t have the capacity to really drive this project beyond the prototype that it is right now.

If you’re interested in JavaScript, event processing, application monitoring and/or API design, serilog.js needs you. Kick the tyres, check out the code, send a pull request! It’s a project “up for grabs” in the largest sense. Can you help change the face of JavaScript logging?

Durable log shipping, from Serilog to Seq

$
0
0

A few days ago I took an impromptu snapshot of one of the Azure machines I have running Seq. Cloud storage being what it is, an I/O problem during the snapshot process crashed the Seq service, but I didn’t notice until the following day. The machine was running horrifically slowly at this point, so I restarted it and all came good.

When I logged into Seq to check some queries, I expected to see a big gap during the outage. It took me a few seconds to realize why there wasn’t one – all of the events raised during the outage period were there! – and that made me think this feature of the Seq client for Serilog might not be as well-known as I’d assumed.

When you set up Serilog to write events to Seq, you use code typically like:

Log.Logger = new LoggerConfiguration()
  .WriteTo.Seq("http://my-seq")
  .CreateLogger();

This will send events in batches directly using HTTP; network problems and hard app crashes can result in event loss.

Some time ago, another parameter became available:

Log.Logger = new LoggerConfiguration()
  .WriteTo.Seq("http://my-seq", bufferBaseFilename: @"C:\Logs\myapp")
  .CreateLogger();

If a buffer location is specified, the Seq client will write events to a set of rolling files like "myapp-2014-06-27.jsnl" (newline-separated JSON) and then ship them to the server when it’s possible. Almost nothing to configure, and voila! Reliable log collection.

There’s still no substitute for a redundant, transactional data store for truly critical events: machines can be lost, disks fill up and so-on. For general application monitoring and diagnostics however, I’m really pleased with how much “bang for the buck” this feature provides.

Filtering events on arrival at Seq

$
0
0

Collecting too much log data makes the “needle in the haystack” all the more difficult to find, but for practical reasons it is often easier to collect as much data as possible anyway and “sort it out later” via Seq retention policies.

These have served us well since the early versions of Seq, and selective retention is a feature that’s been widely appreciated by users. Retention policies do come at a cost however – as events are removed from the stream, space in the storage engine needs to be compacted and this can use significant resources on the server (CPU, RAM and I/O).

In many cases, it is preferable to drop uninteresting events as they arrive. This facility is now available in Seq 1.4.2-pre as “input filters”.

Each Seq API key can now specify a filter expression – just like the event stream supports – that identifies events that will be accepted when logged with the key:

InputFilter

A typical use case may be to drop events below a certain level, but any criterion can be included.

If for any reason it becomes necessary to change the filter during operation, updating it takes immediate effect so that more or fewer events can be recorded.

It’s a simple feature right now that we’re looking for feedback on – if you can grab the new preview from the download page (preview link is at the bottom) and try it out it would be great to hear your thoughts!

Not currently in the preview, but also coming in 1.4 is a MinLevel() query function that will tidy up filtering by level like the example shows.

Viewing all 102 articles
Browse latest View live