跳转至

远程日志errDump调试教程

一、errdump 概述

LuatOS 错误日志上报功能模块名叫:errDump,errDump 对“量产投放市场的设备,远程调试初步定位问题”至关重要, 强烈建议客户一定要使用此功能

原理:errDump 就是将模块运行过程中产生的错误信息或者应用日志通过 TCP/UDP 上报到互联网上的指定服务器,技术人员可以在服务器上查阅日志,协助远程了解设备运行情况,或者故障诊断。

使用合宙云服务器时,迫于服务器压力,只有手动打开 debug 开关(见后面第六章云平台配置有介绍),才有日志上报。

1.1 errDump 有什么用?

errDump 是 LuatOS 系统中的错误日志上报模块,主要用于远程调试与故障诊断。其核心价值体现在:

  • 实时监控设备状态:将设备运行时的错误信息(如系统崩溃、协程异常、网络故障等)通过 TCP/UDP/HTTP 协议上报到指定服务器。
  • 支持量产设备维护:对于已投放市场的设备,无需现场调试即可远程定位问题,显著降低维护成本。
  • 灵活配置:用户可自定义上报周期、服务器地址及日志类型,适应不同场景需求。

二、演示功能概述

本示例将演示 errdump 异常日志上报的功能。下面是 errdump 功能的详细解析:

2.1 errDump 中存储的错误信息及日志类型

目前 errdump 支持 2 类错误日志的储存与上报:

  1. 系统任务报错

脚本报错属于系统异常日志,程序报错时会自动写入,如果使用自动上报到 iot 平台则在下次重启会自动读取并上报,如果是自行读取上报的话可通过 errDump.dump(buff,errDump.TYPE_SYS, ture)** ** 来读取系统异常日志并根据自己需求自定义传输。

  1. 用户自行写入

用户自行写入异常日志是通过 errDump.record() 接口来保存的异常日志,如果设置了定时上报周期的话则会定期上报到服务器中,如果自行读取上报的话可以通过 errDump.dump(nil, errDump.TYPE_SYS, true) 来读取用户异常日志并根据自己需求自定义传输。

2.2 存储区域与空间管理

日志文件都是储存在文件系统中。

系统异常日志文件和用户异常日志文件最大都是 4KB。

当存储空间不足时,新日志会覆盖最旧的数据。用户可通过 errDump.dump() 手动读取并清理日志,避免自动覆盖。

2.3 日志上报与读取方式

自动上报

通过 errDump.config(enable, period, user_flag, custom_id, host, port) 配置服务器地址和周期(默认 600 秒)。上报成功后,本地日志自动清空。

--每小时上报到一次
errDump.config(true, 3600, nil, nil, "dev_msg1.openluat.com", 12425)

手动读取

使用 errDump.dump(zbuff, type, isDelete) 读取日志,支持指定类型(如系统日志、用户日志)并选择是否删除。

-- 读取系统日志并保留副本
local log_data = errDump.dump(nil, errDump.TYPE_SYS, false)
-- 读取用户日志后立即删除
local result = errDump.dump(nil, errDump.TYPE_USR, true)

注意:errDump.dump 接口返回值 true 表示本次读取前并没有写入数据,false 反之,在删除日志前,最好再读一下确保没有新的数据写入了

自定义传输

禁用自动上报后,用户可通过 errDump.dump() 获取日志内容,再通过 MQTT、HTTP API 等方式发送到自有服务器。

2.4 日志清除机制

自动清除:

设置自动模式的话,上报到服务器成功后,本地日志自动删除。

手动清除:

设置手动读取模组,调用 errDump.dump(nil, type, true) 指定日志类型并删除。

覆盖策略:

系统/用户日志达到 4KB 时,新写入内容覆盖旧数据。

三、准备硬件环境

3.1 Air8101 开发板底板

淘宝购买链接:Air8101 开发板淘宝购买链接

3.2 USB 转串口供电下载扩展板

参考:硬件环境清单,准备以及组装好硬件环境。

四、软件环境

在详细阐述本功能示例之前,我们需先精心筹备好以下软件环境。

1. Luatools 工具

2. 内核固件文件(底层 core 固件文件):固件和 Demo - luatos@air8101 - 合宙模组资料中心

3. LuatOS 需要的脚本和资源文件:https://gitee.com/openLuat/LuatOS-Air8101/tree/master/demo/errdump

4. lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;

5. 合宙 IOT 平台账号

准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 开发板 - luatos@air8101 - 合宙文档中心,将本篇文章中演示使用的项目文件烧录到 Air8101 开发板中。

