DEV Community

Cover image for Level up your JavaScript browser logs with these console.log() tips
Ackshaey Singh
Ackshaey Singh

Posted on • Updated on

Level up your JavaScript browser logs with these console.log() tips

I consider myself a backend software engineer - and as any backend engineer would attest, a large part of our life is spent monitoring, troubleshooting, and debugging our applications. The fundamental rule of software development is that software will fail - what separates new developers from experienced ones is how they plan for those failures. Robust and effective logging is an important part of planning for failure, and eventual mitigation. As it is for backend development, logging can be useful for frontend software development, but it goes much further than just troubleshooting and debugging. Effective frontend logging can also make the development experience productive, fast, and fun.

While I’m a big proponent and diligent practitioner of Test-driven development, I love the flexibility, the richness of information, and code confidence browsers provide to frontend developers who make effective use of console.log(). I thought I’d share some frontend logging tips and tricks I’ve learned and incorporated in my workflow over time while building Firecode.io - in the hope that some of these will help you make your development workflow a bit more productive and fun!

I like to divide these tips into two broad categories - quick n’ dirty logging for when you’re actively building and debugging your application, and durable production logging - to know when your app’s working as expected and when it’s not.

Tips for quick n’ dirty development logging with console.log()

Don’t use console.log().

Yes, that’s right. I don’t use console.log(). Well, ok I write wrappers that use console.log() (more on that in the production logging section), but if you want to log something in your app to see what’s going on, use console.trace() instead. In addition to giving you everything console.log() does, it also outputs the entire stack trace so you know where exactly the message is emitted from.

02

Use ES6’s computed property names to identify your objects and avoid variable name confusion

This one is straightforward - use ES6’s computed property syntax and wrap the objects you wish to log in curly braces inside console.log() - i.e. use console.log({user}) vs console.log(user). You’ll find them neatly logged with the variable name set as the key, and the value as the object itself. This is especially useful when you’re in a hurry and want to log multiple objects in the same console.log() command.

03

Embrace tiered log levels - error, warn, info

console.log(param) by default logs at the INFO level - however, you also have 3 other logging levels at your disposal which you should make use of - console.debug(), console.warn() and console.error(). Besides formatting differences (notice the different colors?), the browser’s developer console also lets you easily filter out logs at different levels with a convenient dropdown to declutter your logs.

04

04

When logging lists of items, use console.table()

This one is self-explanatory and one of my favorite console functions - if you ever need to log a list of objects, give console.table() a try.

05

Quickly debug with debugger

Want to save a precious few seconds? Instead of finding your file in the developer console to add a breakpoint, drop a debugger in your code to halt execution when the line is executed. From this point on, you can debug and step over / into functions as you normally would.

06

08

Granular performance profiling with console.profile() and console.time()

Want to profile an exact user flow in your application to find hot spots? Trigger console.profile(profileName) at the start of the action, and console.profileEnd(profileName) at the end to inspect the CPU profile for the flow.

09

10

Related, you can measure exactly how long a flow takes with triggering console.time(id) at the start of the flow, and console.timeEnd(id) at the end.

11

Count labelled executions with console.count()

This one’s one of those console functions I haven’t found much use for personally, but it’s there if you need it. console.count(label) can help you know exactly how many times a piece of code gets executed - which could be useful for finding and eliminating race conditions and other scenarios.

12

Prettify your logging with CSS

This is by far my favorite console feature and one I make extensive use of in production logging (more on this in the production logging section). You can make use of format strings to format your log messages. The %c is the placeholder for CSS styles, and anything after is your message.

13

You can also style multiple elements by extending your format string to include %s for string parameters.

14

Since I’m a highly visual person, I like to spend some time making my info and debug logs look pretty and be useful at the same time. I make extensive use of this feature for production logging in Firecode.io - which is an excellent segue for the next section.

15

Production logging with console.log()

Getting frontend code production ready involves a number of steps - some being uglifying and compressing your code, generating cacheable asset digests, and removing console.log() s from your app. Why? Because you don’t want your users to have to open the developer console to interact with your app, which nullifies the usefulness of your logs and leaves them as pure security holes for the more inquisitive to take advantage of. At the same time, when you use your own app, you most likely want the most granular level of logging to understand how your app is functioning and find and squash bugs. If your app is being used by others, you’d also want to be notified when your application’s users encounter errors so you can track down and fix your code. Here’s a couple of things I do to satisfy these requirements as best one could on the frontend:

Don’t use console.log()

Instead, write a wrapper class that includes logic for conditionally logging based on the log level based set on a global variable by the backend. Warning - you’ll see TypeScript code snippets ahead - if you’re not familiar with TypeScript, think of it as a superset of JavaScript with types tacked on (gross over-simplification) - i.e. const str = “some string”; becomes const str: string = “some string” - types are added after a variable followed by a semicolon.

In the case of Firecode.io, I wrote my own frontend framework that utilizes RxJS, but includes familiar concepts such as components from other popular frameworks such as React and Vue - while adding additional concepts such as engines for processor-heavy code blocks, channels for WebSocket messages, and clients for HTTP requests. Visualizing all these pieces working together was critical, so I implemented custom formatting in a Logger wrapper class that formats and visually differentiates logs from each part of the application.

17

