Sfoglia il codice sorgente

add: exmtn, 增加exmtn扩展库

zengeshuai 1 mese fa
parent
commit
8ff107a1fb
1 ha cambiato i file con 789 aggiunte e 0 eliminazioni
  1. 789 0
      script/libs/exmtn.lua

+ 789 - 0
script/libs/exmtn.lua

@@ -0,0 +1,789 @@
+--[[
+@module  exmtn
+@summary 运维日志扩展库,负责日志的持久化存储
+@version 1.0
+@date    2025.12.9
+@author  zengeshuai
+@usage
+exmtn.init(1, 0)  -- 初始化,1个块,缓存写入
+exmtn.log("info", "tag", "message", 123)  -- 输出运维日志
+]]
+
+local exmtn = {}
+
+-- 常量定义
+local LOG_MTN_CACHE_SIZE = 4096
+local LOG_MTN_FILE_COUNT = 4
+local LOG_MTN_CONFIG_FILE = "/exmtn.trc"
+local LOG_MTN_DEFAULT_BLOCKS_DIVISOR = 40
+local LOG_MTN_ADD_WRITE_THRESHOLD = 256
+local LOG_MTN_CONFIG_VERSION = 1
+
+-- 写入方式常量
+exmtn.CACHE_WRITE = 0
+exmtn.ADD_WRITE = 1
+
+-- 内部状态
+local ctx = {
+    inited = false,
+    enabled = false,
+    cur_index = 1,           -- 1-4
+    block_size = 4096,       -- 默认块大小
+    blocks_per_file = 1,     -- 每文件块数
+    file_limit = 4096,       -- 每文件大小限制
+    write_way = 0,           -- 0=缓存写入, 1=直接追加
+    cache = "",              -- 缓存缓冲区
+    cache_used = 0,          -- 缓存已使用字节数
+}
+
+-- 重置缓存
+local function reset_cache()
+    ctx.cache = ""
+    ctx.cache_used = 0
+end
+
+-- 获取当前文件路径
+local function get_file_path(index)
+    return string.format("/hzmtn%d.trc", index or ctx.cur_index)
+end
+
+-- 获取当前文件大小
+local function get_current_file_size()
+    local path = get_file_path()
+    local file = io.open(path, "rb")
+    if not file then
+        return 0
+    end
+    local size = file:seek("end")
+    file:close()
+    -- file:seek("end") 返回文件大小,如果失败返回 nil
+    if size and size > 0 then
+        return size
+    end
+    return 0
+end
+
+-- 检查文件是否存在
+local function file_exists(path)
+    local file = io.open(path, "rb")
+    if file then
+        file:close()
+        return true
+    end
+    return false
+end
+
+-- 检查所有日志文件是否存在
+local function files_exist()
+    for i = 1, LOG_MTN_FILE_COUNT do
+        local path = get_file_path(i)
+        if file_exists(path) then
+            return true
+        end
+    end
+    return false
+end
+
+-- 删除所有日志文件
+local function remove_files()
+    for i = 1, LOG_MTN_FILE_COUNT do
+        local path = get_file_path(i)
+        os.remove(path)
+    end
+end
+
+-- 创建空文件
+local function create_files()
+    for i = 1, LOG_MTN_FILE_COUNT do
+        local path = get_file_path(i)
+        local file = io.open(path, "wb")
+        if file then
+            file:close()
+        end
+    end
+end
+
+-- 读取配置文件
+local function load_config()
+    local file = io.open(LOG_MTN_CONFIG_FILE, "rb")
+    if not file then
+        return nil  -- 文件不存在,返回 nil
+    end
+    
+    local content = file:read("*a")
+    file:close()
+    
+    if not content or #content == 0 then
+        return nil  -- 文件为空
+    end
+    
+    -- 解析配置:格式为 "VERSION=1\nINDEX=2\nBLOCKS=10\nWRITE_WAY=0\n"
+    local config = {}
+    for line in content:gmatch("[^\r\n]+") do
+        -- 移除首尾空白字符
+        line = line:match("^%s*(.-)%s*$") or line
+        local key, value = line:match("([^=]+)=(.+)")
+        if key and value then
+            -- 移除 key 和 value 的首尾空白字符
+            key = key:match("^%s*(.-)%s*$") or key
+            value = value:match("^%s*(.-)%s*$") or value
+            local num_value = tonumber(value)
+            if num_value then
+                config[key] = num_value
+            else
+                config[key] = value
+            end
+        end
+    end
+    
+    -- 验证版本号
+    if config.VERSION ~= LOG_MTN_CONFIG_VERSION then
+        return nil  -- 版本不匹配
+    end
+    
+    return config
+end
+
+-- 保存配置文件
+local function save_config(index, blocks, write_way)
+    local content = string.format("VERSION=%d\nINDEX=%d\nBLOCKS=%d\nWRITE_WAY=%d\n", 
+        LOG_MTN_CONFIG_VERSION, index, blocks, write_way)
+    
+    local file = io.open(LOG_MTN_CONFIG_FILE, "wb")
+    if not file then
+        log.warn("exmtn", "无法打开配置文件: " .. LOG_MTN_CONFIG_FILE)
+        return false
+    end
+    
+    local ok = file:write(content)
+    file:close()
+    
+    if not ok then
+        log.warn("exmtn", "写入配置文件失败: " .. LOG_MTN_CONFIG_FILE)
+        return false
+    end
+    
+    return true
+end
+
+-- 更新索引(同时保存完整配置)
+local function update_index(index)
+    return save_config(index, ctx.blocks_per_file, ctx.write_way)
+end
+
+-- 格式化时间戳
+-- 返回格式: [2025-11-05 15:06:49.947][00000027.994]
+local function format_timestamp()
+    -- 获取系统运行时间(毫秒)
+    local ticks_ms = 0
+    if mcu and mcu.ticks then
+        local ticks = mcu.ticks()
+        if ticks then
+            ticks_ms = ticks
+        end
+    end
+    
+    -- 获取当前日期时间
+    local date_time_str = ""
+    local ms = 0
+    
+    if os and os.date then
+        -- 获取当前日期时间字符串: 2025-11-05 15:06:49
+        local dt = os.date("%Y-%m-%d %H:%M:%S")
+        if dt then
+            -- 计算毫秒:使用系统运行时间的毫秒部分
+            -- 如果 RTC 已设置,时间会更准确
+            ms = ticks_ms % 1000
+            date_time_str = string.format("%s.%03d", dt, ms)
+        end
+    end
+    
+    -- 如果无法获取日期时间,使用默认格式
+    if date_time_str == "" then
+        date_time_str = "1970-01-01 00:00:00.000"
+    end
+    
+    -- 计算系统运行时间(秒.毫秒)
+    local uptime_sec = math.floor(ticks_ms / 1000)
+    local uptime_ms = ticks_ms % 1000
+    
+    -- 格式化运行时间部分: 00000027.994(固定宽度,9位整数+3位小数)
+    local uptime_str = string.format("%09d.%03d", uptime_sec, uptime_ms)
+    
+    -- 返回完整时间戳
+    return string.format("[%s][%s]", date_time_str, uptime_str)
+end
+
+-- 格式化调试信息
+local function format_debug_info(level, include_level)
+    local info = debug.getinfo(2, "Sl")
+    if not info or not info.source then
+        return nil
+    end
+    
+    local src = info.source
+    -- 跳过第一个字符(@ 或 =)
+    if src:sub(1, 1) == "@" or src:sub(1, 1) == "=" then
+        src = src:sub(2)
+    end
+    
+    local line = info.currentline or 0
+    if line > 64 * 1024 then
+        line = 0
+    end
+    
+    if include_level and level then
+        return string.format("%s/%s:%d", level, src, line)
+    else
+        return string.format("%s:%d", src, line)
+    end
+end
+
+-- 格式化消息(与 log.info/warn/error 格式一致,但添加时间戳前缀)
+local function format_message(level, tag, ...)
+    local argc = select("#", ...)
+    
+    -- 获取 log.style 配置
+    local log_style = 0
+    if log and log.style then
+        log_style = log.style() or 0
+    end
+    
+    -- 根据级别确定日志标识
+    local level_char = "I"  -- 默认 info
+    if level == "warn" then
+        level_char = "W"
+    elseif level == "error" then
+        level_char = "E"
+    end
+    
+    local msg = ""
+    local dbg_info_with_level = format_debug_info(level_char, true)
+    local dbg_info_only = format_debug_info(nil, false)
+    
+    if log_style == 0 then
+        -- LOG_STYLE_NORMAL: "I/user.tag arg1 arg2 ...\n"
+        msg = string.format("%s/user.%s", level_char, tag)
+        for i = 1, argc do
+            local arg = select(i, ...)
+            msg = msg .. " " .. tostring(arg)
+        end
+    elseif log_style == 1 then
+        -- LOG_STYLE_DEBUG_INFO: "I/file.lua:123 tag arg1 arg2 ...\n"
+        if dbg_info_with_level then
+            msg = dbg_info_with_level
+        else
+            msg = level_char
+        end
+        msg = msg .. " " .. tag
+        for i = 1, argc do
+            local arg = select(i, ...)
+            msg = msg .. " " .. tostring(arg)
+        end
+    else
+        -- LOG_STYLE_FULL: "I/user.tag file.lua:123 arg1 arg2 ...\n"
+        msg = string.format("%s/user.%s", level_char, tag)
+        if dbg_info_only then
+            msg = msg .. " " .. dbg_info_only
+        end
+        for i = 1, argc do
+            local arg = select(i, ...)
+            msg = msg .. " " .. tostring(arg)
+        end
+    end
+    
+    msg = msg .. "\n"
+    
+    -- 添加时间戳前缀
+    local timestamp = format_timestamp()
+    return timestamp .. " " .. msg
+end
+
+-- 刷新缓存到文件
+local function flush_cache()
+    if ctx.cache_used == 0 then
+        return true
+    end
+    
+    local path = get_file_path()
+    local file = io.open(path, "ab")
+    if not file then
+        log.warn("exmtn", "无法打开文件: " .. path)
+        return false
+    end
+    
+    -- file:write 返回 true/false 或 nil,不返回字节数
+    local ok = file:write(ctx.cache)
+    file:close()
+    
+    if not ok then
+        log.warn("exmtn", "写入文件失败: " .. path)
+        return false
+    end
+    
+    reset_cache()
+    return true
+end
+
+-- 直接写入文件(ADD_WRITE 模式)
+local function direct_write(data)
+    local path = get_file_path()
+    local file = io.open(path, "ab")
+    if not file then
+        log.warn("exmtn", "无法打开文件: " .. path)
+        return false
+    end
+    
+    -- file:write 返回 true/false 或 nil,不返回字节数
+    local ok = file:write(data)
+    file:close()
+    
+    if not ok then
+        log.warn("exmtn", "写入文件失败: " .. path)
+        return false
+    end
+    
+    return true
+end
+
+-- 将数据追加到缓存或直接写入
+local function buffer_append(data)
+    if not data or #data == 0 then
+        return true
+    end
+    
+    local len = #data
+    
+    -- ADD_WRITE 模式:直接写入文件
+    if ctx.write_way == exmtn.ADD_WRITE then
+        -- 小数据先缓存,累积到阈值再写入
+        if len < LOG_MTN_ADD_WRITE_THRESHOLD then
+            if ctx.cache_used + len > LOG_MTN_CACHE_SIZE then
+                if not flush_cache() then
+                    return false
+                end
+            end
+            ctx.cache = ctx.cache .. data
+            ctx.cache_used = ctx.cache_used + len
+            -- 如果累积到阈值,立即写入
+            if ctx.cache_used >= LOG_MTN_ADD_WRITE_THRESHOLD then
+                return flush_cache()
+            end
+            return true
+        end
+        -- 大数据直接写入
+        return direct_write(data)
+    end
+    
+    -- CACHE_WRITE 模式:原有逻辑
+    if len > LOG_MTN_CACHE_SIZE then
+        -- 先刷新缓存
+        if not flush_cache() then
+            return false
+        end
+        -- 大数据直接写入
+        return direct_write(data)
+    end
+    
+    -- 检查缓存是否足够
+    if ctx.cache_used + len > LOG_MTN_CACHE_SIZE then
+        if not flush_cache() then
+            return false
+        end
+    end
+    
+    ctx.cache = ctx.cache .. data
+    ctx.cache_used = ctx.cache_used + len
+    return true
+end
+
+-- 写入日志到文件
+local function write_to_file(msg)
+    if not ctx.enabled then
+        return true  -- 未启用时返回成功,不写入
+    end
+    
+    local len = #msg
+    
+    -- CACHE_WRITE 模式
+    if ctx.write_way == exmtn.CACHE_WRITE then
+        -- 检查文件大小 + 缓存大小 + 当前数据是否会超过限制
+        -- 如果会超过,先刷新缓存
+        if ctx.cache_used > 0 then
+            local file_sz = get_current_file_size()
+            if file_sz + ctx.cache_used + len > ctx.file_limit then
+                -- 先刷新缓存
+                if not flush_cache() then
+                    return false
+                end
+                -- 重新获取文件大小
+                file_sz = get_current_file_size()
+                -- 检查文件是否已满
+                if file_sz >= ctx.file_limit then
+                    -- 文件已满,切换到下一个文件
+                    ctx.cur_index = (ctx.cur_index % LOG_MTN_FILE_COUNT) + 1
+                    local path = get_file_path()
+                    local file = io.open(path, "wb")
+                    if file then
+                        file:close()
+                    end
+                    if not update_index(ctx.cur_index) then
+                        log.warn("exmtn", "更新索引失败")
+                        return false
+                    end
+                    reset_cache()
+                end
+            end
+        else
+            -- 缓存为空,检查文件大小
+            local file_sz = get_current_file_size()
+            if file_sz + len > ctx.file_limit then
+                -- 文件已满,切换到下一个文件
+                ctx.cur_index = (ctx.cur_index % LOG_MTN_FILE_COUNT) + 1
+                local path = get_file_path()
+                local file = io.open(path, "wb")
+                if file then
+                    file:close()
+                end
+                if not update_index(ctx.cur_index) then
+                    log.warn("exmtn", "更新索引失败")
+                    return false
+                end
+                reset_cache()
+            end
+        end
+        
+        -- 如果加入这条数据后缓存会满,先刷新缓存
+        if ctx.cache_used + len > LOG_MTN_CACHE_SIZE then
+            if not flush_cache() then
+                return false
+            end
+            
+            -- 刷新后再次检查文件大小
+            local file_sz = get_current_file_size()
+            if file_sz >= ctx.file_limit then
+                -- 文件已满,切换到下一个文件
+                ctx.cur_index = (ctx.cur_index % LOG_MTN_FILE_COUNT) + 1
+                local path = get_file_path()
+                local file = io.open(path, "wb")
+                if file then
+                    file:close()
+                end
+                if not update_index(ctx.cur_index) then
+                    log.warn("exmtn", "更新索引失败")
+                    return false
+                end
+                reset_cache()
+            end
+        end
+        
+        -- 加入缓存
+        return buffer_append(msg)
+    else
+        -- ADD_WRITE 模式:先刷新缓存,确保文件大小准确
+        if ctx.cache_used > 0 then
+            if not flush_cache() then
+                return false
+            end
+        end
+        
+        -- 获取当前文件大小
+        local file_sz = get_current_file_size()
+        
+        -- 检查当前文件是否已写满
+        if file_sz >= ctx.file_limit then
+            -- 文件已满,切换到下一个文件
+            ctx.cur_index = (ctx.cur_index % LOG_MTN_FILE_COUNT) + 1
+            local path = get_file_path()
+            local file = io.open(path, "wb")
+            if file then
+                file:close()
+            end
+            if not update_index(ctx.cur_index) then
+                log.warn("exmtn", "更新索引失败")
+                return false
+            end
+            reset_cache()
+        end
+        
+        -- 检查当前数据是否能放入当前文件
+        if file_sz + len > ctx.file_limit then
+            -- 当前数据放不下,切换到下一个文件
+            ctx.cur_index = (ctx.cur_index % LOG_MTN_FILE_COUNT) + 1
+            local path = get_file_path()
+            local file = io.open(path, "wb")
+            if file then
+                file:close()
+            end
+            if not update_index(ctx.cur_index) then
+                log.warn("exmtn", "更新索引失败")
+                return false
+            end
+            reset_cache()
+        end
+        
+        -- 加入缓存或直接写入(buffer_append 会根据大小决定)
+        return buffer_append(msg)
+    end
+end
+
+--[[
+初始化运维日志
+@api exmtn.init(blocks, write_way)
+@int blocks 每个文件的块数,0表示禁用,正整数表示块数量
+@int write_way 写入方式,可选参数。exmtn.CACHE_WRITE(0)表示缓存写入,exmtn.ADD_WRITE(1)表示直接追加写入,默认为exmtn.CACHE_WRITE
+@return boolean 成功返回true,失败返回false
+@usage
+exmtn.init(1, exmtn.CACHE_WRITE)  -- 初始化,1个块,缓存写入
+]]
+function exmtn.init(blocks, write_way)
+    -- 参数校验
+    if blocks == nil then
+        blocks = 0
+    end
+    blocks = math.floor(blocks)
+    if blocks < 0 then
+        log.warn("exmtn", "无效的块数")
+        return false
+    end
+    
+    write_way = write_way or exmtn.CACHE_WRITE
+    if write_way ~= exmtn.CACHE_WRITE and write_way ~= exmtn.ADD_WRITE then
+        write_way = exmtn.CACHE_WRITE
+    end
+    
+    -- 如果禁用
+    if blocks == 0 then
+        reset_cache()
+        remove_files()
+        ctx.enabled = false
+        ctx.cur_index = 1
+        -- 删除配置文件
+        os.remove(LOG_MTN_CONFIG_FILE)
+        ctx.inited = true
+        return true
+    end
+    
+    -- 读取文件系统信息
+    if not ctx.inited then
+        ctx.block_size = 4096
+        ctx.blocks_per_file = 1
+        
+        -- 尝试获取文件系统信息(需要 fs 模块支持)
+        -- fs.fsstat 返回: success, total_blocks, used_blocks, block_size, fs_type
+        if fs and fs.fsstat then
+            local success, total_blocks, used_blocks, block_size, fs_type = fs.fsstat("/")
+            if success and block_size and block_size > 0 then
+                ctx.block_size = block_size
+                if total_blocks and total_blocks > 0 then
+                    local def_blocks = math.floor(total_blocks / LOG_MTN_DEFAULT_BLOCKS_DIVISOR)
+                    if def_blocks > 0 then
+                        ctx.blocks_per_file = def_blocks
+                    end
+                end
+            end
+        end
+    end
+    
+    -- 读取配置文件(仅在首次初始化时读取)
+    if not ctx.inited then
+        local config = load_config()
+        if config then
+            -- 读取索引
+            if config.INDEX and config.INDEX >= 1 and config.INDEX <= LOG_MTN_FILE_COUNT then
+                ctx.cur_index = config.INDEX
+            end
+            
+            -- 读取块数配置
+            if config.BLOCKS and config.BLOCKS > 0 then
+                ctx.blocks_per_file = config.BLOCKS
+            end
+            
+            -- 读取写入方式配置
+            if config.WRITE_WAY == 0 or config.WRITE_WAY == 1 then
+                ctx.write_way = config.WRITE_WAY
+            end
+
+            log.info("exmtn", "读取索引", ctx.cur_index)
+            log.info("exmtn", "读取块数配置", ctx.blocks_per_file)
+            log.info("exmtn", "读取写入方式配置", ctx.write_way)
+        end
+    end
+    
+    -- 检查配置是否变化
+    -- 如果已初始化,比较当前配置和新配置;如果未初始化,不需要判断(首次初始化总是"变化"的)
+    local config_changed = false
+    if ctx.inited then
+        -- 已初始化:比较当前配置和新传入的配置
+        config_changed = (ctx.blocks_per_file ~= blocks) or (ctx.write_way ~= write_way)
+    end
+    -- 未初始化:config_changed 保持为 false,因为首次初始化不算"变化"
+
+    log.info("exmtn", "配置变化", config_changed)
+    -- 更新配置
+    ctx.blocks_per_file = blocks
+    ctx.write_way = write_way
+    ctx.file_limit = ctx.block_size * ctx.blocks_per_file
+    if ctx.file_limit == 0 then
+        ctx.file_limit = LOG_MTN_CACHE_SIZE
+    end
+    
+    -- 处理文件的三种情况
+    if config_changed then
+        -- 情况1:配置变化,清空文件
+        log.info("exmtn", "配置变化,清空文件")
+        reset_cache()
+        remove_files()
+        create_files()
+        ctx.cur_index = 1
+    elseif files_exist() then
+        -- 情况2:配置没有变化,文件存在,根据配置文件中保存的文件指针继续写
+        log.info("exmtn", "配置未变化,文件存在,继续写入")
+        -- ctx.cur_index 已经从配置文件读取(如果是首次初始化)或保持当前值(如果已初始化),不需要重置
+    else
+        -- 情况3:配置没有变化,文件不存在,创建文件
+        log.info("exmtn", "配置未变化,文件不存在,创建文件")
+        create_files()
+        -- ctx.cur_index 已经从配置文件读取(如果是首次初始化)或保持当前值(如果已初始化),不需要重置
+    end
+    
+    -- 保存配置到文件
+    if not save_config(ctx.cur_index, blocks, write_way) then
+        log.warn("exmtn", "保存配置失败")
+        return false
+    end
+    
+    ctx.enabled = true
+    ctx.inited = true
+    
+    -- 打印初始化信息
+    if blocks > 0 then
+        local total_size = ctx.file_limit * LOG_MTN_FILE_COUNT
+        local file_size_mb = ctx.file_limit / (1024 * 1024)
+        local total_size_mb = total_size / (1024 * 1024)
+        local file_size_kb = ctx.file_limit / 1024
+        local total_size_kb = total_size / 1024
+        
+        if ctx.file_limit >= 1024 * 1024 then
+            log.info("exmtn", string.format("初始化成功: 每个文件 %.2f MB (%d 块 × %d 字节), 总空间 %.2f MB (%d 个文件)", 
+                file_size_mb, ctx.blocks_per_file, ctx.block_size, total_size_mb, LOG_MTN_FILE_COUNT))
+        elseif ctx.file_limit >= 1024 then
+            log.info("exmtn", string.format("初始化成功: 每个文件 %.2f KB (%d 块 × %d 字节), 总空间 %.2f KB (%d 个文件)", 
+                file_size_kb, ctx.blocks_per_file, ctx.block_size, total_size_kb, LOG_MTN_FILE_COUNT))
+        else
+            log.info("exmtn", string.format("初始化成功: 每个文件 %d 字节 (%d 块 × %d 字节), 总空间 %d 字节 (%d 个文件)", 
+                ctx.file_limit, ctx.blocks_per_file, ctx.block_size, total_size, LOG_MTN_FILE_COUNT))
+        end
+    end
+    
+    return true
+end
+
+--[[
+输出运维日志并写入文件
+@api exmtn.log(level, tag, ...)
+@string level 日志级别,必须是 "info", "warn", 或 "error"
+@string tag 日志标识,必须是字符串
+@... 需打印的参数
+@return boolean 成功返回true,失败返回false
+@usage
+exmtn.log("info", "message", 123)
+exmtn.log("warn", "message", 456)
+exmtn.log("error", "message", 789)
+]]
+function exmtn.log(level, tag, ...)
+    if not level or type(level) ~= "string" then
+        log.warn("exmtn", "level 必须是字符串")
+        return false
+    end
+    
+    if not tag or type(tag) ~= "string" then
+        log.warn("exmtn", "tag 必须是字符串")
+        return false
+    end
+    
+    -- 根据级别调用对应的底层函数(会被日志级别过滤)
+    if level == "info" then
+        log.info(tag, ...)
+    elseif level == "warn" then
+        log.warn(tag, ...)
+    elseif level == "error" then
+        log.error(tag, ...)
+    else
+        log.warn("exmtn", "level 必须是 'info', 'warn' 或 'error'")
+        return false
+    end
+    
+    -- 格式化消息(用于文件写入)
+    local msg = format_message(level, tag, ...)
+    if not msg then
+        log.warn("exmtn", "格式化消息失败")
+        return false
+    end
+    
+    -- 写入文件(不受日志级别影响)
+    return write_to_file(msg)
+end
+
+--[[
+获取当前配置
+@api exmtn.get_config()
+@return table|nil 配置信息,失败返回nil
+@usage
+local config = exmtn.get_config()
+if config then
+    log.info("exmtn", "blocks:", config.blocks, "write_way:", config.write_way)
+end
+]]
+function exmtn.get_config()
+    if not ctx.inited then
+        return nil
+    end
+    return {
+        enabled = ctx.enabled,
+        cur_index = ctx.cur_index,
+        block_size = ctx.block_size,
+        blocks_per_file = ctx.blocks_per_file,
+        file_limit = ctx.file_limit,
+        write_way = ctx.write_way,
+    }
+end
+
+--[[
+清除所有运维日志文件
+@api exmtn.clear()
+@return boolean 成功返回true,失败返回false
+@usage
+local ok = exmtn.clear()
+if ok then
+    log.info("exmtn", "日志文件已清除")
+end
+]]
+function exmtn.clear()
+    -- 如果已初始化,先刷新缓存(确保数据不丢失)
+    if ctx.inited and ctx.cache_used > 0 then
+        if not flush_cache() then
+            return false
+        end
+    end
+    
+    -- 删除所有日志文件
+    remove_files()
+    
+    -- 重新创建空文件
+    create_files()
+    
+    -- 重置索引为1
+    ctx.cur_index = 1
+    
+    -- 更新配置文件
+    if not save_config(1, ctx.blocks_per_file, ctx.write_way) then
+        return false
+    end
+    
+    log.info("exmtn", "运维日志文件已清除")
+    return true
+end
+
+return exmtn
+