or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

async-operations.mdauthentication.mdcache.mderrors.mdindex.mdlogging.mdtransport.mduser-agent.md
tile.json

logging.mddocs/

Logging

Structured logging interface with debug, info, and error levels for monitoring and debugging Algolia client operations.

Capabilities

Create Null Logger

Creates a no-operation logger that discards all log messages. Useful for production environments where logging is handled externally or disabled.

/**
 * Creates a null logger that discards all log messages
 * @returns Logger instance that performs no operations
 */
function createNullLogger(): Logger;

Usage Examples:

import { createNullLogger } from "@algolia/client-common";

const logger = createNullLogger();

// All log calls are no-ops
await logger.debug("Debug message", { data: "value" });
await logger.info("Info message", { data: "value" });  
await logger.error("Error message", { data: "value" });

// No output is produced

Logger Interface

Standard logging interface with async methods for debug, info, and error levels.

interface Logger {
  /**
   * Logs debug-level messages for detailed troubleshooting
   * @param message - Log message string
   * @param args - Optional additional data to log
   * @returns Promise resolving when log operation completes
   */
  debug: (message: string, args?: any) => Promise<void>;

  /**
   * Logs info-level messages for general information
   * @param message - Log message string
   * @param args - Optional additional data to log
   * @returns Promise resolving when log operation completes
   */
  info: (message: string, args?: any) => Promise<void>;

  /**
   * Logs error-level messages for errors and failures
   * @param message - Log message string
   * @param args - Optional additional data to log
   * @returns Promise resolving when log operation completes
   */
  error: (message: string, args?: any) => Promise<void>;
}

Log Levels

Enumeration and type definitions for log levels with numeric values.

/**
 * Log level enumeration with numeric values
 */
const LogLevelEnum: Readonly<Record<string, LogLevelType>> = {
  Debug: 1,
  Info: 2,
  Error: 3,
};

/**
 * Log level type definition
 */
type LogLevelType = 1 | 2 | 3;

Usage Examples:

import { LogLevelEnum, LogLevelType } from "@algolia/client-common";

// Use log level constants
const currentLevel: LogLevelType = LogLevelEnum.Info;

// Level comparison
function shouldLog(messageLevel: LogLevelType, currentLevel: LogLevelType): boolean {
  return messageLevel >= currentLevel;
}

console.log(shouldLog(LogLevelEnum.Debug, LogLevelEnum.Info)); // false
console.log(shouldLog(LogLevelEnum.Error, LogLevelEnum.Info)); // true

Custom Logger Implementation

The logging interface allows for custom implementations based on your requirements:

Console Logger Example:

import { Logger } from "@algolia/client-common";

class ConsoleLogger implements Logger {
  private minLevel: LogLevelType;

  constructor(minLevel: LogLevelType = LogLevelEnum.Info) {
    this.minLevel = minLevel;
  }

  async debug(message: string, args?: any): Promise<void> {
    if (this.shouldLog(LogLevelEnum.Debug)) {
      console.debug(`[DEBUG] ${message}`, args || '');
    }
  }

  async info(message: string, args?: any): Promise<void> {
    if (this.shouldLog(LogLevelEnum.Info)) {
      console.info(`[INFO] ${message}`, args || '');
    }
  }

  async error(message: string, args?: any): Promise<void> {
    if (this.shouldLog(LogLevelEnum.Error)) {
      console.error(`[ERROR] ${message}`, args || '');
    }
  }

  private shouldLog(level: LogLevelType): boolean {
    return level >= this.minLevel;
  }
}

// Usage
const logger = new ConsoleLogger(LogLevelEnum.Debug);
await logger.debug("Debug message", { requestId: "123" });

Structured Logger Example:

import { Logger } from "@algolia/client-common";

interface LogEntry {
  timestamp: string;
  level: string;
  message: string;
  data?: any;
  source: string;
}

class StructuredLogger implements Logger {
  private logEntries: LogEntry[] = [];

  async debug(message: string, args?: any): Promise<void> {
    this.addEntry('DEBUG', message, args);
  }

  async info(message: string, args?: any): Promise<void> {
    this.addEntry('INFO', message, args);
  }

  async error(message: string, args?: any): Promise<void> {
    this.addEntry('ERROR', message, args);
  }

  private addEntry(level: string, message: string, data?: any): void {
    const entry: LogEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      data,
      source: 'algolia-client-common'
    };

    this.logEntries.push(entry);
    
    // Also output to console in development
    if (process.env.NODE_ENV === 'development') {
      console.log(JSON.stringify(entry, null, 2));
    }
  }

  // Additional methods for log management
  getLogEntries(): LogEntry[] {
    return [...this.logEntries];
  }

  clearLogs(): void {
    this.logEntries = [];
  }

  async flush(): Promise<void> {
    // Send logs to external service
    if (this.logEntries.length > 0) {
      await this.sendToLoggingService(this.logEntries);
      this.clearLogs();
    }
  }

  private async sendToLoggingService(entries: LogEntry[]): Promise<void> {
    // Implementation for external logging service
    // e.g., send to Elasticsearch, CloudWatch, etc.
  }
}

