typescript-logging

Logging library specifically for TypeScript (23,478 weekly downloads, 98 GitHub stars). Provides type-safe logging API leveraging TypeScript characteristics. For niche applications but equipped with specialized features for pure TypeScript environments.

logging libraryTypeScriptcategory styleLog4TSAngularReactSPAzero configuration

Logging Library

typescript-logging

Overview

typescript-logging is a logging library designed specifically for TypeScript. Specialized for Single Page Applications (SPA) built with frameworks like Angular, React, and Ember, it provides an easy-to-use logging mechanism that can be used with minimal configuration. It features dynamic construction capabilities and the ability to easily enable or change logging after application deployment.

Details

typescript-logging was designed to solve challenges in modern TypeScript application development. It leverages TypeScript's type system that traditional JavaScript logging libraries cannot fully support, achieving both type safety during development and runtime flexibility. The library provides two main styles: Category-style and Log4TS style, allowing selection based on project requirements.

Technical Features

  • Full TypeScript Support: Logging library written in and for TypeScript
  • Two Styles: Optional architecture of Category-style and Log4ts-style
  • SPA Optimization: Design specialized for frameworks like Angular, React, Ember
  • Dynamic Configuration: Runtime configuration changes and logger control
  • Minimal Setup: Ready to use immediately without complex configuration
  • Post-deployment Control: Easy logging level adjustments in production environments
  • TypeScript Type Safety: Complete type checking and IntelliSense support

Architecture Styles

  • Category-style: Configure provider and expose getLogger function for other modules to use
  • Log4ts-style: Similar configuration approach with provider and getLogger function

Main Components

  • LoggerFactory: Creation and management of logger instances
  • Category: Categorization and filtering of log messages
  • LogLevel: Definition and control of log levels
  • LogFormat: Log message format configuration

Pros and Cons

Pros

  • TypeScript Specialized: Complete type safety in TypeScript projects
  • SPA Optimized: Excellent integration with modern frontend frameworks
  • Simple Setup: Ready to use immediately without complex configuration files
  • Dynamic Control: Runtime logging configuration changes and dynamic enabling
  • Two Styles: Flexible architecture selection based on project needs
  • Post-deployment Adjustment: Easy logging level adjustments in production environments
  • Lightweight: Minimal dependencies and small bundle size
  • IntelliSense: Excellent development experience through TypeScript benefits

Cons

  • TypeScript Only: Cannot be fully utilized in JavaScript projects
  • Community Size: Smaller user base compared to Winston, Pino
  • Documentation: Limited Japanese documentation and usage examples
  • Ecosystem: Fewer surrounding tools and plugin options
  • Learning Curve: Understanding of unique architecture styles required
  • Version Incompatibility: Incompatibility between version 1 and version 2
  • Maintenance: Unclear frequency of active development and updates

Reference Links

Usage Examples

Basic Setup (Category-style)

// Package installation
// npm install --save typescript-logging
// npm install --save typescript-logging-category-style

import { Category, CategoryLogger, CategoryServiceFactory, CategoryConfiguration, LogLevel } from 'typescript-logging';

// Logger category configuration
const category = new Category('MyApp');
const logger: CategoryLogger = CategoryServiceFactory.getLogger(category);

// Basic log output
logger.info('Application started');
logger.warn('Warning: Configuration file not found');
logger.error('Error: Database connection failed');

// Structured logging
logger.info('User login', { userId: 12345, sessionId: 'abc123' });
logger.debug('API response time', { endpoint: '/api/users', responseTime: '150ms' });

Advanced Category Configuration

import { 
  Category, 
  CategoryLogger, 
  CategoryServiceFactory, 
  CategoryConfiguration, 
  LogLevel,
  DateFormat,
  LogFormat
} from 'typescript-logging';

// Multiple category definition
const rootCategory = new Category('MyApp');
const authCategory = new Category('Auth', rootCategory);
const apiCategory = new Category('API', rootCategory);
const databaseCategory = new Category('Database', rootCategory);

// Custom log configuration
const config = new CategoryConfiguration(
  LogLevel.Debug,
  LogLevel.Info,
  new LogFormat(new DateFormat()),
  true // Include StackTrace
);

// Apply configuration to categories
CategoryServiceFactory.setDefaultConfiguration(config);

// Get logger for each category
const authLogger: CategoryLogger = CategoryServiceFactory.getLogger(authCategory);
const apiLogger: CategoryLogger = CategoryServiceFactory.getLogger(apiCategory);
const dbLogger: CategoryLogger = CategoryServiceFactory.getLogger(databaseCategory);

// Log output by category
class AuthService {
  constructor() {
    authLogger.info('AuthService initialization complete');
  }
  
