Log Management and Analytics

Explore the full capabilities of Log Management and Analytics powered by SolarWinds Loggly

View Product Info

FEATURES

Infrastructure Monitoring Powered by SolarWinds AppOptics

Instant visibility into servers, virtual hosts, and containerized environments

View Infrastructure Monitoring Info

Application Performance Monitoring Powered by SolarWinds AppOptics

Comprehensive, full-stack visibility, and troubleshooting

View Application Performance Monitoring Info

Digital Experience Monitoring Powered by SolarWinds Pingdom

Make your websites faster and more reliable with easy-to-use web performance and digital experience monitoring

View Digital Experience Monitoring Info

Join SolarWinds Day October 19 to see the next evolution in observability. Register now.

Use Cases

What Is Structured Logging and How to Use It

Start Free Trial

Fully Functional for 30 Days

Writing logs in a machine-readable format ensures the log can be leveraged by software.

What Is Structured Logging?

First, to learn what structured logging is, you must take a step back and understand what exactly is unstructured logging.

With unstructured logging, events are expressed in plain text—in other words, plain English (or another language). The assumption here is that humans are the main target audience for logs, which isn’t always the case.

For instance, being able to search through your log files to find all occurrences of a given event is valuable if you’re trying to troubleshoot some issue or investigating a concerning trend.

Structured logging makes this easier by generating logs in more easily parsable formats—such as JSON and XML. This way, you can treat your log events as data rather than mere text.

Why Use Structured Logging?

Structured logging addresses the limitations of regular logging, bringing benefits for different roles in the organization:

  • Developers:
    • Easier search. With structured logging, developers can more easily search through events, making troubleshooting defects easier.
    • Easier integration. A consistent format for logs makes it easier when an application needs to consume log data from a different one.
  • Administrators
    • Improved readability. Structured logging doesn’t only apply for machines. Ensuring logs from different sources use a consistent format makes life easier for system administrators when they need to read raw log files.
  • Business
    • Easier parsing. Structured logging ensures log events can be easily parsed, which subsequently makes it easier to process log files for business intelligence or analytics purposes.
    • Reduced costs. The use of inconsistent log formats requires custom software to parse them. Development resources have to be diverted to do that work, which might be a waste, especially when you consider opportunity cost.

Structured logging acknowledges that machines are the most common readers of logs nowadays, while also striking a nice balance with the need for human readability.

Structured Logging: An Example

A Humble Start

Say your application lets users create reports, which you want to log. The bare minimum log message for that could look like this:

2021-07-29 | New report created

You have the date in ISO-8601 format, followed by the message. It could use some more context, such as the time of day and a log level to express the severity of the event:

2021-07-29 14:52:55.1623|INFO|New report created

Things are getting better. Let’s also include the ID of the user who created the report:

2021-07-29 14:54:55.1623|INFO|New report created by user 4253

It wouldn’t be a stretch to imagine log events recording different occurrences:

INFO|New report created by user 4253
INFO|Report updated by user 257
INFO|User 478 deleted report 

As you can see, users can also change and delete ports. Also, the last example doesn’t use the same format as the other two.

Here Comes the Problem

Now comes the tricky part. Let’s say you need to search through your log events to find all instances of a given user performing some action to reports. That’s not so easy to do, thanks to the inconsistent format of the messages.

Besides reports, users can create other types of artifacts, and such activities are also logged. With the right amount of regex, you’ll probably be able to pull it off. However, such code is error-prone, fragile, and doesn’t generate the most value for you and your customers.

Structured Logging to the Rescue

Structured logging solves the above problems by generating easily parsable logs. For instance, the following is an example of how we could structure the events in the example using JSON:

{
    "TimeStamp": "2021-07-29 14:52:55.1623",
    "Level": "Info",
    "Message": "New report created",
    "UserId": "4253",
    "ReportId": "4567",
    "Action": "Report_Creation"
}

We have properties that indicate what action occurred and the identifications of the relevant objects, such as the user and the report created. Parsing a collection of events in the format above makes it easier to search through events than it would be with plain text.

How to Get Started With Structured Logging: A Quick Tutorial

You can use structured logging in any major language. However, we have to pick a language for the tutorial, so we’ll go with C#. And despite there being alternatives, we’ll pick Serilog for our logging framework.

Requirements

Before we start, let’s make sure you have everything you need:

Obtaining the Application

For brevity’s sake, I won’t walk you through creating a .NET app. Instead, there’s a sample app you can get from this GitHub repo. Clone the repo using Git or download the whole application as a .zip file.

Starting

After cloning or downloading the application, access the extracted/cloned folder and open it using your favorite editor. You should see the following structure:

We have properties that indicate what action occurred and the identifications of the relevant objects, such as the user and the report created. Parsing a collection of events in the format above makes it easier to search through events than it would be with plain text.

How to Get Started With Structured Logging: A Quick Tutorial