五、errdump 软件参考

https://docs.openluat.com/air8101/luatos/api/core/errDump/

六、代码示例介绍

6.1 代码实现

使用 Air8101 开发板来演示 errdump 日志上报功能:

在此 demo 中除了 main.lua 外还有三个不同使用场景的 demo 脚本,演示使用 iot 平台的自动上传 errdump_test.lua,手动读取通过串口上传的 errdump_uart.lua,手动读取通过 tcp 上传的 errdump_tcp.lua

main.lua
--[[
必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
PROJECT:项目名,ascii string类型
        可以随便定义,只要不使用,就行
VERSION:项目版本号,ascii string类型
        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可

本demo演示的功能为:
使用Air8101开发板来演示errdumpy异常日志上报功能
]]
PROJECT = "errdump_demo"
VERSION = "001.000.000"

-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)

-- 加载必须用到的sys功能模块
-- sys功能模块是LuatOS运行框架的基础模块
_G.sys = require("sys")

-- 加载大部分情况下会用到的sysplus功能模块
-- sysplus功能模块是sys功能模块的强力补充
-- 如果项目脚本中用到了sysplus的接口,则打开此处的注释
-- _G.sysplus = require("sysplus")

-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
if wdt then
    --配置喂狗超时时间为9秒钟
    wdt.init(9000)
    --启动一个循环定时器,每隔3秒钟喂一次狗
    sys.timerLoopStart(wdt.feed, 3000)
end

-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 远程升级的详细用法,可以参考fota的demo进行使用
PRODUCT_KEY = "XPwaCKtrZywvcwTMvvdGUTtuiP6zLRTY" --换成自己的

-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
sys.timerLoopStart(function()
    log.info("mem.lua", rtos.meminfo())
    log.info("mem.sys", rtos.meminfo("sys"))
 end, 10000)

 -- 加载errdump测试模块
-- require "errdump_test"  --自动上报异常日志到iot平台
-- require "errdump_uart"  --手动读取异常日志并通过串口传输
require "errdump_tcp"  --手动读取异常日志并通过tcp协议传输

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
errdump_test.lua
--[[
本功能模块演示的内容为:
使用Air8101开发板来演示errdump日志上报功能
使用自动上报异常日志到iot平台
]]
-- 统一联网函数
sys.taskInit(function()
    sys.wait(1000)
    -----------------------------
    ---------wifi 联网-----------
    -----------------------------
    if wlan and wlan.connect then
        -- wifi 联网, Air8101系列均支持
        local ssid = "kfyy123"
        local password = "kfyy123456"
        log.info("wifi", ssid, password)
        wlan.init()
        wlan.setMode(wlan.STATION)
        wlan.connect(ssid, password, 1)
        local result, data = sys.waitUntil("IP_READY")
        log.info("wlan", "IP_READY", result, data)
        device_id = wlan.getMac()
    end
    log.info("已联网")
    sys.publish("net_ready")
end)

local function test_user_log()
    sys.waitUntil("net_ready") --等待网络连接成功
    -- 下面演示自动发送异常日志到iot平台
    errDump.config(true, 600, "user_id") -- 默认是关闭,用这个可以额外添加用户标识,比如用户自定义的ID之类
    while true do
        sys.wait(15000)
        errDump.record("测试一下用户的记录功能")
    end
end

local function test_error_log() --故意写错用来触发系统异常日志记录
    sys.wait(60000)
    lllllllllog.info("测试一下用户的记录功能") --默认写错代码死机
end

sys.taskInit(test_user_log) -- 启动errdemp测试任务
sys.taskInit(test_error_log)--启动错误函数任务
errdump_uart.lua
--[[
本功能模块演示的内容为:
使用Air8101开发板来演示errdump日志上报功能
使用手动读取异常日志并通过串口传出
]]

