Wisual Logger

A simple implementation of a hierarchical logger for React/JavaScript browser applications

Author profile picture
Pedro Tacla Yamada
20 Jun 20223 min read

Logging is an important strategy for building observable software systems. The practices and set-up for application logging often consist of a program printing some of its operations onto a file, console or network such that later on they can be inspected by a human or automated tools.

This is particularly useful when the software mis-behaves and requires debugging that would otherwise be hard to conduct on an opaque system.

Often on back-end systems, logs will end-up, temporarily, on some tool that can archive, search or visualise the output (Splunk, ELK).

Logs should be complimentary to metrics or analytics and will also be useful during the software development process in order to quickly provide some feedback of internal behaviour.

When developing back-end web services, logging is essential in order to allow the software to be operated by teams at scale. This is partially due to such services running on closed environments, which can not be easily probed otherwise.

I believe this is in contrast with GUI applications and, in particular, browser based GUIs. While analytics, metrics and some form of automated application monitoring are wide-spread amongst front-end application, logging from the front-end does not seem to be fully exploited.

@wisual/logger

@wisual/logger on GitHub

I wrote a very simple logger for a side-project which tries to bring hierarchy into browser logs.

Usage is as follows:

import { LoggerFactory } from "@wisual/logger";

const logger = LoggerFactory.getLogger("MyLogger");

logger.info("Hello world", { data: 10 });

This will print:

2022-06-20T10:48:47.470Z [app>MyLogger] (info) Hello world [Object >]

Where ‘object’ is a table in Chrome inspector or a JSON string on Node.js.

Hierarchy

Loggers can have contextual data attached to them:

const usersServiceLogger = logger.child("UsersService", {
  dbBackend: "psql",
  flushTimeout: 1000,
});
const usersCacheLogger = usersServiceLogger.child("UsersCache", {
  cacheBackend: "redis",
});

The context will be merged with data of each log message.

React integration

There are hooks provided for react integration:

import React from "react";
import { useLogger } from "@wisual/logger";

function MyComponent({ userId, data }) {
  // Creating our logger and properly setting its context
  const logger = useLogger("MyComponent", { userId });

  // Will log on render with proper context / names
  logger.info("rendering");

  // This will `useEffect` to log & log everytime the argument object's values change
  logger.onInfo("Data has changed", { data });

  // This is wrapping the children with this logger's context provider
  // calling `useLogger` on a child will use `MyComponent` logger as the parent.
  return logger.wrap(<div />);
}

Logger sinks

Sinks are implementors of the LoggerSink interface.

Tree sinks are provided:

  • PrettyConsoleSink - Pretty print messages on Node.js
  • PrettyBrowserSink - Pretty print messages on browser consoles
  • DelegatingSink - Send messages to multiple sinks

There’s no filtering currently implemented.

Configuring the sink

Use LoggerFactory.setSink.

Why named and hierarchical loggers?

Large applications will benefit from being able to filter out logs based on their place in the hierarchy. For React applications the UI hierarchy can be indicative of ownership or package boundaries.

What else?

That is it. This is a very simple package for now and should grow with the needs of my project.

All the best