Console Utilities You (Didn't) Know
This article was taken, but adapted for the web, from the book JavaScript for Rails Developers that was recently released.
In this article I am going beyond console/log, like various other levels (warn and error), console.trace and console.group. What? đ€Ż
You most likely know console.log already. console is a global variable and all global variables are properties of the â window object. This means â window.console.log and â console.log are effectively the same thing. It is still a great debugging tool today, but it has quite a few more tricks up its sleeve than just printing some text.
Before exploring those, letâs add a little helper to make us, lazy developers, a little bit more productive. Head over to your app/javascript/application.js:
// app/javascript/application.js
// âŠ
window.log = (...params) => console.log(...params)
Now you can write: log("Hello from Rails Designer!") instead. 7 characters saved Ă 0,24 seconds per character Ă 15 logs = 25,2 seconds saved per debugging session! đ€
You are not limited to just text or single variables. Multiple arguments can be passed. And also string substitution is supported.
log("User:", { name: "Kendall", age: 30 })
log("Status:", "active", "since:", new Date())
log("%s will be the %s book to finally grasp JavaScript", "âJavaScript for Rails Devsâ", "best")
Console logging levels
There are many other logging levels available, just like with ActiveSupport::Logger.
console.info("Info message") // for important information, usually positive/success flows
console.warn("Warning message") // issues, deprecated features, things to watch out for
console.error("Error message") // actual errors, exceptions, critical issues
console.debug("Debug message") // info for debugging, often filtered out in production
Each level has different colors and icons in the developer tools. You can also filter or search by log level and configure which ones to show or hide. You can choose to hide certain levels, like debug. Each logging level supports the same attributes as seen for console.og().
Product-minded Rails notes
Monthly roundup on what we're building, open source work and recent articles.
Trace
console.trace â outputs the current stack trace to the console, which will help you track the execution path and function call hierarchy that led to a specific point in your code. Adding it the top of Stimulus controllerâs connect method outputs:
console.trace() editor_controller.js:54:12
connect editor_controller.js:54
connect stimulus.js:1498
connectContextForScope stimulus.js:1668
scopeConnected stimulus.js:2054
elementMatchedValue stimulus.js:1961
tokenMatched stimulus.js:999
// âŠ
This trace shows Stimulusâ controller lifecycle, where the connect() hook in the editor_controller.js on line 54 was called as part of Stimulusâs initialization sequence. It then first matches the DOM element with its controller through â tokenMatched, sets up the scope (scopeConnected) and finally executes the controllerâs connect() method.
This can help you find out which function led to a particular method being called, identify unexpected function calls or debug race conditions.
Assert
console.assert(false, "NUCLEAR LAUNCH DETECTED") is a debugging method that tests if a condition is, in this case, â false. Good for deliberately triggering an error during development or for adding impossible state checks in code (like below example). You can pass it any assertion:
function gettingGood(level) {
console.assert(level >= 0 && level <= 100,
"Getting good at JavaScript can only be between 0 - 100!"
)
// getting good logic hereâŠ
}
Table
The â console.table() method displays array or object data in a formatted table in your browserâs console. Useful at times to read, analyze or compare. Could be used to list performance monitoring data, API responses or configuration between development and production.
const users = [
{ id: 1, name: "Kendall", role: "admin", lastLogin: "1995-12-21" },
{ id: 2, name: "Cam", role: "user", lastLogin: "2004-07-01" }
]
console.table(users)
Dir
With â console.dir() you can display a list of properties for a specified object with customizable output formatting. It is not something you are likely to use much, but it is there if you need it.
console.dir(document.body)
console.dir(document.querySelector("[data-controller='editor']"))
console.dir(document.forms[0])
console.dir(this.element) // when inside a Stimulus controller
Group
Console groups create collapsible sections in your console, which helps organize related log messages into nested, indented blocks. Use it like so:
processOrder() {
console.group("Checkout Process")
console.log("1. Validating cartâŠ")
this.validateCart()
console.log("2. Processing paymentâŠ")
this.processPayment()
console.log("3. Creating orderâŠ")
this.createOrder()
console.groupEnd()
}
Time
console.time() wraps around logic and can measure execution time (in milliseconds) between start and end points. The labels for time and timeEnd need to be exactly the same.
console.time("Controller setup")
this.editor = new EditorView({
doc: this.contentValue,
parent: this.element,
extensions: this.#extensions
})
console.timeEnd("Controller setup")
Then there is also console.timeLog("Controller setup", "some custom value"). It allows you to see intermediate timing measurements without stopping the timer.
connect() {
console.time("Controller setup")
this.collaboration = new Collaboration(this.contributorValue.id)
console.timeLog("Controller setup", "Before EditorView instantiation")
this.editor = new EditorView({
doc: this.contentValue,
parent: this.element,
extensions: this.#extensions
})
console.timeLog("Controller setup", "After EditorView instantiation")
this.collaboration.attach({ to: this.editor })
console.timeEnd("Controller setup")
}
Count
â console.count() helps debug code by counting how many times a specific operation occurs (using a required string label as the identifier). Then use â console.countReset() to restart that count from zero using the same label. Useful for tracking API calls, retries, or any repeating operation you want to monitor.
class Stripe {
async fetch() {
console.count("stripe-api")
try {
const response = await fetch("https://api.stripe.com/v1/customers")
console.countReset("stripe-api")
return response.json()
} catch (error) {
if (this.retries < 3) {
await this.delay(1000)
return this.fetch()
}
throw error
}
}
}
And now for the final act: use them altogether! đ»
async function confirmPaymentIntent(clientSecret) {
console.group("Stripe Payment")
console.time("request")
console.log("Intent:", { clientSecret: `${clientSecret.slice(0, 10)}âŠ` })
// Simulate Stripe API call
await new Promise(resolve => setTimeout(resolve, 300))
console.timeLog("request", "After promise")
console.table([
{ status: "processing", time: new Date().toLocaleTimeString() }
])
console.timeEnd("request")
console.groupEnd()
return await stripe.confirmPayment({ clientSecret })
}
Then when done, or you feel like spring cleaning, â console.clear() method clears the console output in your browserâs developer tools. But you can also use the keyboard shortcut (typically CMD+K for most browsers on macOS).
console.clear()
So, tell me, which of the above utilities have you learned about today? đ And how many are you actually going to use? đŹ
This article is just a taste of whatâs in JavaScript for Rails Developers.
Want to read me more?
-
New: Rails Development Tool
New from Rails Designer: the Rails Development Tool. Improve your Rails development efficiency. -
Enhanced debugging for Stimulus
Introducing a new (experimental) feature in Stimulus FX. -
Stimulus basics: what is a Stimulus controller?
Learn about the basics of Stimulus controller: that they are really just plain JavaScript classes.
Over to youâŠ
What did you like about this article? Learned something knew? Found something is missing or even broken? 𫣠Let me (and others) know!
Comments are powered by Chirp Form
{{comment}}