Published on

Debugging in Multiple Languages

Authors
  • avatar
    Name
    Nali Thephavong Haehn
    Twitter

Fact of life: you will have bugs in your code. It's nothing to be ashamed of, but (1) making sure you have a robust testing suite in place to catch those pesky bugs before production and (2) being aware of the tools available to track down errors and correct mistakes will help you sleep a little easier at night.

Let's Talk Code

JavaScript:

If you've ever coded in JavaScript, you know the almighty console.log(), but there's so much more to the Console API than just writing some text to the console. Here are three techniques I use most often:

Categorize log messages to make it more readable:

console.debug("debug message");
console.info("info message");
console.warn("warning message");
console.error("error message");

Log execution time of blocks of code:

console.time(); // start timer
Math.random(); // execute some function
console.timeLog(); // log current timer time
Math.random(); // execute another function
console.timeEnd(); // end timer

Group messages:

console.group("Group Name");
console.log("This will be indented");
console.log("Another indented line in the group");
console.groupEnd();

Alternatively, use console.groupCollapsed() instead of console.group() to create...well...a collapsed group. Users would need to expand the group to see all the child messages.

You can find more console methods on MDN.

Python:

First, the basics, an example of print with string literals and string interpolations:

print("this is a basic message")

var = "variable"
print(f"this is an f-string message with a {var}")

Sure, using print is easy, but there's a better way: using the logging module.

Messages can be categorized like with JavaScript's console:

import logging

logging.debug("debug message");
logging.info("info message");
logging.warning("warning message");
logging.error("error message");
logging.critical("danger zone");

Specify which messages to log by setting the default logging level:

import logging

logging.basicConfig(level=logging.WARNING)

logging.debug("this will NOT be logged")
logging.info("this will NOT be logged")
logging.warning("this will be logged")
logging.error("this will be logged")
logging.critical("this will be logged")

Specify a default format for messages:

import logging

logging.basicConfig(format="%(asctime)s %(message)s", level=logging.WARNING)

logging.warning("this will be logged")

# Output will look like this:
# 2023-09-04 21:16:40,374 this will be logged

Log your messages to a file:

import logging

logging.basicConfig(filename="debug.log", encoding="utf-8", level=logging.DEBUG)

logging.debug("This will be logged to file")
logging.warning("This will also be logged to file")
NOTE
logging.basicConfig() can only be called once to set the defaults, so any subsequent calls to it won't be applied.

More info here: Python Docs

Ruby:

I'm less familiar with Ruby, but with the limited knowledge that I do have, it seems to work very similar to Python.

The easy way: using puts, print, and p.

puts "log this" # log a message with a new line at the end
print "log this" # log a message without a new line at the end

# using double quotes allows for string interpolation
var = "variable"
puts "this is a message with a #{var}"

# using single quotes only supports string literals
var = "variable"
puts 'this is a message with a #{var}' # var will not be replaced with variable

# use p to return objects
p [1,2,3] # will return [1,2,3]

Ruby has a separate class for logging called Logger. With Logger, messages can be categorized:

require "logger"

logger = Logger.new(STDOUT)

logger.debug("debug message")
logger.info("info message")
logger.warn("warning message")
logger.error("error message")
logger.fatal("danger zone")

You can also specify a default logging level:

require "logger"

logger = Logger.new(STDOUT) # this outputs to the standard output stream
logger.level = Logger::WARN

logger.debug("this will NOT be logged")
logger.info("this will NOT be logged")
logger.warn("this will be logged")
logger.error("this will be logged")
logger.fatal("this will be logged")

Similar to Python, additional options include outputting to a file and applying a default format to your messages. You can find more information here: Ruby.