  login(username: string): boolean {
    authLogger.debug(`Login attempt: ${username}`);
    
    try {
      // Authentication logic
      const isValid = this.validateCredentials(username);
      
      if (isValid) {
        authLogger.info(`Login successful: ${username}`);
        return true;
      } else {
        authLogger.warn(`Login failed: ${username}`);
        return false;
      }
    } catch (error) {
      authLogger.error('Login processing error', error);
      return false;
    }
  }
  
  private validateCredentials(username: string): boolean {
    authLogger.trace(`Validating credentials: ${username}`);
    // Authentication logic
    return username.length > 0;
  }
}

class APIService {
  async fetchUserData(userId: number) {
    apiLogger.info(`User data retrieval started: ${userId}`);
    
    try {
      const startTime = Date.now();
      // API call
      const userData = await fetch(`/api/users/${userId}`);
      const endTime = Date.now();
      
      apiLogger.info('User data retrieval successful', {
        userId,
        responseTime: `${endTime - startTime}ms`,
        status: userData.status
      });
      
      return userData;
    } catch (error) {
      apiLogger.error(`User data retrieval failed: ${userId}`, error);
      throw error;
    }
  }
}

Log4ts-style Configuration

// Package installation
// npm install --save typescript-logging
// npm install --save typescript-logging-log4ts-style

import { 
  LFService, 
  LoggerFactory, 
  LoggerFactoryOptions, 
  LogLevel, 
  LogGroupRule, 
  LogFormat, 
  DateFormatEnum 
} from 'typescript-logging-log4ts-style';

// Log4ts style configuration
const options = new LoggerFactoryOptions()
  .addLogGroupRule(new LogGroupRule(new RegExp('.+'), LogLevel.Debug))
  .setDateFormat(DateFormatEnum.YearMonthDayTime)
  .setFormat(new LogFormat());

// Create logger factory
const factory = LFService.createNamedLoggerFactory('MyAppFactory', options);

// Get loggers for each module
const appLogger = factory.getLogger('MyApp');
const authLogger = factory.getLogger('MyApp.Auth');
const apiLogger = factory.getLogger('MyApp.API');

// Log4j style log output
class UserController {
  private logger = factory.getLogger('MyApp.Controller.User');
  
  constructor() {
    this.logger.info('UserController initialization');
  }
  
  async createUser(userData: any) {
    this.logger.debug('User creation started', userData);
    
    try {
      // Validation
      if (!this.validateUserData(userData)) {
        this.logger.warn('Invalid user data', userData);
        throw new Error('Invalid user data');
      }
      
      // User creation
      const user = await this.saveUser(userData);
      
      this.logger.info('User creation successful', { userId: user.id });
      return user;
    } catch (error) {
      this.logger.error('User creation error', error);
      throw error;
    }
  }
  
  private validateUserData(userData: any): boolean {
    this.logger.trace('Validating user data');
    return userData && userData.email && userData.name;
  }
  
  private async saveUser(userData: any) {
    this.logger.debug('Saving to database');
    // Database operation
    return { id: Date.now(), ...userData };
  }
}

Dynamic Log Level Control

import { CategoryServiceFactory, LogLevel, Category } from 'typescript-logging';

// Runtime log level changes
class LoggerManager {
  private categories: Map<string, Category> = new Map();
  
  registerCategory(name: string, parent?: Category): Category {
    const category = new Category(name, parent);
    this.categories.set(name, category);
    return category;
  }
  
  // Dynamically change log level
  setLogLevel(categoryName: string, level: LogLevel): void {
    const category = this.categories.get(categoryName);
    if (category) {
      CategoryServiceFactory.setConfigurationCategory(category, level);
      console.log(`Changed log level of category '${categoryName}' to ${LogLevel[level]}`);
    }
  }
  
  // Enable debug mode
  enableDebugMode(): void {
    this.categories.forEach((category, name) => {
      CategoryServiceFactory.setConfigurationCategory(category, LogLevel.Debug);
    });
    console.log('Enabled debug mode for all categories');
  }
  
  // Set production mode
  setProductionMode(): void {
    this.categories.forEach((category, name) => {
      if (name.includes('Debug') || name.includes('Trace')) {
        CategoryServiceFactory.setConfigurationCategory(category, LogLevel.Warn);
      } else {
        CategoryServiceFactory.setConfigurationCategory(category, LogLevel.Info);
      }
    });
    console.log('Set production mode log levels');
  }
  
  // Automatic configuration based on environment variables
  configureFromEnvironment(): void {
    const logLevel = process.env.LOG_LEVEL || 'info';
    const level = this.parseLogLevel(logLevel);
    
    this.categories.forEach((category) => {
      CategoryServiceFactory.setConfigurationCategory(category, level);
    });
    
    console.log(`Set log level to ${logLevel} based on environment variable`);
  }
  
