380 lines
8.8 KiB
V
380 lines
8.8 KiB
V
module hono
|
|
|
|
import os
|
|
import time
|
|
import json
|
|
|
|
// 日志级别枚举
|
|
pub enum LogLevel {
|
|
debug = 0
|
|
info = 1
|
|
warn = 2
|
|
error = 3
|
|
}
|
|
|
|
// 日志级别字符串映射
|
|
const log_level_strings = {
|
|
LogLevel.debug: 'DEBUG'
|
|
LogLevel.info: 'INFO'
|
|
LogLevel.warn: 'WARN'
|
|
LogLevel.error: 'ERROR'
|
|
}
|
|
|
|
// 日志输出类型
|
|
pub enum LogOutput {
|
|
console
|
|
file
|
|
both
|
|
}
|
|
|
|
// 日志条目结构
|
|
pub struct LogEntry {
|
|
pub mut:
|
|
timestamp string
|
|
level string
|
|
message string
|
|
module string
|
|
function string
|
|
file string
|
|
line int
|
|
fields map[string]string
|
|
request_id string
|
|
}
|
|
|
|
// 日志配置
|
|
pub struct LoggerConfig {
|
|
pub mut:
|
|
level LogLevel = LogLevel.info
|
|
output LogOutput = LogOutput.console
|
|
file_path string = './logs/app.log'
|
|
max_file_size u64 = 10 * 1024 * 1024 // 10MB
|
|
max_backup_files int = 5
|
|
enable_colors bool = true
|
|
enable_json bool
|
|
time_format string = '2006-01-02 15:04:05'
|
|
}
|
|
|
|
// 日志器结构
|
|
pub struct Logger {
|
|
pub mut:
|
|
config LoggerConfig
|
|
current_file_size u64
|
|
}
|
|
|
|
// 创建新的日志器实例
|
|
pub fn new_logger(config LoggerConfig) &Logger {
|
|
return &Logger{
|
|
config: config
|
|
}
|
|
}
|
|
|
|
// 字符串转日志级别
|
|
pub fn parse_log_level(level_str string) LogLevel {
|
|
match level_str.to_lower() {
|
|
'debug' { return LogLevel.debug }
|
|
'info' { return LogLevel.info }
|
|
'warn' { return LogLevel.warn }
|
|
'error' { return LogLevel.error }
|
|
else { return LogLevel.info }
|
|
}
|
|
}
|
|
|
|
// 日志级别转字符串
|
|
pub fn log_level_to_string(level LogLevel) string {
|
|
return log_level_strings[level] or { 'INFO' }
|
|
}
|
|
|
|
// 检查日志级别是否应该输出
|
|
fn (l &Logger) should_log(level LogLevel) bool {
|
|
return int(level) >= int(l.config.level)
|
|
}
|
|
|
|
// 格式化时间戳
|
|
fn format_timestamp() string {
|
|
now := time.now()
|
|
return now.format_ss()
|
|
}
|
|
|
|
// 获取ANSI颜色代码
|
|
fn get_color_code(level LogLevel) string {
|
|
match level {
|
|
.debug { return '\033[36m' } // 青色
|
|
.info { return '\033[32m' } // 绿色
|
|
.warn { return '\033[33m' } // 黄色
|
|
.error { return '\033[31m' } // 红色
|
|
}
|
|
}
|
|
|
|
// 重置ANSI颜色
|
|
const color_reset = '\033[0m'
|
|
|
|
// 格式化日志消息(文本格式)
|
|
fn (l &Logger) format_text_log(entry LogEntry) string {
|
|
mut parts := []string{}
|
|
|
|
// 时间戳
|
|
parts << '[${entry.timestamp}]'
|
|
|
|
// 日志级别(带颜色)
|
|
if l.config.enable_colors {
|
|
color := get_color_code(parse_log_level(entry.level))
|
|
parts << '${color}${entry.level}${color_reset}'
|
|
} else {
|
|
parts << entry.level
|
|
}
|
|
|
|
// 模块信息
|
|
if entry.module != '' {
|
|
parts << '[${entry.module}]'
|
|
}
|
|
|
|
// 消息
|
|
parts << entry.message
|
|
|
|
// 附加字段
|
|
if entry.fields.len > 0 {
|
|
mut field_parts := []string{}
|
|
for key, value in entry.fields {
|
|
field_parts << '${key}=${value}'
|
|
}
|
|
parts << '{${field_parts.join(', ')}}'
|
|
}
|
|
|
|
// 请求ID
|
|
if entry.request_id != '' {
|
|
parts << '[req:${entry.request_id}]'
|
|
}
|
|
|
|
return parts.join(' ')
|
|
}
|
|
|
|
// 格式化日志消息(JSON格式)
|
|
fn (l &Logger) format_json_log(entry LogEntry) string {
|
|
return json.encode(entry)
|
|
}
|
|
|
|
// 写入日志到文件
|
|
fn (mut l Logger) write_to_file(content string) {
|
|
// 确保日志目录存在
|
|
log_dir := os.dir(l.config.file_path)
|
|
if !os.exists(log_dir) {
|
|
os.mkdir_all(log_dir) or {
|
|
eprintln('无法创建日志目录: ${err}')
|
|
return
|
|
}
|
|
}
|
|
|
|
// 检查文件大小,必要时轮转
|
|
if l.config.max_file_size > 0 {
|
|
if os.exists(l.config.file_path) {
|
|
file_size := os.file_size(l.config.file_path)
|
|
if file_size >= l.config.max_file_size {
|
|
l.rotate_log_file()
|
|
}
|
|
}
|
|
}
|
|
|
|
// 写入日志
|
|
mut file := os.open_append(l.config.file_path) or {
|
|
eprintln('无法打开日志文件: ${err}')
|
|
return
|
|
}
|
|
defer {
|
|
file.close()
|
|
}
|
|
|
|
file.writeln(content) or {
|
|
eprintln('无法写入日志文件: ${err}')
|
|
}
|
|
}
|
|
|
|
// 轮转日志文件
|
|
fn (l &Logger) rotate_log_file() {
|
|
// 删除最旧的备份文件
|
|
oldest_backup := '${l.config.file_path}.${l.config.max_backup_files}'
|
|
if os.exists(oldest_backup) {
|
|
os.rm(oldest_backup) or {}
|
|
}
|
|
|
|
// 移动现有备份文件
|
|
for i := l.config.max_backup_files - 1; i >= 1; i-- {
|
|
old_file := '${l.config.file_path}.${i}'
|
|
new_file := '${l.config.file_path}.${i + 1}'
|
|
if os.exists(old_file) {
|
|
os.mv(old_file, new_file) or {}
|
|
}
|
|
}
|
|
|
|
// 移动当前日志文件为第一个备份
|
|
if os.exists(l.config.file_path) {
|
|
backup_file := '${l.config.file_path}.1'
|
|
os.mv(l.config.file_path, backup_file) or {}
|
|
}
|
|
}
|
|
|
|
// 核心日志方法
|
|
fn (mut l Logger) log(level LogLevel, message string, mod_name string, fields map[string]string, request_id string) {
|
|
if !l.should_log(level) {
|
|
return
|
|
}
|
|
|
|
entry := LogEntry{
|
|
timestamp: format_timestamp()
|
|
level: log_level_to_string(level)
|
|
message: message
|
|
module: mod_name
|
|
fields: fields
|
|
request_id: request_id
|
|
}
|
|
|
|
// 格式化日志内容
|
|
content := if l.config.enable_json {
|
|
l.format_json_log(entry)
|
|
} else {
|
|
l.format_text_log(entry)
|
|
}
|
|
|
|
// 输出日志
|
|
match l.config.output {
|
|
.console {
|
|
println(content)
|
|
}
|
|
.file {
|
|
l.write_to_file(content)
|
|
}
|
|
.both {
|
|
println(content)
|
|
l.write_to_file(content)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 公共日志方法
|
|
pub fn (mut l Logger) debug(message string) {
|
|
l.log(LogLevel.debug, message, '', {}, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) info(message string) {
|
|
l.log(LogLevel.info, message, '', {}, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) warn(message string) {
|
|
l.log(LogLevel.warn, message, '', {}, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) error(message string) {
|
|
l.log(LogLevel.error, message, '', {}, '')
|
|
}
|
|
|
|
// 带模块的日志方法
|
|
pub fn (mut l Logger) debug_with_module(message string, mod_name string) {
|
|
l.log(LogLevel.debug, message, mod_name, {}, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) info_with_module(message string, mod_name string) {
|
|
l.log(LogLevel.info, message, mod_name, {}, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) warn_with_module(message string, mod_name string) {
|
|
l.log(LogLevel.warn, message, mod_name, {}, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) error_with_module(message string, mod_name string) {
|
|
l.log(LogLevel.error, message, mod_name, {}, '')
|
|
}
|
|
|
|
// 带字段的日志方法
|
|
pub fn (mut l Logger) debug_with_fields(message string, fields map[string]string) {
|
|
l.log(LogLevel.debug, message, '', fields, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) info_with_fields(message string, fields map[string]string) {
|
|
l.log(LogLevel.info, message, '', fields, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) warn_with_fields(message string, fields map[string]string) {
|
|
l.log(LogLevel.warn, message, '', fields, '')
|
|
}
|
|
|
|
pub fn (mut l Logger) error_with_fields(message string, fields map[string]string) {
|
|
l.log(LogLevel.error, message, '', fields, '')
|
|
}
|
|
|
|
// 带请求ID的日志方法
|
|
pub fn (mut l Logger) debug_with_request(message string, request_id string) {
|
|
l.log(LogLevel.debug, message, '', {}, request_id)
|
|
}
|
|
|
|
pub fn (mut l Logger) info_with_request(message string, request_id string) {
|
|
l.log(LogLevel.info, message, '', {}, request_id)
|
|
}
|
|
|
|
pub fn (mut l Logger) warn_with_request(message string, request_id string) {
|
|
l.log(LogLevel.warn, message, '', {}, request_id)
|
|
}
|
|
|
|
pub fn (mut l Logger) error_with_request(message string, request_id string) {
|
|
l.log(LogLevel.error, message, '', {}, request_id)
|
|
}
|
|
|
|
// 完整的日志方法
|
|
pub fn (mut l Logger) log_full(level LogLevel, message string, mod_name string, fields map[string]string, request_id string) {
|
|
l.log(level, message, mod_name, fields, request_id)
|
|
}
|
|
|
|
// HTTP请求日志结构
|
|
pub struct RequestLog {
|
|
pub mut:
|
|
method string
|
|
path string
|
|
status_code int
|
|
response_time f64 // 毫秒
|
|
user_agent string
|
|
remote_addr string
|
|
request_size u64
|
|
response_size u64
|
|
request_id string
|
|
}
|
|
|
|
// 记录HTTP请求日志
|
|
pub fn log_request(mut logger Logger, req_log RequestLog) {
|
|
fields := {
|
|
'method': req_log.method
|
|
'path': req_log.path
|
|
'status': req_log.status_code.str()
|
|
'response_time': '${req_log.response_time:.2f}ms'
|
|
'user_agent': req_log.user_agent
|
|
'remote_addr': req_log.remote_addr
|
|
'request_size': req_log.request_size.str()
|
|
'response_size': req_log.response_size.str()
|
|
}
|
|
|
|
message := '${req_log.method} ${req_log.path} ${req_log.status_code} ${req_log.response_time:.2f}ms'
|
|
logger.log_full(LogLevel.info, message, 'HTTP', fields, req_log.request_id)
|
|
}
|
|
|
|
// 性能监控日志
|
|
pub fn log_performance(mut logger Logger, operation string, duration f64, details map[string]string) {
|
|
mut fields := {
|
|
'operation': operation
|
|
'duration': '${duration:.2f}ms'
|
|
}
|
|
|
|
// 合并详细信息
|
|
for key, value in details {
|
|
fields[key] = value
|
|
}
|
|
|
|
message := '性能监控: ${operation} 耗时 ${duration:.2f}ms'
|
|
logger.log_full(LogLevel.info, message, 'PERF', fields, '')
|
|
}
|
|
|
|
// 错误日志(带堆栈信息)
|
|
pub fn log_error_with_stack(mut logger Logger, message string, err_details string, mod_name string) {
|
|
fields := {
|
|
'error_details': err_details
|
|
'stack_trace': 'V语言暂不支持堆栈跟踪'
|
|
}
|
|
|
|
logger.log_full(LogLevel.error, message, mod_name, fields, '')
|
|
} |