Skip to main content

Log Management

1 Initialize Log​

Initialize the host log. Provide the function to print log levels and proxy host log printing.

1.1 Interface Prototype​

hostHandle.initLog(level, printer)

1.2 Parameter Description​

parameterData TypeRequiredAttribute Description
levelStringYesLog Priority String
printerObjectNoLog Printer, used for customizing log output

1.3 LogLevel Log Level​

The following string values can be used as log levels:

Level StringDescription
'debug'Debug Information
'info'General Information
'warning' or 'warn'Warning Message
'error'Error Message
'fault'Severe Error

1.4 Log Printer Interface​

The Log Printer is an object that needs to implement the printLog method:

{
printLog: (level, message) => void
}

Parameter Description

parameterData TypeDescription
levelStringLog level string (e.g., 'debug', 'info', 'error', etc.)
messageStringLog Message

1.5 Code Example​

1.5.1 Basic Usage​

const { TJHostHandle } = require('webglhost-runtime-pc');

// Initialize SDK
TJHostHandle.initialize('sdk-key', 'sdk-secret', (hostHandle, error) => {
if (error) {
console.error('SDK initialization failed:', error);
return;
}

// Initialize log with DEBUG level, using default console printer
hostHandle.initLog('debug');

console.log('Log initialized with DEBUG level');
});

1.5.2 Use Custom Log Printer​

const { TJHostHandle } = require('webglhost-runtime-pc');

// Custom log printer
class CustomLogPrinter {
printLog(level, message) {
const timestamp = new Date().toISOString();
const formattedMessage = `[${timestamp}] [${level}] ${message}`;

// Output to console with colors
switch (level.toLowerCase()) {
case 'debug':
console.log('\x1b[36m%s\x1b[0m', formattedMessage); // Cyan
break;
case 'info':
console.log('\x1b[32m%s\x1b[0m', formattedMessage); // Green
break;
case 'warning':
case 'warn':
console.log('\x1b[33m%s\x1b[0m', formattedMessage); // Yellow
break;
case 'error':
case 'fault':
console.log('\x1b[31m%s\x1b[0m', formattedMessage); // Red
break;
default:
console.log(formattedMessage);
}
}
}

// Initialize with custom printer
TJHostHandle.initialize('sdk-key', 'sdk-secret', (hostHandle, error) => {
if (error) return;

const printer = new CustomLogPrinter();
hostHandle.initLog('info', printer); // INFO level with custom printer
});

1.5.3 Log Writing to File​

const fs = require('fs');
const path = require('path');
const os = require('os');