local function test_user_log()
    -- 下面演示手动获取异常日志信息,手动读取到异常日志可以上报到自己服务器,或者输出到串口。下面演示通过串口输出系统异常日志和用户记录日志数据,
    errDump.config(true, 0,nil,nil) --配置为手动读取,如果配置为自动上报将无法手动读取系统异常日志
    -- --初始化串口,用于输出读取到的错误日志
    local uartid = 1      -- 根据实际设备选取不同的uartid
    uart.setup(
        uartid,           --串口id
        115200,           --波特率
        8,                --数据位
        1                 --停止位
    )
    local buff = zbuff.create(4096)
    local new_flag = errDump.dump(buff, errDump.TYPE_SYS) -- 开机手动读取一次异常日志
    if buff:used() > 0 then
        log.info("errBuff",buff:toStr(0, buff:used())) -- 打印出异常日志
        uart.write(uartid, buff:toStr(0, buff:used()))
        --这里读取到的系统异常日志通过串口输出
    end
    new_flag = errDump.dump(buff, errDump.TYPE_SYS)
    if not new_flag then
        log.info("没有新数据了,删除系统错误日志")
        errDump.dump(nil, errDump.TYPE_SYS, true)
    end
    while true do
        sys.wait(15000)
        errDump.record("测试一下用户的记录功能") --写入用户的日志,注意最大只有4KB,超过部分新的覆盖旧的
        local new_flag = errDump.dump(buff, errDump.TYPE_USR)
        if new_flag then
            log.info("errBuff", buff:toStr(0, buff:used()))
            uart.write(uartid, buff:toStr(0, buff:used()))
            --这里读取到的用户写入日志通过串口输出
        end
        new_flag = errDump.dump(buff, errDump.TYPE_USR)
        if not new_flag then
            log.info("没有新数据了,删除用户错误日志")
            errDump.dump(nil, errDump.TYPE_USR, true)
        end
    end
end

local function test_error_log() --故意写错用来触发系统异常日志记录
    sys.wait(60000)
    lllllllllog.info("测试一下用户的记录功能") --默认写错代码死机
end

sys.taskInit(test_user_log) -- 启动errdemp测试任务
sys.taskInit(test_error_log)--启动错误函数任务
errdump_tcp.lua
--[[
本功能模块演示的内容为:
使用Air8101开发板来演示errdump日志上报功能
手动读取异常日志上传到自己平台
]]
local taskName = "TCP_TASK"
local libnet = require "libnet"         -- libnet库,支持tcp、udp协议所用的同步阻塞接口
local ip = "112.125.89.8"               -- 连接tcp服务器的ip地址
local port = 47772                -- 连接tcp服务器的端口
local netCB = nil                       -- socket服务的回调函数
local connect_state = false             -- 连接状态 true:已连接   false:未连接
local protocol = false                  -- 通讯协议 true:UDP协议  false:TCP协议
local ssl = false                       -- 加密传输 true:加密     false:不加密
local tx_buff = zbuff.create(1024)      -- 发送至tcp服务器的数据
local rx_buff = zbuff.create(1024)      -- 从tcp服务器接收到的数据
-- 统一联网函数
sys.taskInit(function()
    sys.wait(1000)
    -----------------------------
    ---------wifi 联网-----------
    -----------------------------
    if wlan and wlan.connect then
        -- wifi 联网, Air8101系列均支持
        local ssid = "kfyy123"
        local password = "kfyy123456"
        log.info("wifi", ssid, password)
        wlan.init()
        wlan.setMode(wlan.STATION)
        wlan.connect(ssid, password, 1)
        local result, data = sys.waitUntil("IP_READY")
        log.info("wlan", "IP_READY", result, data)
        device_id = wlan.getMac()
    end
    log.info("已联网")
    sys.publish("net_ready")
end)

function TCP_TASK()
    -- 打印一下连接的目标ip和端口号
    log.info("connect ip: ", ip, "port:", port)
    sys.waitUntil("IP_READY")                -- 等待联网成功
    netCB = socket.create(nil, taskName)     -- 创建socket对象
    socket.debug(netCB, true)                -- 打开调试日志
    socket.config(netCB, nil, protocol, ssl) -- 此配置为TCP连接,无SSL加密
    while true do
        -- 连接服务器,返回是否连接成功
        result = libnet.connect(taskName, 15000, netCB, ip, port)
        -- 如果连接成功,则改变连接状态参数,并且随便发一条数据到服务器,看服务器能不能收到
        if result then
            connect_state = true
            libnet.tx(taskName, 0, netCB, "TCP  CONNECT")
        end
        while result do
            succ, param, _, _ = socket.rx(netCB, rx_buff) -- 接收数据
            if not succ then
                log.info("服务器断开了", succ, param, ip, port)
                break
            end
            if rx_buff:used() > 0 then
                log.info("收到服务器数据,长度", rx_buff:used())
                rx_buff:del()
            end
            if tx_buff:used() > 0 then
                log.info("发送到服务器数据,长度", tx_buff:used())
                local result = libnet.tx(taskName, 0, netCB, tx_buff) -- 发送数据
                if not result then
                    log.info("发送失败了", result, param)
                    break
                end
            end
            tx_buff:del()

            if tx_buff:len() > 1024 then
                tx_buff:resize(1024)
            end
            if rx_buff:len() > 1024 then
                rx_buff:resize(1024)
            end
            result, param = libnet.wait(taskName, 15000, netCB)
            if not result then
                log.info("服务器断开了", result, param)
                break
            end
        end
        -- 服务器断开后的行动,由于while true的影响,所以会再次重新执行进行 重新连接。
        connect_state = false
        libnet.close(d1Name, 5000, netCB)
        tx_buff:clear(0)
        rx_buff:clear(0)
        sys.wait(1000)
    end