You can use structured logging in any major language. However, we have to pick a language for the tutorial, so we’ll go with C#. And despite there being alternatives, we’ll pick Serilog for our logging framework.

Requirements

Before we start, let’s make sure you have everything you need:

Obtaining the Application

For brevity’s sake, I won’t walk you through creating a .NET app. Instead, there’s a sample app you can get from this GitHub repo. Clone the repo using Git or download the whole application as a .zip file.

Starting

After cloning or downloading the application, access the extracted/cloned folder and open it using your favorite editor. You should see the following structure:

Inside the src folder, you’ll find the SampleApp folder and, inside it, the Program.cs class. Open it. Here’s what it looks like:

It’s a very simple app: it prompts you to type a username. If the username is a valid GitHub user, it then displays the list of public repositories for that user.

Adding Serilog to Our App

The next step is to add Serilog to our app. We’ll do that using Nuget.

Open up your terminal and navigate to the root folder of the project. Then, run the following commands:

cd src/SampleApp
dotnet add package Serilog --version 2.10.0
dotnet add package Serilog.Sinks.File --version 5.0.0
dotnet add package Serilog.Formatting.Compact --version 1.1.0

With the commands above, you navigate to the project’s folder and install the necessary packages we’ll need to work with Serilog.

Serilog has the concept of sinks. These components effectively write your log message to its destination. The list of available sinks for Serilog is long. For our tutorial, we’re using the file sink.

Let’s Write Some Logs

Configure Your Logger

First, start by adding the necessary using statements to the top of your class:

using Serilog;
using Serilog.Formatting.Compact;

Subsequently, configure your log. Right at the top of the Main method, add the following lines of code:

Log.Logger = new LoggerConfiguration()
                .WriteTo.File(new CompactJsonFormatter(), "log.json")
                .CreateLogger();

With the lines above, you create a new logger that uses a file sink, which will write to a file called “log.json” using a JSON formatter.

Creating Your First Structured Log

You’re now ready to log something using the structured approach. In the code, go immediately after this line:

WriteLine($"The user {user.Name} was successfully retrieved!");

Then, paste the following code:

var result = new {
                    Action = "USER_RETRIEVAL",
                    Success = true,
                    Fullname = user.Name,
                    Login = username};

  Log.Information("{@Result}", result);

In the lines above, we create a variable using C#’s anonymous types feature. The object represents the fact that we’ve successfully retrieved a GitHub user.

Then, we use the logger to log the object with the Information level.

Now you can simply run the app and type a valid GitHub username when prompted. Then, navigate to the output folder of the project and you’ll find a log.json file there. Open it and you’ll see something like this:

{
    "@t": "2021-07-30T00:08:03.2619130Z",
    "@mt": "{@Result}",
    "Result": {
        "Action": "USER_RETRIEVAL",
        "Success": true,
        "Fullname": "Phil Haack",
        "Login": "haacked"
    }
}

(For my tests, I’ve used Phil Haack’s username.)

We get a nice JSON containing the properties of the result object we’ve created and the timestamp for the event. A notable absence is the log level. However, this is by design: omission of level implies INFORMATION.

For the next step, we’ll add a log to the part of the code that handles a user not found.

Creating Your Second Structured Log

Right after this line:

WriteLine($"{username} isn't a valid GitHub user!");

Paste the following code:

var result = new {
                    Action = "USER_RETRIEVAL",
                    Success = false,
                    Login = username};

Log.Error("{@Result}", result);

The code is virtually the same code we’ve used before, with only two differences:

  • The result anonymous object doesn’t have a Fullname property since the user wasn’t found.
  • We’re now logging with the Error level instead of Information.

To test, you only have to run the application and, when prompted, type an invalid username.

After that, go back to the log.json file, and you’ll find a new entry:

{
    "@t": "2021-07-30T00:18:18.4842413Z",
    "@mt": "{@Result}",
    "@l": "Error",
    "Result": {
        "Action": "USER_RETRIEVAL",
        "Success": false,
        "Login": "itsharderthaniexpected"
    }
}

Now, the resulting JSON contains a property depicting the log level.

Conclusion

No matter how great your QA strategy is, things will go wrong in production. That’s why logging is so valuable: it’s an essential part of troubleshooting applications.

What should your next steps be? For starters, continue to learn about structured logging, such as message templates.

Then, maybe you’ll want to start looking at tools to take your logging approach to the next level, such as log management/log analysis tools.

SolarWinds® Loggly® is a cloud-based log management tool you can use to aggregate and analyze your logs from virtually any source. To make things better, Serilog’s long list of sinks include a sink to Loggly. We invite you to create your Loggly account today.

SolarWinds Loggly

See unified log analysis and monitoring for yourself.
Start FREE Trial

Fully Functional for 30 Days

This post was written by Carlos Schults. Carlos is a consultant and software engineer with experience in desktop, web, and mobile development. Though his primary language is C#, he has experience with a number of languages and platforms. His main interests include automated testing, version control, and code quality.