The Homebridge logging system provides a structured framework with prefixes, log levels, and formatting options for plugin debugging and system monitoring. It supports both plugin-specific logging through the
LoggingLoggerThe main logging interface provided to plugins for structured output.
/**
* Plugin logging interface provided to all plugins
* Supports multiple log levels and automatic prefixing
*/
interface Logging {
/** Plugin-specific log prefix */
readonly prefix: string;
/** Log informational messages */
info(message: string, ...parameters: any[]): void;
/** Log success messages (green output) */
success(message: string, ...parameters: any[]): void;
/** Log warning messages (yellow output) */
warn(message: string, ...parameters: any[]): void;
/** Log error messages (red output) */
error(message: string, ...parameters: any[]): void;
/** Log debug messages (only shown when debug mode enabled) */
debug(message: string, ...parameters: any[]): void;
/** Log messages with specific level */
log(level: LogLevel, message: string, ...parameters: any[]): void;
/** Log info messages using function call syntax */
(message: string, ...parameters: any[]): void;
}Usage Examples:
import { AccessoryPlugin, Logger, AccessoryConfig, API } from "homebridge";
class MyAccessory implements AccessoryPlugin {
constructor(
private log: Logger, // Logging interface
private config: AccessoryConfig,
private api: API
) {
// Basic logging
this.log.info("Initializing accessory:", this.config.name);
this.log.success("Configuration loaded successfully");
this.log.warn("Using default polling interval");
this.log.debug("Debug information:", { config: this.config });
// Function call syntax for info level
this.log("Accessory ready"); // Same as this.log.info("Accessory ready")
// Error logging with details
try {
this.initializeDevice();
} catch (error) {
this.log.error("Failed to initialize device:", error);
}
// Formatted logging with parameters
this.log.info("Device status: %s, Temperature: %d°C", "online", 23);
}
private async getState() {
this.log.debug("Getting device state");
try {
const state = await this.fetchDeviceState();
this.log.debug("Retrieved state:", state);
return state;
} catch (error) {
this.log.error("Failed to get state:", error.message);
throw error;
}
}
}The main Logger implementation with static methods for system-wide configuration.
/**
* Main logger implementation
* Provides static methods for system configuration and instance methods for logging
*/
class Logger implements Logging {
/** Internal system logger instance */
static readonly internal: Logger;
/** Plugin-specific log prefix */
readonly prefix: string;
/** Create a logger with a specific prefix */
static withPrefix(prefix: string): Logger;
/** Enable or disable debug logging globally */
static setDebugEnabled(enabled: boolean): void;
/** Enable or disable timestamps in log output */
static setTimestampEnabled(enabled: boolean): void;
/** Force color output even when not connected to a TTY */
static forceColor(): void;
/** Log informational messages */
info(message: string, ...parameters: any[]): void;
/** Log success messages (green output) */
success(message: string, ...parameters: any[]): void;
/** Log warning messages (yellow output) */
warn(message: string, ...parameters: any[]): void;
/** Log error messages (red output) */
error(message: string, ...parameters: any[]): void;
/** Log debug messages (only shown when debug mode enabled) */
debug(message: string, ...parameters: any[]): void;
/** Log messages with specific level */
log(level: LogLevel, message: string, ...parameters: any[]): void;
/** Log info messages using function call syntax */
(message: string, ...parameters: any[]): void;
}Usage Examples:
// Create plugin-specific logger
const myLogger = Logger.withPrefix("MyPlugin");
myLogger.info("Plugin initialized");
// Configure global logging settings
Logger.setDebugEnabled(true);
Logger.setTimestampEnabled(false);
Logger.forceColor();
// Use internal logger for system messages
Logger.internal.warn("System warning message");
// Different log levels
const logger = Logger.withPrefix("DeviceManager");
logger.info("Device discovery started");
logger.success("Found 5 devices");
logger.warn("Device offline: Living Room Light");
logger.error("Connection failed: Kitchen Switch");
logger.debug("Device response:", { status: "ok", data: [...] });
// Formatted logging
logger.info("Device %s changed state to %s", deviceName, newState);
logger.debug("Processing %d devices in %dms", deviceCount, processingTime);Enumeration of available log levels with their output formatting.
/**
* Available log levels for structured logging
* Each level has distinct formatting and color coding
*/
enum LogLevel {
/** Informational messages (white/default color) */
INFO = "info",
/** Success messages (green color) */
SUCCESS = "success",
/** Warning messages (yellow color) */
WARN = "warn",
/** Error messages (red color) */
ERROR = "error",
/** Debug messages (gray color, only shown when debug enabled) */
DEBUG = "debug"
}Usage Examples:
// Using log levels explicitly
logger.log(LogLevel.INFO, "System ready");
logger.log(LogLevel.SUCCESS, "Operation completed");
logger.log(LogLevel.WARN, "Configuration issue detected");
logger.log(LogLevel.ERROR, "Critical error occurred");
logger.log(LogLevel.DEBUG, "Detailed debugging information");
// Using convenience methods (preferred)
logger.info("System ready");
logger.success("Operation completed");
logger.warn("Configuration issue detected");
logger.error("Critical error occurred");
logger.debug("Detailed debugging information");
// Conditional logging based on level
const logLevel = LogLevel.DEBUG;
if (logLevel === LogLevel.DEBUG) {
logger.debug("Verbose debugging information");
}Logging integration patterns for different plugin types.
Accessory Plugin Logging:
class MyAccessory implements AccessoryPlugin {
constructor(
private log: Logger,
private config: AccessoryConfig,
private api: API
) {
this.log.info("Accessory starting:", this.config.name);
}
async setState(value: boolean) {
this.log.debug("Setting state to:", value);
try {
await this.deviceApi.setState(value);
this.log.success("State changed successfully");
} catch (error) {
this.log.error("Failed to set state:", error.message);
throw error;
}
}
}Platform Plugin Logging:
class MyPlatform implements DynamicPlatformPlugin {
private readonly log: Logger;
constructor(log: Logger, config: PlatformConfig, api: API) {
this.log = log;
this.log.info("Platform initializing with", config.devices?.length || 0, "devices");
}
configureAccessory(accessory: PlatformAccessory) {
this.log.info("Configuring cached accessory:", accessory.displayName);
// Create accessory-specific logger
const accessoryLog = Logger.withPrefix(`${this.log.prefix}:${accessory.displayName}`);
accessoryLog.debug("Restoring accessory services");
}
private async discoverDevices() {
this.log.info("Starting device discovery");
try {
const devices = await this.deviceApi.discover();
this.log.success("Discovered", devices.length, "devices");
for (const device of devices) {
this.log.debug("Found device:", device.name, device.id);
}
} catch (error) {
this.log.error("Device discovery failed:", error.message);
}
}
}Legacy logging functions maintained for backward compatibility.
/**
* @deprecated Use Logger.withPrefix() instead
*/
function withPrefix(prefix: string): Logger;
/**
* @deprecated Use Logger.setDebugEnabled() instead
*/
function setDebugEnabled(enabled: boolean): void;
/**
* @deprecated Use Logger.setTimestampEnabled() instead
*/
function setTimestampEnabled(enabled: boolean): void;
/**
* @deprecated Use Logger.forceColor() instead
*/
function forceColor(): void;Contextual Logging:
class DeviceManager {
private createLogger(context: string): Logger {
return Logger.withPrefix(`DeviceManager:${context}`);
}
async manageDevice(deviceId: string) {
const log = this.createLogger(deviceId);
log.info("Starting device management");
log.debug("Device configuration:", this.getDeviceConfig(deviceId));
try {
await this.initializeDevice(deviceId);
log.success("Device initialized successfully");
} catch (error) {
log.error("Device initialization failed:", error.message);
}
}
}Structured Logging:
class ApiClient {
private log = Logger.withPrefix("ApiClient");
async makeRequest(endpoint: string, data: any) {
const requestId = Math.random().toString(36).substr(2, 9);
const requestLog = Logger.withPrefix(`ApiClient:${requestId}`);
requestLog.info("Making API request to:", endpoint);
requestLog.debug("Request data:", data);
const startTime = Date.now();
try {
const response = await this.httpClient.post(endpoint, data);
const duration = Date.now() - startTime;
requestLog.success("Request completed in", duration, "ms");
requestLog.debug("Response:", response.data);
return response.data;
} catch (error) {
const duration = Date.now() - startTime;
requestLog.error("Request failed after", duration, "ms:", error.message);
throw error;
}
}
}type LogLevel = "info" | "success" | "warn" | "error" | "debug";