end

local function test_user_log()
    -- 下面演示手动获取异常日志信息,手动读取到异常日志可以上报到自己服务器
    errDump.config(true, 0) --配置为手动读取,如果配置为自动上报将无法手动读取系统异常日志
    local err_buff = zbuff.create(4096)
    local new_flag = errDump.dump(err_buff, errDump.TYPE_SYS) -- 开机手动读取一次异常日志
    if err_buff:used() > 0 then
        -- log.info(err_buff:toStr(0, err_buff:used())) -- 打印出异常日志
        tx_buff:copy(nil, err_buff)
        err_buff:del()
        if d1Online then    -- 如果已经在线了,则发送socket.EVENT消息来打断任务里的阻塞等待状态,让任务循环继续
            sys_send(d1Name, socket.EVENT, 0)
        end
    end
    new_flag = errDump.dump(err_buff, errDump.TYPE_SYS) --  errDump.dumpf返回值:true表示本次读取前并没有写入数据,false反之,在删除日志前,最好再读一下确保没有新的数据写入了
    if not new_flag then
        log.info("没有新数据了,删除系统错误日志")
        errDump.dump(nil, errDump.TYPE_SYS, true)
    end
    while true do
        sys.wait(15000)
        errDump.record("测试一下用户的记录功能") --写入用户的日志,注意最大只有4KB,超过部分新的覆盖旧的
        local new_flag = errDump.dump(err_buff, errDump.TYPE_USR)
        if new_flag then
            log.info("errBuff", err_buff:toStr(0, err_buff:used()))
            tx_buff:copy(nil, err_buff)
            err_buff:del()
            if d1Online then    -- 如果已经在线了,则发送socket.EVENT消息来打断任务里的阻塞等待状态,让任务循环继续
                sys_send(d1Name, socket.EVENT, 0)
            end
        end
        new_flag = errDump.dump(err_buff, errDump.TYPE_USR)
        if not new_flag then
            log.info("没有新数据了,删除用户错误日志")
            errDump.dump(nil, errDump.TYPE_USR, true)
        end
    end
end

local function test_error_log() --故意写错用来触发系统异常日志记录
    sys.wait(60000)
    lllllllllog.info("测试一下用户的记录功能") --默认写错代码死机
end

-- libnet库依赖于sysplus,所以只能通过sysplus.taskInitEx创建的任务函数中运行
sysplus.taskInitEx(TCP_TASK, taskName, netCB) --启动tcp task
sys.taskInit(test_user_log) -- 启动errdemp测试任务
sys.taskInit(test_error_log)--启动错误函数任务

6.2 自动上传 iot 平台

6.2.1 云平台配置

合宙云平台:https://iot.openluat.com

6.2.1.1 打开 IOT 平台

客户采购了模块时,如果客户采购人员没有告知这批模块应该放在 IOT 平台的哪个产品下,合宙则会以采购人的手机号为账号,默认密码 888888,为客户创建一个 IOT 账号,用这个账号,可以访问合宙的 IOT 平台,然后会将 8101 的 sta mac 归属到此账号的名下。

6.2.1.2 新建一个项目

6.2.1.3 将你自己建的项目 key 复制到 demo 中

6.2.1.4 打开设备 DEBUG 开关

6.2.2 自动上传 iot 平台功能验证

6.2.2.1 Luatools 日志打印

6.2.2.2 云平台查看错误上报

6.3 手动读取通过串口上传功能验证

手动读取系统异常日志和用户自定义消息,并通过串口输出

6.3.1 Luatools 日志打印

6.3.2 串口打印异常日志

6.4 手动读取通过 tcp 上传功能验证

手动读取系统异常日志和用户自定义消息,并通过 TCP 协议上报到服务器中,本文使用下面这个 TCP 服务器来演示 https://netlab.luatos.com/

6.4.1 启动 TCP 服务器

启动 TCP 服务器并修改 demo 中的 ip 和端口号。

6.4.2 Luatools 日志打印

6.4.3 服务器日志打印

七、总结

本示例介绍了将错误日志上报到合宙云平台的功能。