How to Enhance Firebase Emulator Logs

Pretty up, remove noise, and enhance your Firebase emulator logs.

I’m a big fan for 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 with JSON.stringify({...}, null, "\t"). However, things get messy when you use Firebase’s recommended logger. The Logger is essentially the Google Cloud Logger, which allows you 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, thing 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 enhancements.

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 example: 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!
Firebase Formatted Logs
Formatted Firebase Emulator 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 begin with “function” or “hosting” are suppressed, and you can turn off pretty formatting.

ParameterDescriptionRequired
–fileThe source file of the logsyes
–quietSuppress system logging such as “function”, “hosting”, “storage”, and “pubsub”no
–pretty-offTurn off pretty formatting of JSON objects. Default: trueno
Firebase Emulator Logging Parameters

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";
import colorizer from "json-colorizer";

The tail-file is an awesome package that allows “tailing” of a files – whenever something changes, an event occurs. The JSON colorizer is a package that allows you to specify which elements of the JSON gets some color.