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!)