日志管理
1. 初始化日志
初始化宿主日志。提供打印日志等级,代理宿主日志打印功能。
接口原型
hostHandle.initLog(level, printer)
参数说明
| 参数 | 数据类型 | 必填 | 属性说明 |
|---|---|---|---|
level | String | 是 | 日志优先级字符串 |
printer | Object | 否 | 日志打印器,用于自定义日志输出 |
LogLevel 日志等级
可以使用以下字符串值作为日志等级:
| 等级字符串 | 说明 |
|---|---|
'debug' | 调试信息 |
'info' | 一般信息 |
'warning' 或 'warn' | 警告信息 |
'error' | 错误信息 |
'fault' | 严重错误 |
日志打印器接口
日志打印器是一个对象,需要实现 printLog 方法:
{
printLog: (level, message) => void
}
参数说明
| 参数 | 数据类型 | 说明 |
|---|---|---|
level | String | 日志等级字符串 (如 'debug', 'info', 'error' 等) |
message | String | 日志消息 |
代码示例
基本使用
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();
最佳实践
-
开发环境使用 DEBUG 级别
const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info';hostHandle.initLog(level, printer); -
生产环境使用 INFO 或 WARNING 级别
const level = process.env.NODE_ENV === 'production' ? 'warning' : 'debug'; -
实现日志轮转
- 当日志文件超过一定大小时自动创建新文件
- 保留最近 N 个日志文件
-
日志文件位置
- macOS:
~/Library/Logs/WebGLHost/ - Windows:
%APPDATA%\WebGLHost\Logs\ - Linux:
~/.local/share/WebGLHost/logs/
- macOS:
-
性能考虑
- 避免在高频操作中打印过多日志
- 文件 I/O 使用异步操作(在非关键路径)
- 考虑使用日志缓冲机制
-
日志隐私
- 不要记录敏感信息(密码、token 等)
- 对用户数据进行脱敏处理