Instead of calling console.log("Cache SET to", {value}), I call Logger.debug(“Cache set to”, {value}, Framework.Cache) . The Logger class has a TypeScript enum that maps each framework component to the color to be used:

18

This allows me to visually focus on components of the app during development - for example, if I want to see what the WsRequestCache is doing I can tune out everything else besides the turquoise badged logs.

Protect your logs by having the backend set your log level

I have Firecode.io configured to turn on debug level logging by default for admin users with a JavaScript variable that is set by the backend. While adventurous users can still find and set these flags in the developer console to turn on granular logging, it is better than having all logs exposed to every user of your application by default, or having a post-processor remove all logging completely from your application in production.

Set in a Ruby on Rails view:
const logLevel: number = <%= @app.get_log_level_for_user %>

And in the Logger class:

class Logger {
   ...
   ...
   static info(...) {
     shouldLog(Level.INFO) && console.log(...);
        ...
   }
}
Enter fullscreen mode Exit fullscreen mode

Log and notify on actionable errors

Last but not least, you want to be notified when exceptional conditions are encountered by users without necessarily outputting logs to the developer console. You can do this by including a call to pipe your errors to a third party APM service such as AppSignal in your Logger‘s error function like so:

class Logger {
   ...
   ...
   static error(e) {
     if (shouldLog(Level.ERROR)) {
       console.error(e);
     }
     appsignal.sendError(e);
   }
}
Enter fullscreen mode Exit fullscreen mode

AppSignal includes integrations to pipe your errors to outbound notifications services such as Slack, PagerDuty, and OpsGenie - you can even hook up a project management tool such as JIRA or Trello to automatically create issues and bugs for your team.

Summary

I really hope these tips and anecdotes make your frontend development experience a little more productive and fun! I’ve obviously only touched the surface of logging ninjitsu in this post, so if you have any more tips to share I’d love to read them over on my Twitter.

plug

Two parting plugs - I’m rebuilding Firecode.io from the ground up with a brand new set of coding interview questions for JavaScript, Java, Python, and Scala. If you’re interested in coding interview prep that adapts to your learning style and is fun - sign up with your email here I’ll also be putting out more content about building a production scale web app like Firecode.io from scratch as a side project - follow me at @ackshaey or @firecodeio to learn more. Lastly, if you’re new to JavaScript and want to understand how object-oriented JavaScript and prototypal inheritance work under the hood, check out my favorite book on the subject - The Principles of Object-Oriented JavaScript, and if you’re interested in learning more about why you should use TypeScript instead, check out Effective TypeScript.

Oldest comments (18)

Collapse
 
hdayi profile image
Hacı DAYI

Very useful tips, thanks 👍

Collapse
 
echo2477 profile image
Eko Yusyono

Great...

Collapse
 
ravigithub profile image
Ravi Kasireddy

Great article thanks for share 👍

Collapse
 
ksaaskil profile image
Kimmo Sääskilahti

Very useful, never knew you can do so much to improve console logs!

Collapse
 
umaar profile image
Umar Hansa

These are some really useful tips! Nice work. For anyone interested in Chrome DevTools tips, I've posted loads here: umaar.com/dev-tips/

Collapse
 
devworkssimone profile image
DevWorksSimone

Uhhh interesting article... ti bad Javascript doesnt show up on firecode will get by as I learn a bit of Java !

Collapse
 
iyushb profile image
iyushb

Thank you for sharing the article.

Collapse
 
timhuang profile image
Timothy Huang

Excellent! This article is really useful.

Collapse
 
richliv profile image
Rich Livingstone

Very interesting and useful. Mostly new topics to me and I will be integrating most of these techniques into my applications.

Collapse
 
dennila2 profile image
Denni

You wrote: "Don’t use console.log() Instead, write a wrapper class..."

Console.log()/table()/etc... print in console place (filename and string number) where was he called.
joxi.ru/gmv6P5LsqgZKEm?d=1
If wrap console.log() in class in console always printing file and string number where console.log() is live in Log class!!. How can I know without additional params where log() was called?

Collapse
 
ackshaey profile image
Ackshaey Singh

Good question, which drives home the reason why this tip was listed under the production logging section and not development or debugging. The question you'd need to ask yourself is how useful is it to have that extra file:line link there? If you're doing things right and have compressed / minified your app's assets in production you're likely not going to find the location link useful anyway - instead your log messages should have all the info you need to identify where the log is emitted from and be actionable. Also keep in mind that console.trace() will still have the entire stack trace one level down even when invoked from a wrapper.

Collapse
 
tulo69 profile image
stefano valicchia

console.table() = love! Thank you, never used before!

Collapse
 
shtabnoy profile image
Denis Shtabnoy

Awesome article. I've never even heard about these console methods as .table, .trace, etc. Very informative. Thank you! 🤓

Collapse
 
aspiiire profile image
Aspiiire

Thank you for sharing :D

Collapse
 
tomiito profile image
Tomás de Priede

Great article! I will start immediately to use this stuff!

Collapse
 
jakubkeller profile image
Jakub Keller

Do not write a wrapper around console.log, unless you're using an opinionated framework like React or Angular, which then you can expose the functionality via a custom, injectable service. Otherwise, do use console.log or any language equivalent and stay away from heavy-handed logging frameworks.