  private parseLogLevel(levelString: string): LogLevel {
    switch (levelString.toLowerCase()) {
      case 'trace': return LogLevel.Trace;
      case 'debug': return LogLevel.Debug;
      case 'info': return LogLevel.Info;
      case 'warn': return LogLevel.Warn;
      case 'error': return LogLevel.Error;
      case 'fatal': return LogLevel.Fatal;
      default: return LogLevel.Info;
    }
  }
}

// Usage example
const loggerManager = new LoggerManager();

// Register categories
const appCategory = loggerManager.registerCategory('App');
const authCategory = loggerManager.registerCategory('Auth', appCategory);
const apiCategory = loggerManager.registerCategory('API', appCategory);

// Environment-based configuration
if (process.env.NODE_ENV === 'development') {
  loggerManager.enableDebugMode();
} else {
  loggerManager.setProductionMode();
}

// Runtime level changes (e.g., from admin interface)
function changeLogLevel(category: string, level: string) {
  const logLevel = LogLevel[level as keyof typeof LogLevel];
  loggerManager.setLogLevel(category, logLevel);
}

Angular Integration Example

// Usage example in Angular service
import { Injectable } from '@angular/core';
import { Category, CategoryLogger, CategoryServiceFactory } from 'typescript-logging';

@Injectable({
  providedIn: 'root'
})
export class LoggingService {
  private appCategory = new Category('AngularApp');
  private serviceCategory = new Category('Service', this.appCategory);
  private componentCategory = new Category('Component', this.appCategory);
  
  getServiceLogger(serviceName: string): CategoryLogger {
    const category = new Category(serviceName, this.serviceCategory);
    return CategoryServiceFactory.getLogger(category);
  }
  
  getComponentLogger(componentName: string): CategoryLogger {
    const category = new Category(componentName, this.componentCategory);
    return CategoryServiceFactory.getLogger(category);
  }
}

// Usage in component
import { Component, OnInit } from '@angular/core';
import { LoggingService } from './logging.service';
import { CategoryLogger } from 'typescript-logging';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit {
  private logger: CategoryLogger;
  
  constructor(private loggingService: LoggingService) {
    this.logger = this.loggingService.getComponentLogger('UserList');
  }
  
  ngOnInit(): void {
    this.logger.info('UserListComponent initialization');
    this.loadUsers();
  }
  
  async loadUsers(): Promise<void> {
    this.logger.debug('User list loading started');
    
    try {
      // Get user data
      const users = await this.userService.getUsers();
      
      this.logger.info('User list loading complete', { count: users.length });
      this.users = users;
    } catch (error) {
      this.logger.error('User list loading error', error);
    }
  }
  
  onUserSelect(user: any): void {
    this.logger.debug('User selected', { userId: user.id, userName: user.name });
    // User selection processing
  }
}

React Integration Example

// Usage example in React Hook
import React, { useEffect, useState } from 'react';
import { Category, CategoryLogger, CategoryServiceFactory } from 'typescript-logging';

// Custom hook for logging
function useLogger(componentName: string): CategoryLogger {
  const [logger] = useState(() => {
    const category = new Category(`React.${componentName}`);
    return CategoryServiceFactory.getLogger(category);
  });
  
  return logger;
}

// User list component
interface User {
  id: number;
  name: string;
  email: string;
}

const UserListComponent: React.FC = () => {
  const logger = useLogger('UserList');
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(false);
  
  useEffect(() => {
    logger.info('UserListComponent mounted');
    loadUsers();
    
    return () => {
      logger.info('UserListComponent unmounted');
    };
  }, []);
  
  const loadUsers = async () => {
    logger.debug('User list loading started');
    setLoading(true);
    
    try {
      const response = await fetch('/api/users');
      const userData = await response.json();
      
      logger.info('User list loading complete', { count: userData.length });
      setUsers(userData);
    } catch (error) {
      logger.error('User list loading error', error);
    } finally {
      setLoading(false);
    }
  };
  
  const handleUserClick = (user: User) => {
    logger.debug('User clicked', { userId: user.id, userName: user.name });
    // Navigate to user detail screen, etc.
  };
  
  if (loading) {
    logger.debug('Loading state displayed');
    return <div>Loading...</div>;
  }
  
  return (
    <div>
      <h2>User List</h2>
      {users.map(user => (
        <div key={user.id} onClick={() => handleUserClick(user)}>
          {user.name} ({user.email})
        </div>
      ))}
    </div>
  );
};

export default UserListComponent;