Jordan Cuadrado's blog

Logging that Data

Written by Jordan Cuadrado | Jan 17, 2026 2:27:15 PM

We programmers tend to focus all our efforts on building the application, and when it's finally complete, we ship it out to the world. Unfortunately, in our drive to push out the application, we often place logging on the back burner. When bugs appear, we always wish we had implemented it earlier.

What does it mean to log?

Logging is the action of storing data for reference outside of the application. The stored data usually falls into the following categories:

  • Trace - Level 0
  • Info - Level 10
  • Debug - Level 20
  • Warnings - Level 30
  • Errors - Level 40
  • Critical - Level 50
  • Fatals - Level 60

The level numbers themselves aren't as important as the category names. Generally speaking, the more critical the issue, the higher the level.

When logging is implemented correctly, it collects all the necessary data throughout the application and stores it in a place that can be easily accessed at any time, regardless of the application's current state (running or not).

This ability allows developers to review logged data for debugging or performance analysis—especially crucial for applications that have crashed. If the application isn't running, it can't provide real-time error feedback, but the logs stored externally can.

How to start logging?

We're going to tackle this in a simple way—by creating a logger ourselves! While many packages across various languages and frameworks accomplish this, there's no better way to learn than by doing it yourself. At a high level, we know what we want: to store information from our application in an external location for future reference.

The text file

As a fun fact, all loggers start with a simple .txt file. As logging needs become more complex, the process may "evolve" into a more structured system, but it all begins with a basic text file—and that's where we'll start.

To get started, we'll use C#/.NET 8 to create our simple text file, but you can do this in any language:

All we're doing here is choosing a location to store our new file, creating the file, writing to it, and/or appending data. Finally, we add "This is my text file!" to confirm that it has been created.

The takeaway here is that we've successfully created a separate file called **logging.txt** outside of our application. You might be thinking, "So what? This is just basic file creation." And you would be right! However, what we do next is what defines logging. This is just step one, and it's a crucial step—it establishes an external storage system for our application's data.

Creating the JSON format

Today, JSON is considered the modern and widely used format for storing and transmitting data. Occasionally, you might encounter XML, which was the preferred format before JSON. While the format isn't critical to our understanding of logging, these two are the most commonly used.

Now, let's create a JSON example in our newly created **logging.txt** file:

Here, we add a test object to be logged in **logger.txt**. We include details like time, level, type, message, and environment. Once we create our test object, we convert it to JSON format and pass that information into our static logger file. This is what the log file looks like with all levels included:

If you've ever looked at a raw log file, this should look familiar! The format is well-structured, and the data is stored independently of the application. There's still more work to do, but things are coming together.

Making our function reusable

Now that we have a working function that creates the file and writes logging data in JSON format, we need to make it reusable. This means breaking the code into separate classes and methods so that we can call the same logic throughout our application using single-line function calls instead of copy-pasting.

I won’t go into full detail here, as that would make this blog post too long—perhaps it warrants its own post. But here's what it looks like:

We broke the file creation function and JSON object logic into a method called **AddToLogger()**. Then, we created separate methods within the **Logger** class to handle different logging categories. Finally, we call our **Logger** class using the **AddTracer()** method within our application. This simplifies logging to a single line of code with a message.

Completing it with a basic example

We integrated our basic console app with an API call and applied our newly created **Logger.AddTrace()** method. Now, each API call records relevant information in **logging.txt**. Just like that, we have basic logging in place! This approach allows us to manually add logging to any part of our application and reference it externally whenever needed. We can even take it a step further by integrating a visualization tool like **Grafana** to read our logs dynamically!

How to take it to the next level?

What we've implemented here is very basic—there are many enhancements we could incorporate, such as web availability logging, database connection monitoring, memory usage tracking, and console log integration. However, this simple approach provides a solid starting point. Keeping logging straightforward ensures we stay on top of every change and implementation.

Conclusion

As we've seen, creating a logging system that records data in an external file is quite simple. The key is to keep it focused and aligned with our goals.

P.S. I thought of coding out a more completed example with the web availability logging, background processes and hooking it up to Grafana for visualization, but this was getting too long and I'm losing focus. If you'd like to see it, let me know on LinkedIn!