Last updated at Tue, 25 Apr 2023 21:24:58 GMT

Developers are writing Javascript applications of increasing complexity designed to run in web browsers, on desktops, and on servers.  Javascript applications have reached a level of maturity that means they are running important business operations.  They must be more maintainable and supportable now that they have achieved this level of responsibility in the enterprise.  Javascript applications should be expected to provide the same information for support and maintenance as any other application in the enterprise stack.

A big part of providing that information is proper logging.  It is incredibly difficult to maintain software without the ability to understand its state before, during, and after problems occur.  When well done, logging gives us insight for resolving problems and optimizing functionality.

What Does a Log Entry Mean for Your Organization

One of the big challenges with logs is avoiding noise.  It is important to consider issues like severity when planning your logging strategy.  If you use a service like Logentries, this may not be as true because of the enhanced tools at your disposal.  We will discuss that point in more depth shortly.  Identifying several levels of severity, message types, or a combination of the two will help with the noise problem.

For example, asynchronous Javascript calls to a server can often time-out due to network load, latency, or too big a response.  Logging every time this happens may not be helpful.  Most likely your Javascript application can handle this eventuality and will try again, or notify the user to try again. The sometimes flaky nature of a Javascript asynchronous call to the server may raise false flags.  The same is not true if the timeout repeatedly occurs or occurs for several clients.  Being aware of repeated failures might help point to a failed service or downed server.

The difference between those two scenarios illustrates the need for message severity. The application should be aware enough of the severity of the issue to mark it correctly before surfacing it.  In our above example, a single asynchronous call might not need to be called out, or only called out as a warning.  Once multiple requests in a row have failed, then it can be handled more seriously as an error.  A simple message type error structure could be:

  • DEBUG
  • INFO
  • WARN
  • ERROR
  • FATAL

The same structure with severity levels:

  • LOW
  • MEDIUM
  • HIGH
  • CRITICAL

It is more important for the developers and support team to have a structure to use, than the specifics of what it is.  Make sure both groups spend time together working out a structure for your organization.

The Console

All Javascript developers are familiar with the console.  The console is a simple output window provided by all web browsers.  It is standard practice when building Javascript applications to use the console to output information for debugging or development.

In production, logging to the client can be a useful tool for immediately supporting customers.  If you have access to the user’s machine, then standardized output to the console can help identify the problem a user is having. Output to the console seen in the context of a user’s operations is extremely useful for support.

A well-placed error message in the console might make all the difference between a frustrated user and a happy well-supported user.  A support person will have a much better idea of how to resolve the problem if they have the context and application state at the time available.  If your organization is too big, then bringing this information into the UI in a way the user can share it with support can serve the same helpful end.

An easy mechanism for toggling the amount of information presented in the console is the URL.  Making your application check for a string, such as “debug=1” in the URL is a simple toggle mechanism.  For example, if the application’s regular URL is:

http://internal.examplecomp.com/myjsapp

Then use:

http://internal.examplecomp.com/myjsapp?debug=1

In a traditional Javascript application, using a URL switch allows debugging to be enabled on a page by page basis.  In a single page application, the URL switch can be used to set a global variable in the application.  For sanity’s sake, and best practice set the “global” using your application’s namespace.  It would not be a good idea to set any flag of this nature at the window level.

Unfortunately, the console is not a reliable solution for supporting Javascript applications in the long term:

  • The console on one client machine is isolated.   There is no way to take the information in the console and use it to identify larger issues.
  • Console logs can’t be aggregated with other users’ information to determine large-scale problems.
  • Information stored in the console is lost as soon as the page refreshes.
  • Asking users to relay information may not be the most efficient path.

That is why all Javascript applications must eventually look to sending log information out to a server.

Sending Logs Entries to the Server

A server can store the log information from all clients in one location.  All the log information stored together in one place serves lots of purposes:

  • Better user support – A client’s error and state sent to the server with a log entry can be used to help debug customer problems remotely.
  • Proactive user support – A user, or several users, can be contacted directly by support.  If the system sees several errors over a short period of time, it can notify support to contact the user(s).
  • Aggregation – With all log events in one place, information can be pooled together to create metrics.
  • Problem Research – Events in a clearly defined severity structure (as detailed above) can help to identify trends that could lead to problems.
  • Optimization – Log entries can be aggregated to recognize pain points to target for improvement.

Creating a server in Node.js, or some other server-side framework, is one potential route for logging to a server.  A log server can be as simple as having an endpoint on the server that writes out to a text file.  It can also be as complex as a fully relational database back end with a custom UI.  There might be open source logging packages with a solid framework in place that you can use.  With or without an open source framework, creating and maintaining a logging server requires a good amount of effort and time.

Alternatively, there are third party solutions, like Logentries, that can significantly simplify the logging part of your application.  A third party is going to have a solid database structure, and will be able to make entries available within milliseconds.  There will also be a powerful UI to help reveal log information and to aid in investigation and alerting. Third party solutions will also maintain client side and server side libraries to ease logging integration.  Cloud logging simplifies logging in the same way cloud computing simplifies server management.

Sending log events out to a server is crucial for any long-term application maintenance. Despite the importance of logging to a server, the console should not be abandoned. The best solution is to make both available. Consider sending events to the server by default, while still providing a way to output to the console on demand.

Conclusion

A robust logging strategy should be considered essential for the implementation of any Javascript application.  The days of Javascript applications built without attention to long-term maintenance and support are ending.  Make sure to use all tools at your employ: the console, the UI, and especially, the server.  A good logging strategy can make the difference between user acceptance and user resentment.


Cloud logging simplifies logging in the same way cloud computing simplifies server management.

Ready to start capturing your logs from your Javascript or Node.js application?  Sign up for a free Logentries account.