Strategically logging messages to the console

Christopher T.

May 21st, 2019

Share This Post

Developers often log messages to the console for all kinds of reasons. The most common reason is that it provides helpful output to assist them in making sure that their app is working as they expect it to. This post will show several examples for strategic uses in console.log and console.error.

console.log

The most commonly used method to log messages to the console is console.log. Its simplicity just makes life a lot better to use them in your development flow (in my opinion). Console.log takes in a value (majority of the time you will be passing a string, however you can pass in other types like objects and numbers) as an argument and uses it to log it to the console.

const logSomething = (str) => {
  console.log(str)
}

logSomething('this is going to the console')

Result:

console log logSomething

A handy little trick to make these console messages more useful is to colorize their output in the console. By passing in a string prefixed with %c as the first argument, it will act as a placeholder for CSS values and automatically apply the styles you provide to the second argument. The styles argument is a string delimited by semi-colons ";" used to apply multiple styles to the message that will be displayed.

For example, if you were to log the message "Three seconds has passed" in orange whenever your function has timed out after 3 seconds from an http request, you can do something like:

// timeout in milliseconds
const message =
  '%cThis message will turn orange when the timeout gets to zero after 3 seconds'
setTimeout(() => {
  const styles = 'color:orange;'
  console.log(message, styles)
}, 3000)

Output:

log output orange

Now if you were wondering when this would be useful other than making console messages look pretty, you can use colors to customize and differentiate what part of your app is doing what, which line of code may be important to look out for, which line of code is giving an abnormal behavior---the list is endless.

Suppose you were working on an app and you needed to set up headers for future requests. You created a utility function that manages a headers object which you can use for constructing request objects.

You decide to name this utility function makeHeaders and it will return 3 methods:

Method Description
get Retrieves a key/value pair from the headers object
set Sets a key/value to the headers object
returnHeaders Returns the whole headers object

And the constructor in code:

const makeHeaders = function(options) {
  const headers = {}

  return {
    get: function(key) {
      return headers[key] || null
    },
    set: function(key, value) {
      if (key === undefined) return
      if (value === null) {
        console.warn(
          `WARNING! You tried to set "${key}" to headers but the value was null or undefined. The operation was skipped.`,
        )
        return
      }
      headers[key] = value
    },
    returnHeaders: function() {
      return headers
    },
  }
}

Now in your app you might have some code that uses it like this:

const headers = makeHeaders()
headers.set('Content-Type', 'application/json')

For our example use case, we want to make a post request to https://something.com/v1/api/user/ (this is not a real endpoint) to authenticate ourselves so we can have access to data from future API calls. A basic authentication service usually asks for a username and password as the parameters to the body in a post request:

const url = 'https://someapi.com'
const method = 'post'
const data = {
  username: 'myUsername',
  password: 'myPassword123',
}

And then you would apply it to your request object along with the other options for the API request. For this example we will be using axios:

const callApi = async (params) => {
  try {
    const response = await axios(params)
    if (response.data) {
      console.log(response.data) // result: { "token": "..." }
    }
    return response.data
  } catch (error) {
    // do something with error
  }
}

callApi({ url, method, data, headers })
  .then(({ token }) => console.log(token))
  .catch((error) => console.error(error))

The authentication service will validate the credentials and return an expiring token with a default expiration date of 1 hour after the creation of the token. All is good, however lets say they support an optional third parameter, expires_in. By passing in a number in milliseconds the default expiration date can be overrided:

const headers = makeHeaders()
headers.set('Content-Type', 'application/json')

const url = 'https://someapi.com'
const method = 'post'

const expiringMinutesToMs = (mins) => {
  return mins * 60000
}

const data = {
  username: 'myUsername',
  password: 'myPassword123',
  expires_in: expiringMinutesToMs(75), // 1 hour and 15 minutes
}

const callApi = async (params) => {
  try {
    const response = await axios(params)
    if (response.data) {
      console.log(response.data) // result: { "token": "..." }
    }
    return response.data
  } catch (error) {
    // do something with error
  }
}

callApi({ url, method, data, headers })
  .then(({ token }) => console.log(token))
  .catch((error) => console.error(error))

If we tried to pass in a number to minutesToMs and received an undefined value, we can log it with a plain ole console.log('some message here'), but if there are several messages getting sent to the console for different reasons it may look messy and hard to read if there is a lot of output to the console.

By incorporating colors, you're able to distinguish and draw quicker attention to important messages from the console. This improves the effectiveness of your development flow by making your development process quicker and funner!

const headers = makeHeaders()
headers.set('Content-Type', 'application/json')

const url = 'https://someapi.com'
const method = 'post'

const expiringMinutesToMs = (mins) => {
  if (mins === undefined) {
    const msg =
      'WARNING! You tried to pass in "mins" to the "expiringMinutesToMs" call but the value was undefined. It was defaulted to 60 minutes (1 hour) instead'
    console.log(`%c${msg}`, 'color:#FF4500')
  }
  return mins * 60000
}

let expiresInMinutes

const data = {
  username: 'myUsername',
  password: 'myPassword123',
  expires_in: expiringMinutesToMs(expiresInMinutes),
}

const callApi = async (params) => {
  try {
    const response = await axios(params)
    if (response.data) {
      console.log(response.data) // result: { "token": "..." }
    }
    return response.data
  } catch (error) {
    // do something with error
  }
}

callApi({ url, method, data, headers })
  .then(({ token }) => console.log(token))
  .catch((error) => console.error(error))

colored console output

console.error

Using console.error will print errors to the console. This will attempt to format anything you pass to it as an error.

strategilly logging console messages

What I like about console.error is that it includes a stack trace which allows you to navigate to previous calls that lead to the error. I wouldn't use this other than logging errors to the console otherwise it may become misleading to other developers who edit your code.

And that concludes the end of this post. I leave the rest to you to play around with!


Tags

javascript
browser
console
logging

Subscribe to the Newsletter
Get continuous updates
© jsmanifest 2021