npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@travetto/log

v5.0.15

Published

Logging framework that integrates at the console.log level.

Downloads

451

Readme

Logging

Logging framework that integrates at the console.log level.

Install: @travetto/log

npm install @travetto/log

# or

yarn add @travetto/log

This module provides logging functionality, building upon ConsoleManager in the Runtime module. This is all ultimately built upon console operations. The logging infrastructure is built upon the Dependency Injection system, and so new loggers can be created that rely upon dependency injected services and sources.

Extending the Common Logger

By default, the system ships with the CommonLogger, and by default will leverage the LineLogFormatter and the ConsoleLogAppender. The configuration CommonLoggerConfig provides two configuration variables that allows for switching out LineLogFormatter for the JsonLogFormatter, depending on the value of CommonLoggerConfig.format. Additionally the ConsoleLogAppender can be swapped out for the FileLogAppender depending on the value of CommonLoggerConfig.output.

Code: Standard Logging Config

export class CommonLoggerConfig {
  @EnvVar('TRV_LOG_FORMAT')
  format: 'line' | 'json' = 'line';

  @EnvVar('TRV_LOG_OUTPUT')
  output: 'console' | 'file' | string = 'console';
}

In addition to these simple overrides, the CommonLogger can be extended by providing an implementation of either a LogFormatter or LogAppender, with the declared symbol of LogCommonⲐ.

Code: Sample Common Formatter

import { Injectable } from '@travetto/di';
import { LogFormatter, LogCommonⲐ, LogEvent } from '@travetto/log';

@Injectable(LogCommonⲐ)
export class SampleFormatter implements LogFormatter {

  format(e: LogEvent): string {
    return `${e.timestamp} [${e.level}]#[${e.scope ?? 'unknown'}] ${e.message ?? 'NO MESSAGE'} ${(e.args ?? []).join(' ')}`;
  }
}

As you can see, implementing LogFormatter/LogAppender with the appropriate symbol is all that is necessary to customize the general logging functionality.

Creating a Logger

The default pattern for logging is to create a Logger which simply consumes a logging event. The method is not asynchronous as ensuring the ordering of append calls will be the responsibility of the logger. The default logger uses console.log and that is synchronous by default.

Code: Logger Shape

export interface Logger {
  log(ev: LogEvent): unknown;
}

Code: Log Event

export interface LogEvent extends ConsoleEvent {
  /**
   * Log message
   */
  message?: string;
}

Code: Console Event

export type ConsoleEvent = {
  /** Time of event */
  timestamp: Date;
  /** The level of the console event */
  level: 'info' | 'warn' | 'debug' | 'error';
  /** The line number the console event was triggered from */
  line: number;
  /** The module name for the source file */
  module: string;
  /** The module path  for the source file*/
  modulePath: string;
  /** The computed scope for the console. statement.  */
  scope?: string;
  /** Arguments passed to the console call*/
  args: unknown[];
};

The LogEvent is an extension of the ConsoleEvent with the addition of two fields:

  • message - This is the primary argument passed to the console statement, if it happens to be a string, otherwise the field is left empty
  • context - This is the final argument passed to the console statement, if it happens to be a simple object. This is useful for external loggers that allow for searching/querying by complex data

Code: Custom Logger

import { Injectable } from '@travetto/di';
import { LogEvent, Logger } from '@travetto/log';

@Injectable()
export class CustomLogger implements Logger {
  log(ev: LogEvent): void {
    const headers = new Headers();
    headers.set('Content-Type', 'application/json');
    const body = JSON.stringify(ev);
    fetch('http://localhost:8080/log', { method: 'POST', headers, body, });
  }
}

Creating a Decorator

In addition to being able to control the entire logging experience, there are also scenarios in which the caller may want to only add information to the log event, without affecting control of the formatting or appending. The Logger is an interface that provides a contract that allows transforming the LogEvent data. A common scenario for this would be to add additional metadata data (e.g. server name, ip, code revision, CPU usage, memory usage, etc) into the log messages.

Code: Log Decorator Shape

export interface LogDecorator {
  decorate(ev: LogEvent): LogEvent;
}

Code: Custom Logger

import os from 'node:os';

import { Injectable } from '@travetto/di';
import { LogDecorator, LogEvent } from '@travetto/log';

@Injectable()
export class CustomDecorator implements LogDecorator {
  decorate(ev: LogEvent): LogEvent {

    // Add memory usage, and hostname
    Object.assign(ev.context ??= {}, {
      memory: process.memoryUsage,
      hostname: os.hostname()
    });

    return ev;
  }
}

Logging to External Systems

By default the logging functionality logs messages directly to the console, relying on the util.inspect method, as is the standard behavior. When building distributed systems, with multiple separate logs, it is useful to rely on structured logging for common consumption. The framework supports logging as JSON, which is easily consumable by services like elasticsearch or AWS Cloudwatch if running as a lambda or in a docker container.

The main caveat that comes with this, is that not all objects can be converted to JSON (specifically circular dependencies, and unsupported types). That end, the framework recommends logging with the following format, message: string   context: Record<string, Primitive>. Here context can be recursive, but the general idea is to only pass in known data structures that will not break the JSON production.

Environment Configuration

Code: Standard Logging Config

export class CommonLoggerConfig {
  @EnvVar('TRV_LOG_FORMAT')
  format: 'line' | 'json' = 'line';

  @EnvVar('TRV_LOG_OUTPUT')
  output: 'console' | 'file' | string = 'console';
}

The following environment variables have control over the default logging config:

  • TRV_LOG_FORMAT - This determines whether or not the output is standard text lines, or is it output as a single line of JSON
  • TRV_LOG_OUTPUT - This determines whether or not the logging goes to the console or if it is written to a file
  • TRV_LOG_PLAIN - Allows for an override of whether or not to log colored output, this defaults to values provided by the Terminal in response to FORCE_COLOR and NO_COLOR
  • TRV_LOG_TIME - This represents what level of time logging is desired, the default is ms which is millisecond output. A value of s allows for second level logging, and false will disable the output. When ingesting the content into another logging, its generally desirable to suppress the initial time output as most other loggers will append as needed.