title | tags | date |
---|---|---|
Using template tags for simple logging |
tags, javascript, logging |
08-01-2020 |
Often times I've wanted to utilize Tagged Template Literals to make writing logs super easy. This has often alluded me due to the structure of tags.
Tags expect a function which look like tag(strings?: Requireable<string>[], ...variables: any[]): any
and passes the parts of the string to it when used; where a string Why hello ${firstName} ${lastName}! How are you?
get converted into some representation of tag(["Why hello ", " ", "! How are you?"], firstName, lastName)
.
This is nice when you're doing some advanced parsing, but not neccessary for simple logging.
Usage of this helper is straight forward. useString
takes an optional callback and will return a new tag which converts the input to the original string.
Callback
Let's take the scenario that started this off for me. I wanted to simplify my logging so tagging strings rather than "calling" functions. This is useful in a couple of ways, firstly it reduces friction which increases the amount of logging and decreases console.log
pollution; secondly it allows us to manage the logger and enhance it as desired.
// Build the logger
const logger = (service, type = 'log') => useStringTag(string => {
console[type](`[${service}] ${new Date().toISOString()} ${string}`)
});
// Create the tags
const workerLog = logger('worker');
const workerWarn = logger('worker', 'warn');
const workerError = logger('worker', 'error');
// Run the logs
workerLog`📻 Beep boop`;
workerWarn`❗️ Pay attention to this!`;
workerError`🚨 Woah nelly!`;
Async
This also works with async.
// Build the async tag
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
const doAsync = useStringTag(async string => {
const ms = Math.round(Math.random() * 100 + 100)
await wait(ms);
return `${string} [took ${ms}]`;
});
// Run the work
doAsync`hello there`.then(console.log)
Fallback use with no callback
One might wonder why make the callback optional. I don't have a good use case, but I didn't want to throw an error.
// Create a new `str` tag
const str = useStringTag()
const friend = 'bob'
const result = str`hello ${friend}` // hello bob
// This is the same as: const result = `hello ${friend}`