class FileLogPrinter {
constructor(logFilePath) {
this.logFilePath = logFilePath;
this.ensureLogFile();
}

ensureLogFile() {
const dir = path.dirname(this.logFilePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}

printLog(level, message) {
const timestamp = new Date().toISOString();
const logEntry = `[${timestamp}] [${level}] ${message}\n`;

// Write to console
console.log(logEntry.trim());

// Write to file
fs.appendFileSync(this.logFilePath, logEntry, 'utf-8');
}
}

// Usage
const logPath = path.join(os.homedir(), '.webglhost', 'logs', 'game.log');
const filePrinter = new FileLogPrinter(logPath);

hostHandle.initLog('debug', filePrinter); // DEBUG level, log to file

1.5.4 Multi-target Log Output​

class MultiTargetLogPrinter {
constructor(targets) {
this.targets = targets; // Array of log printers
}

printLog(level, message) {
// Send log to all targets
for (const target of this.targets) {
try {
target.printLog(level, message);
} catch (error) {
console.error('Error in log target:', error);
}
}
}
}

// Create multiple log targets
const consolePrinter = new CustomLogPrinter();
const filePrinter = new FileLogPrinter('./logs/game.log');

// Combine them
const multiPrinter = new MultiTargetLogPrinter([consolePrinter, filePrinter]);

hostHandle.initLog('info', multiPrinter); // INFO level, log to console and file

2 Complete Example​

const { TJHostHandle } = require('webglhost-runtime-pc');
const fs = require('fs');
const path = require('path');
const os = require('os');

class AdvancedLogPrinter {
constructor(options = {}) {
this.logToConsole = options.logToConsole !== false;
this.logToFile = options.logToFile !== false;
this.logFilePath = options.logFilePath || this.getDefaultLogPath();
this.maxFileSize = options.maxFileSize || 10 * 1024 * 1024; // 10MB
this.logFilter = options.logFilter || ((level) => true);

if (this.logToFile) {
this.ensureLogDirectory();
}
}

getDefaultLogPath() {
const logsDir = path.join(os.homedir(), '.webglhost', 'logs');
const timestamp = new Date().toISOString().replace(/:/g, '-').split('.')[0];
return path.join(logsDir, `game-${timestamp}.log`);
}

ensureLogDirectory() {
const dir = path.dirname(this.logFilePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}

printLog(level, message) {
// Apply filter
if (!this.logFilter(level)) {
return;
}

const timestamp = new Date().toISOString();
const levelName = this.getLevelName(level);
const formattedMessage = `[${timestamp}] [${levelName}] ${message}`;

// Console output
if (this.logToConsole) {
this.printToConsole(level, formattedMessage);
}

// File output
if (this.logToFile) {
this.printToFile(formattedMessage);
}
}

printToConsole(level, message) {
const colors = {
'debug': '\x1b[36m', // Cyan
'info': '\x1b[32m', // Green
'warning': '\x1b[33m', // Yellow
'warn': '\x1b[33m', // Yellow
'error': '\x1b[31m', // Red
'fault': '\x1b[35m' // Magenta
};
const resetColor = '\x1b[0m';
const color = colors[level.toLowerCase()] || '';
console.log(`${color}${message}${resetColor}`);
}

printToFile(message) {
try {
// Check file size and rotate if necessary
if (fs.existsSync(this.logFilePath)) {
const stats = fs.statSync(this.logFilePath);
if (stats.size > this.maxFileSize) {
this.rotateLogFile();
}
}

// Append to log file
fs.appendFileSync(this.logFilePath, message + '\n', 'utf-8');
} catch (error) {
console.error('Failed to write to log file:', error);
}
}

rotateLogFile() {
const ext = path.extname(this.logFilePath);
const base = this.logFilePath.slice(0, -ext.length);
const timestamp = new Date().toISOString().replace(/:/g, '-').split('.')[0];
const newPath = `${base}-${timestamp}${ext}`;

fs.renameSync(this.logFilePath, newPath);
console.log(`Log file rotated: ${newPath}`);
}
}

// Usage example
class GameLogger {
constructor() {
this.hostHandle = null;
this.logLevel = 'info';
}

async initialize(sdkKey, sdkSecret) {
return new Promise((resolve, reject) => {
TJHostHandle.initialize(sdkKey, sdkSecret, (hostHandle, error) => {
if (error) {
reject(error);
return;
}

this.hostHandle = hostHandle;
this.setupLogger();
resolve();
});
});
}

setupLogger() {
// Determine log level based on environment
const isDevelopment = process.env.NODE_ENV === 'development';
this.logLevel = isDevelopment ? 'debug' : 'info';

// Create advanced log printer
const printer = new AdvancedLogPrinter({
logToConsole: true,
logToFile: true,
logFilePath: path.join(os.homedir(), '.webglhost', 'logs', 'game.log'),
maxFileSize: 10 * 1024 * 1024, // 10MB
logFilter: (level) => {
// Only log messages at or above current log level
const levelOrder = ['debug', 'info', 'warning', 'error', 'fault'];
const currentIndex = levelOrder.indexOf(this.logLevel.toLowerCase());
const messageIndex = levelOrder.indexOf(level.toLowerCase());
return messageIndex >= currentIndex;
}
});

// Initialize log system
this.hostHandle.initLog(this.logLevel, printer);
console.log(`Logger initialized with level: ${this.logLevel}`);
}

setLogLevel(level) {
this.logLevel = level;
this.setupLogger(); // Re-initialize with new level
}
}

// Main usage
async function main() {
const logger = new GameLogger();

try {
await logger.initialize('sdk-key', 'sdk-secret');

// Change log level at runtime (if needed)
if (process.argv.includes('--debug')) {
logger.setLogLevel('debug');
}

console.log('Application started with logging enabled');
} catch (error) {
console.error('Failed to initialize logger:', error);
}
}

main();

3 Best Practices​

  1. The Development Environment uses DEBUG level

    const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info';
    hostHandle.initLog(level, printer);
  2. Use INFO or WARNING level in the production environment

    const level = process.env.NODE_ENV === 'production' ? 'warning' : 'debug';
  1. Implement log rotation

    • Automatically create a new file when the log file exceeds a certain size

    • Retain the last N log files

  2. Log File Location

    • macOS: ~/Library/Logs/WebGLHost/

    • Windows: %APPDATA%\WebGLHost\Logs\

    • Linux: ~/.local/share/WebGLHost/logs/

  1. Performance Considerations

    • Avoid printing excessive logs during high-frequency operations

    • File I/O uses asynchronous operations (on non-Critical Path)

    • Consider using a log buffering mechanism

  2. Log Privacy

    • Do not record sensitive information (passwords, tokens, etc.)

    • Desensitize user data