Remote Logger Example:

import { Logger } from "@algolia/client-common";

class RemoteLogger implements Logger {
  private endpoint: string;
  private apiKey: string;
  private buffer: any[] = [];
  private batchSize: number = 10;

  constructor(endpoint: string, apiKey: string) {
    this.endpoint = endpoint;
    this.apiKey = apiKey;
  }

  async debug(message: string, args?: any): Promise<void> {
    await this.log('debug', message, args);
  }

  async info(message: string, args?: any): Promise<void> {
    await this.log('info', message, args);
  }

  async error(message: string, args?: any): Promise<void> {
    await this.log('error', message, args);
  }

  private async log(level: string, message: string, args?: any): Promise<void> {
    const logEntry = {
      timestamp: Date.now(),
      level,
      message,
      data: args,
      sessionId: this.getSessionId()
    };

    this.buffer.push(logEntry);

    if (this.buffer.length >= this.batchSize) {
      await this.flush();
    }
  }

  async flush(): Promise<void> {
    if (this.buffer.length === 0) return;

    const logs = [...this.buffer];
    this.buffer = [];

    try {
      await fetch(this.endpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.apiKey}`
        },
        body: JSON.stringify({ logs })
      });
    } catch (error) {
      // Fall back to console in case of network issues
      console.warn('Failed to send logs to remote service:', error);
      logs.forEach(log => console.log(log));
    }
  }

  private getSessionId(): string {
    // Generate or retrieve session ID
    return 'session-' + Math.random().toString(36).substr(2, 9);
  }
}

Integration with Transport Layer

Logging is integrated throughout the transport layer for monitoring and debugging:

import { createTransporter, createNullLogger, Logger } from "@algolia/client-common";

// Using with transporter
const logger: Logger = createNullLogger(); // or your custom logger

const transporter = createTransporter({
  // ... other options
  logger,
});

// The transporter will automatically log:
// - Retryable failures with stack traces
// - Host state changes
// - Request/response details (in debug mode)

Common Log Messages from Transport Layer:

// Info level - retryable failures
await logger.info('Retryable failure', {
  request: sanitizedRequest,
  response: sanitizedResponse,
  host: hostInfo,
  triesLeft: 2
});

// Debug level - request details  
await logger.debug('Making request', {
  method: 'POST',
  path: '/1/indexes/my-index/query',
  headers: sanitizedHeaders
});

// Error level - critical failures
await logger.error('All hosts unreachable', {
  lastError: errorDetails,
  hostsAttempted: hostList
});

Best Practices

Log Level Usage:

  • Debug: Detailed information for troubleshooting (request/response details, state changes)
  • Info: General operational information (retries, host changes, performance metrics)
  • Error: Error conditions and failures (API errors, network failures, validation errors)

Data Sanitization:

Always sanitize sensitive data before logging:

function sanitizeForLogging(data: any): any {
  if (!data) return data;
  
  const sanitized = { ...data };
  
  // Remove sensitive fields
  if ('x-algolia-api-key' in sanitized) {
    sanitized['x-algolia-api-key'] = '*****';
  }
  
  if ('apiKey' in sanitized) {
    sanitized['apiKey'] = '*****';
  }
  
  return sanitized;
}

// Usage in custom logger
await logger.info('Request completed', sanitizeForLogging(requestData));

Performance Considerations:

  • Use async logging to avoid blocking operations
  • Implement batching for remote logging services
  • Consider log level filtering to reduce noise
  • Buffer logs for performance-critical paths
  • Implement log rotation for file-based logging

Error Handling in Loggers:

class SafeLogger implements Logger {
  private fallbackLogger: Logger;

  constructor(private primaryLogger: Logger, fallbackLogger?: Logger) {
    this.fallbackLogger = fallbackLogger || createNullLogger();
  }

  async debug(message: string, args?: any): Promise<void> {
    try {
      await this.primaryLogger.debug(message, args);
    } catch (error) {
      await this.fallbackLogger.debug(`Logging failed: ${message}`, { originalArgs: args, error });
    }
  }

  async info(message: string, args?: any): Promise<void> {
    try {
      await this.primaryLogger.info(message, args);
    } catch (error) {
      await this.fallbackLogger.info(`Logging failed: ${message}`, { originalArgs: args, error });
    }
  }

  async error(message: string, args?: any): Promise<void> {
    try {
      await this.primaryLogger.error(message, args);
    } catch (error) {
      await this.fallbackLogger.error(`Logging failed: ${message}`, { originalArgs: args, error });
    }
  }
}

Environment Considerations

  • Development: Use console logger with debug level for full visibility
  • Production: Use structured logging with appropriate level filtering
  • Browser: Consider log aggregation to avoid console spam
  • Node.js: Use file-based or remote logging for persistence
  • Serverless: Use cloud-native logging services for proper aggregation