Few thoughts on traces

Recently I was asked to debug a piece of code that I hadn't been involved in before. As the issue at hand only seemed to appear in the production environment I turned to our traces to start my bug hunt.

Working my way through the traces (trying to map them to the code) I came to realize - again - how important good tracing is. Finding this bug took me several days of searching where I could have probably found it in under half a day should there have been proper traces.

Some takeaways I wrote down during my search where:

  1. Add sufficient traces
  2. Avoid meaningless traces
  3. Add actual values for variables
  4. Add concise traces
  5. Avoid secrets in the traces
  6. Use a log management and analysis platform
  7. Rethink traces in high frequency methods
  8. Some tips on logging exceptions

Add sufficient traces

One of the main benefits of using traces in source code is that they provide a detailed record of the program's execution. This is particularly useful when dealing with complex programs that involve multiple threads or processes, as it can help to identify potential sources of errors and inconsistencies.

Another advantage of using traces is that they can help to improve the overall performance of your program. By providing a detailed record of the program's execution, traces can help to identify bottlenecks and optimize the code accordingly.

In addition to these benefits, adding sufficient traces help providing a detailed record of a program's execution and thus help to understand how the code works (not at least when dealing with code that was written by someone else).

Avoid meaningless traces

Traces that do not provide any useful information or insights about the program's behavior should be avoided. Meaningless traces are a problem for several reasons. First, they clutter up trace logs, making it difficult to find and interpret the information that is actually relevant to the program's behavior.

Second, meaningless traces can also reduce the overall effectiveness of trace statements. When trace logs are filled with irrelevant information, it can be harder to identify and understand the messages that actually provide valuable insights into the program's behavior.

Show actual values for variables

It is important to show the actual values for variables because this provides valuable information about the state of the program at a given point in time. By showing the actual values of variables, trace logs can provide a detailed record of the program's behavior, and can help developers to understand what is really happening within the code.

Ryan Putra Zszf3u2ri9m Unsplash

Use concise and consistent traces

Some tips to make your traces more readable:

  • Use a consistent format: When writing trace statements, it is important to use a consistent format for the messages. This can help to make the trace logs more readable and easier to interpret. For example, you could use a standard format that includes the time, the function name, and the trace message.

  • Be specific and concise: Avoid using vague or general messages, and instead focus on providing clear, concise information about what the program is doing at a given point in time.

  • Use trace levels: Trace statements can be assigned different levels, such as "info" or "warning". When writing trace messages, it is important to use these levels consistently, and to choose the appropriate level for each message.

  • Avoid meaningless traces: As mentioned previously, meaningless traces can be a problem because they can clutter up the trace logs and make it harder to find and interpret the information that is actually relevant to the program's behavior.

Avoid secrets in the traces

While reading through the traces I was surprised to see passwords, connection string, usernames, etc. in the trace logs. There are a few key steps that developers should take to avoid writing secret information in traces:

  • Make sure that sensitive data is not included in trace messages. This includes things like passwords, API keys, or other confidential data.
  • Use sensitive data masks: If it is necessary to include sensitive data in trace messages, consider using sensitive data masks to obscure the data.

  • Use separate trace logs for sensitive data: If it is needed to trace the sensitive data for some reason consider using a separate trace log to store this data. The appropriate user rights for reading this log can then be put in place.

  • Use encryption for trace logs: To further protect sensitive data in trace logs, consider using encryption to secure the logs.

Need help help with your software development?

We can help you with your custom software or project rescue. Let's have a chat!

Use a log management and analysis platform

To be able to easily find the traces you are looking for (and not having to spend a day finding the correct credentials to the production server in order to access the log files) I recommend using a log management system. Such a system allows for the central storage of the trace logs and the appropriate user rights.

Seq allows us to collect, store, and analyze log data from a variety of sources, including applications, servers, and other systems. One of the key features of Seq is its ability to provide real-time visibility into the behavior of software applications. This is achieved by collecting log data in real-time, and providing tools for searching, filtering, and analyzing this data.

Seq is also highly scalable, allowing it to handle large volumes of log data without performance degradation. This makes it well-suited for use in high-traffic environments, such as web applications or distributed systems.

Rethink traces in high frequency methods

Traces in functions that are called an extreme number of times can be a problem for several reasons.

  • These traces can generate a large amount of log data, which can make it difficult to find and interpret the information that is actually relevant to the program's behavior.

  • Traces in frequently-called functions can also have a negative impact on the performance of the program. When a trace statement is executed, it adds overhead to the function, and this overhead can add up quickly if the function is called many times.

Carefully consider whether the trace statements in these functions are actually necessary, and remove any that are not providing valuable information. Consider using trace filters to limit the amount of log data generated. Only log that information relevant to the bug you are searching for.

Some tips on logging exceptions

  • Log the full exception stack trace, including the exception type and message

  • Use a separate log level for exceptions, such as ERROR or FATAL, to make it easier to find and filter the exception messages in your log files.

  • Include contextual information in your log messages, such as the request URL, user ID, and any relevant data from the request or application state. This can help you understand the context in which the exception occurred and what led up to it.

  • Monitor your logs for exceptions and other errors, and set up alerts or notifications to notify you when an error occurs.