How to Enhance Firebase Emulator Logs

Pretty up and remove noise

Geoffrey Bourne
Better Programming

--

Image by author

I’m a big fan of Firebase and extensively use the emulator for local development with our social media API.

There are a lot of great features of the emulator, such as simulating cloud functions, Firestore, and even pub/sub. If you output JSON logs to the console using console.log, the output is tolerable, especially if you format it with JSON.stringify({...}, null, "\t").

However, things get messy when you use Firebase's recommended logger. The Logger is the Google Cloud Logger, which allows you to structure your logs and JSON so they can be easily viewed and searched in the Google Cloud Dashboard.

While the Firebase logger output looks great in the Google Cloud Dashboard, things don’t look so nice in our terminal output:

Firebase Emulator Logs

Enhancing the Firebase Emulator Logs

What would we ideally want with the Firebase emulator logs?

  • Formatting of JSON objects when using the Logger.
  • Color output so we can highlight errors.
  • Remove extraneous data that we might not care about, such as “functions: You are running the Functions emulator in debug mode (port=9229). This means that functions will execute in sequence rather than in parallel.”

Since Firebase doesn’t “yet” provide these capabilities, let’s build our own.

We will save the output of the Firebase emulator to a file, monitor the file for changes, process the changes (format, etc.), and output the processed data to the console.

  1. Clone the Firebase-Emulator-Logging GitHub repository. This is a Node.js app. Run the typical npm install in the cloned directory.
  2. Start the Firebase emulator as you normally do and output to a file. For examples: firebase emulators:start > save.txt or npm run serve > save.txt.
  3. Back in the cloned directory, run the Node app with node index.js --file {file location}. For example: node index.js --file ./save.txt
  4. Enjoy the new logs!
Formatted Firebase Logs

Enhanced Logs Parameters

There are a few options when running the Node app. You’ve already seen the — file, but you can also set the output to quiet, meaning system output that begins with “function” or “hosting” are suppressed, and you can turn off pretty formatting.

Behind the Scenes

If you want to see all the code, head to GitHub, or here is the index.js file:

import readline from "readline";
import TailFile from "@logdna/tail-file";
import colorizer from "json-colorizer";

const QUIET_STRING = ["functions", "hosting", "storage", "pubsub"];

const quiet = process.argv.indexOf("--quiet");
const prettyOff = process.argv.indexOf("--pretty-off");
const fileIndex = process.argv.indexOf("--file");

if (fileIndex <= -1 || !process.argv[fileIndex + 1]) {
console.error(
"You seem to be missing the --file argument. Please provide a file to tail."
);
process.exit(1);
}

const options = {
pretty: prettyOff <= -1 ? true : false,
colors: { STRING_LITERAL: "white" },
};

async function startTail() {
const tail = new TailFile(process.argv[fileIndex + 1]).on(
"tail_error",
(err) => {
console.error("TailFile had an error!", err);
}
);

try {
await tail.start();
const linesplitter = readline.createInterface({
input: tail,
});

linesplitter.on("line", (line) => {
if (
quiet &&
QUIET_STRING.some((str) =>
new RegExp(`(?<=^...)(.*)${str}`, "gm").test(line)
)
)
return;

let newLine = line;
if (newLine.startsWith(">") && newLine.endsWith("}")) {
const overrideOptions = { ...options };

try {
const json = JSON.parse(newLine.slice(3));
switch (json?.severity) {
case "INFO":
overrideOptions.colors.STRING_KEY = "blue";
overrideOptions.colors.BRACE = "blue";
break;
case "WARNING":
overrideOptions.colors.STRING_KEY = "yellow";
overrideOptions.colors.BRACE = "yellow";
break;
case "ERROR":
overrideOptions.colors.STRING_KEY = "red";
overrideOptions.colors.BRACE = "red";
break;
default:
break;
}

newLine = colorizer(newLine.slice(3), overrideOptions);
} catch (err) {
// ignore
}
}

console.log(newLine);
});
} catch (err) {
console.error("Cannot start. Does the file exist?", err);
}
}

startTail().catch((err) => {
process.nextTick(() => {
throw err;
});
});

Two external NPM packages are used:

  • import TailFile from "@logdna/tail-file"; — The tail file is an awesome package that allows “tailing” of a file — whenever something changes, an event occurs.
  • import colorizer from "json-colorizer"; — The JSON colorizer is a package that allows you to specify which JSON elements get some color.

--

--

Co-Founder of ayrshare.com — an API-first platform to automate your social media posts with a few lines of code.