跳到主要内容

日志管理

1. 初始化日志​

初始化宿主日志。提供打印日志等级,代理宿主日志打印功能。

接口原型​

hostHandle.initLog(level, printer)

参数说明​

参数数据类型必填属性说明
levelString是日志优先级字符串
printerObject否日志打印器,用于自定义日志输出

LogLevel 日志等级​

可以使用以下字符串值作为日志等级:

等级字符串说明
'debug'调试信息
'info'一般信息
'warning' 或 'warn'警告信息
'error'错误信息
'fault'严重错误

日志打印器接口​

日志打印器是一个对象,需要实现 printLog 方法:

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

参数说明

参数数据类型说明
levelString日志等级字符串 (如 'debug', 'info', 'error' 等)
messageString日志消息

代码示例​

基本使用​

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');
});

使用自定义日志打印器​

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
});

日志写入文件​

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

多目标日志输出​

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

完整示例​

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();

最佳实践​

  1. 开发环境使用 DEBUG 级别

    const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info';
    hostHandle.initLog(level, printer);
  2. 生产环境使用 INFO 或 WARNING 级别

    const level = process.env.NODE_ENV === 'production' ? 'warning' : 'debug';
  3. 实现日志轮转

    • 当日志文件超过一定大小时自动创建新文件
    • 保留最近 N 个日志文件
  4. 日志文件位置

    • macOS: ~/Library/Logs/WebGLHost/
    • Windows: %APPDATA%\WebGLHost\Logs\
    • Linux: ~/.local/share/WebGLHost/logs/
  5. 性能考虑

    • 避免在高频操作中打印过多日志
    • 文件 I/O 使用异步操作(在非关键路径)
    • 考虑使用日志缓冲机制
  6. 日志隐私

    • 不要记录敏感信息(密码、token 等)
    • 对用户数据进行脱敏处理