Logs are a fantastic resource for additional "behind the scenes" information on your application's behavior. However, with so many different logs emitted by an application across various logging levels, which ones should you be storing?
The answer to what logs you should store will vary depending on your application and intentions; however, this article will provide examples of when logs can be valuable and worth storing and which logs are better suited to local debugging.
The logs you store should be useful in helping you gain more significant insights into the behavior of your application and should provide valuable information. When deciding if a log is worth storing, consider:
To ensure your logs meet the above criteria, we recommend:
Logs that describe critical flows in your application are very useful when debugging an issue in your application.
You can format your logs in a logical and descriptive manner that allows you to quickly understand where in your application a log is being generated from:
FlowName - UUID action Function/MethodName
FlowName - UUID action Function/MethodName
Let's imagine we want to log user actions within an application's order flow:
INFO -- Main: Order - User 1293 started CreateOrder INFO -- Main: Order - User 1293 started OrderPayment INFO -- Main: Order - User 1293 finished OrderPayment INFO -- Main: Order - User 1293 started SendOrder INFO -- Main: Order - User 1293 finished SendOrder
INFO -- Main: Order - User 1293 started CreateOrder INFO -- Main: Order - User 1293 started OrderPayment INFO -- Main: Order - User 1293 finished OrderPayment INFO -- Main: Order - User 1293 started SendOrder INFO -- Main: Order - User 1293 finished SendOrder
In the above example, the logs tell us that a user has started and ended a process flow within our application.CreateOrder
, OrderPayment
, and SendOrder
refer to modules in our code base, as they are defined in our code base, which allows us to locate the code generating the logs easily.
When writing logs, providing additional context, such as external requests, response data, and outcomes of critical steps, can often be invaluable.
For example:
INFO -- Main: Order - User 1293 started SendOrder INFO -- Main: Order - User 1293 sent order 5302 to PizzaAPI INFO -- Main: Order - User 1293 received response 201 status Created from PizzaAPI INFO -- Main: Order - User 1293 finished SendOrder
INFO -- Main: Order - User 1293 started SendOrder INFO -- Main: Order - User 1293 sent order 5302 to PizzaAPI INFO -- Main: Order - User 1293 received response 201 status Created from PizzaAPI INFO -- Main: Order - User 1293 finished SendOrder
The above log lines give us more significant insights into the user's flow and allow us to filter on the user's UUID.
As we often look at our logs to help us understand why something went wrong, it's invaluable to store error logs.
When writing error logs, include as much information as possible to help deduce the reason for an error, such as response codes and messages.
ERROR -- Main: Order - User 1293 received response 400 status Bad request from PizzaAPI with message "Invalid data type for 'pizza_topping_ids'. Expected an array of integers."
ERROR -- Main: Order - User 1293 received response 400 status Bad request from PizzaAPI with message "Invalid data type for 'pizza_topping_ids'. Expected an array of integers."
While your application may generate dozens of logs a minute, not all are worth storing. Some may only be useful in specific use cases, such as locally debugging your application.
Storing logs that are not needed can generate a lot of noise, making log filters and queries less effective not to mention over time, these logs can take up a lot of space which may cost money or prevent you from storing more useful logs.
To avoid storing logs that provide no to little added value to your application monitoring, we advise:
Regarding logging levels, debug
and trace
level logs are for development and debugging purposes, often contain verbose information, and should not be stored in your log management. You can read more about log levels in our "What is logging?" article.
Logs that aren't descriptive should ideally be omitted. For example, imagine you have an application that writes an info
log every time a user logs in.
INFO -- Main: User 2313 logged in INFO -- Main: User 4476 logged in INFO -- Main: User 5968 logged in INFO -- Main: User 4092 logged in
INFO -- Main: User 2313 logged in INFO -- Main: User 4476 logged in INFO -- Main: User 5968 logged in INFO -- Main: User 4092 logged in
While this information may be helpful when debugging your application locally, on a production application with thousands of unique sign-ins per day, it's noise that can potentially distract from more important and valuable logs - and not to mention, if you are using a log management solution you'll be paying to store these logs too!
It can be advised to remove such logs or write them as debug
logs so that they can assist you in debugging while not being stored by your log manager.
Suppose you log information, like the above example, to see if a user is logged in. In that case, you can use alternative methods like AppSignal's Custom Metrics to track an active user count rather than rely on logs.
It can be tempting to log everything because if you log everything, you can re-create every scenario and understand everything happening in your application.
However, logging too much information can be as detrimental as logging too little; flooding your logs with large amounts of verbose data can make it more difficult to query and filter your logs for useful information and slow down your ability to debug issues:
INFO -- Main: User 87235 logged in: {"user_id"=>87235, "last_log_in"=>"2023-07-27 09:15:23", "language"=>"English", "country"=>"Canada", "notifications_enabled"=>false, "organization_id"=>567, "profile_description"=>"Travel enthusiast.", "avatar_image"=>"avatar_default.jpg", "updated_at"=>"2023-07-27 10:30:05", "created_at"=>"2023-07-27 08:40:12"} INFO -- Main: User 46579 logged in: {"user_id"=>46579, "last_log_in"=>"2023-07-27 17:55:10", "language"=>"Spanish", "country"=>"Spain", "notifications_enabled"=>true, "organization_id"=>987, "profile_description"=>"Tech lover and gamer.", "avatar_image"=>"avatar_46579.jpg", "updated_at"=>"2023-07-27 18:10:20", "created_at"=>"2023-07-27 16:20:30"} INFO -- Main: User 23107 logged in: {"user_id"=>23107, "last_log_in"=>"2023-07-27 06:25:48", "language"=>"German", "country"=>"Germany", "notifications_enabled"=>true, "organization_id"=>321, "profile_description"=>"Fitness freak and nature enthusiast.", "avatar_image"=>"avatar_23107.jpg", "updated_at"=>"2023-07-27 07:40:15", "created_at"=>"2023-07-27 05:10:25"} INFO -- Main: User 90876 logged in: {"user_id"=>90876, "last_log_in"=>"2023-07-27 12:45:33", "language"=>"Japanese", "country"=>"Japan", "notifications_enabled"=>false, "organization_id"=>123, "profile_description"=>"Foodie and photography enthusiast.", "avatar_image"=>"avatar_90876.jpg", "updated_at"=>"2023-07-27 13:20:55", "created_at"=>"2023-07-27 11:30:40"} INFO -- Main: User 78903 logged in: {"user_id"=>78903, "last_log_in"=>"2023-07-27 23:05:15", "language"=>"English", "country"=>"Belgium", "notifications_enabled"=>true, "organization_id"=>456, "profile_description"=>"Bookworm and writer.", "avatar_image"=>"avatar_78903.jpg", "updated_at"=>"2023-07-27 23:45:30", "created_at"=>"2023-07-27 21:50:10"}
INFO -- Main: User 87235 logged in: {"user_id"=>87235, "last_log_in"=>"2023-07-27 09:15:23", "language"=>"English", "country"=>"Canada", "notifications_enabled"=>false, "organization_id"=>567, "profile_description"=>"Travel enthusiast.", "avatar_image"=>"avatar_default.jpg", "updated_at"=>"2023-07-27 10:30:05", "created_at"=>"2023-07-27 08:40:12"} INFO -- Main: User 46579 logged in: {"user_id"=>46579, "last_log_in"=>"2023-07-27 17:55:10", "language"=>"Spanish", "country"=>"Spain", "notifications_enabled"=>true, "organization_id"=>987, "profile_description"=>"Tech lover and gamer.", "avatar_image"=>"avatar_46579.jpg", "updated_at"=>"2023-07-27 18:10:20", "created_at"=>"2023-07-27 16:20:30"} INFO -- Main: User 23107 logged in: {"user_id"=>23107, "last_log_in"=>"2023-07-27 06:25:48", "language"=>"German", "country"=>"Germany", "notifications_enabled"=>true, "organization_id"=>321, "profile_description"=>"Fitness freak and nature enthusiast.", "avatar_image"=>"avatar_23107.jpg", "updated_at"=>"2023-07-27 07:40:15", "created_at"=>"2023-07-27 05:10:25"} INFO -- Main: User 90876 logged in: {"user_id"=>90876, "last_log_in"=>"2023-07-27 12:45:33", "language"=>"Japanese", "country"=>"Japan", "notifications_enabled"=>false, "organization_id"=>123, "profile_description"=>"Foodie and photography enthusiast.", "avatar_image"=>"avatar_90876.jpg", "updated_at"=>"2023-07-27 13:20:55", "created_at"=>"2023-07-27 11:30:40"} INFO -- Main: User 78903 logged in: {"user_id"=>78903, "last_log_in"=>"2023-07-27 23:05:15", "language"=>"English", "country"=>"Belgium", "notifications_enabled"=>true, "organization_id"=>456, "profile_description"=>"Bookworm and writer.", "avatar_image"=>"avatar_78903.jpg", "updated_at"=>"2023-07-27 23:45:30", "created_at"=>"2023-07-27 21:50:10"}
For this reason, just like we advise against storing debug
and trace
logs, we also advise against including hashes in your log message and advocate instead of using custom log attributes instead. These attributes can be queried and filtered; you can log valuable attributes instead of all of an object's attributes.
Regardless of what logs you ultimately decide to store in your log manager, you must refrain from logging Personal Identifiable Information, or PII for short. Your log data must not contain any personal data, such as names, email addresses, etc. You must filter and remove this data before writing a log.
If you need to identify specific users in your logs, you must use alternative forms of identification, such as a user ID or pseudonym.
AppSignal's Custom log attributes allow you to add additional, filterable data to your log lines. These attributes are helpful when logging API interactions, allowing you to contextualize better how the API and your application behave.
Let's imagine we have an app that allows users to order pizza, an inefficient way to capture the API request information would be to include it in the log message:
INFO -- Main: Order - User 9421 sent order to PizzaAPI params: {"timestamp": "2022-06-02T04:17:25.783Z", "group": "organizations", "severity": "info", "message": "Order sent successfully to pizzeria", "shop_id": "23910", "order_id": "5302", "pizza_base_id": "12", "pizza_topping_ids": ["9", "12", "17", "24"], "user_id": "9421", "delivery_method": "collect"}
INFO -- Main: Order - User 9421 sent order to PizzaAPI params: {"timestamp": "2022-06-02T04:17:25.783Z", "group": "organizations", "severity": "info", "message": "Order sent successfully to pizzeria", "shop_id": "23910", "order_id": "5302", "pizza_base_id": "12", "pizza_topping_ids": ["9", "12", "17", "24"], "user_id": "9421", "delivery_method": "collect"}
However, as previously discussed, sending large hashes of data is not optimal and can be counterproductive. Instead, with AppSignal, we can create a log with custom attributes of the information we need, like in this JSON example:
{ "timestamp": "2022-06-02T04:17:25.783Z", "group": "organizations", "severity": "info", "message": "Order sent successfully to pizzeria", "attributes": { "shop_id": "23910", "order_id": "5302", "user_id": "9421" } }
{ "timestamp": "2022-06-02T04:17:25.783Z", "group": "organizations", "severity": "info", "message": "Order sent successfully to pizzeria", "attributes": { "shop_id": "23910", "order_id": "5302", "user_id": "9421" } }
In AppSignal, we'd be able to filter our logs based on the log message or any of the custom attributes we added to the log, like shop_id
, helping us quickly access the contextual information needed should an incident occur.
If you're interested in AppSignal log management, you may be interested in the following:
You can start managing your application's logs in as little as five minutes with AppSignal if you are not yet a customer sign up for a free trial. If you get stuck or have a question about AppSignal, our dev-to-dev support is always on hand to help!
You can learn more about AppSignal Logging on our Log management page.
AppSignal offers a 30-day free trial, no credit card is required. All features are available in all plans. Start monitoring your application in just a few clicks!