Files
BR_YKC/4G/code/lib/errDump.lua
2026-03-31 15:46:04 +08:00

348 lines
14 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--- 模块功能:系统错误日志管理(强烈建议用户开启此模块的“错误日志上报调试服务器”功能).
-- 错误日志包括四种:
-- 1、系统主任务运行时的错误日志
-- 此类错误会导致软件重启,错误日志保存在/luaerrinfo.txt文件中
-- 2、调用sys.taskInit创建的协程运行过程中的错误日志
-- 此类错误会终止当前协程的运行,但是不会导致软件重启,错误日志保存在/lib_err.txt中
-- 3、调用errDump.appendErr或者sys.restart接口保存的错误日志
-- 此类错误日志保存在/lib_err.txt中
-- 4、调用errDump.setNetworkLog接口打开网络异常日志功能后会自动保存最近几种网络异常日志
-- 错误日志保存在/lib_network_err.txt中
-- 5、底层固件的死机信息
--
-- 其中2和3保存的错误日志最多支持5K字节
-- 每次上报错误日志给调试服务器之后,会清空已保存的日志
-- @module errDump
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2017.09.26
require"socket"
require"misc"
module(..., package.seeall)
--错误信息文件以及错误信息内容
local LIB_ERR_FILE,libErr,LIB_ERR_MAX_LEN = "/lib_err.txt","",5*1024
local LUA_ERR_FILE,luaErr = "/luaerrinfo.txt",""
local sReporting,sProtocol,switch
local LIB_NETWORK_ERR_FILE,sNetworkLog,stNetworkLog,sNetworkLogFlag = "/lib_network_err.txt","",{}
local firmwareAssertErr = ""
-- 初始化LIB_ERR_FILE文件中的错误信息(读取到内存中,并且打印出来)
-- @return nil
-- @usage readTxt.initErr()
local function initErr()
libErr = io.readFile(LIB_ERR_FILE) or ""
if libErr~="" then
log.error("errDump.libErr", libErr)
end
luaErr = io.readFile(LUA_ERR_FILE) or ""
if luaErr~="" then
log.error("errDump.luaErr", luaErr)
end
sNetworkLog = io.readFile(LIB_NETWORK_ERR_FILE) or ""
if sNetworkLog~="" then
log.error("errDump.libNetErr", sNetworkLog)
end
if type(rtos.get_fatal_info)=="function" then
firmwareAssertErr = rtos.get_fatal_info() or ""
if firmwareAssertErr~="" then
log.error("errDump.firmwareAssertErr", firmwareAssertErr)
end
end
end
--- 追加错误信息到LIB_ERR_FILE文件中文件最多允许存储5K字节的数据
-- @string s 用户自定义的错误信息errDump功能模块会对此错误信息做如下处理
-- 1、重启后会通过Luat下载调试工具输出在trace中搜索errDump.libErr可以搜索到错误信息
-- 2、如果用户调用errDump.request接口设置了错误信息要上报的调试服务器地址和端口则每次重启会自动上报错误信息到调试服务器
-- 3、如果用户调用errDump.request接口设置了定时上报则定时上报时会上报错误信息到调试服务器
-- 其中第2和第3种情况上报成功后会自动清除错误信息
-- @return bool resulttrue表示成功false或者nil表示失败
-- @usage errDump.appendErr("net working timeout!")
function appendErr(s)
if s then
s=s.."\r\n"
log.error("errDump.appendErr",s)
if (s:len()+libErr:len())<=LIB_ERR_MAX_LEN then
libErr = libErr..s
return io.writeFile(LIB_ERR_FILE, libErr)
end
end
end
local function reportData()
local s = _G.PROJECT.."_"..rtos.get_version()..",".._G.VERSION..","..misc.getImei()..","..misc.getSn()..","
s = s.."\r\npoweron reason:"..rtos.poweron_reason().."\r\n"..luaErr..(luaErr:len()>0 and "\r\n" or "")..libErr..(libErr:len()>0 and "\r\n" or "")..sNetworkLog
s = s..(firmwareAssertErr:len()>0 and "\r\n" or "")..firmwareAssertErr
return s
end
local function httpPostCbFnc(result,statusCode)
log.info("errDump.httpPostCbFnc",result,statusCode)
sys.publish("ERRDUMP_HTTP_POST",result,statusCode)
end
local function checkSwitch(addr)
local first = true
while true do
if not socket.isReady() then sys.waitUntil("IP_READY_IND") end
--log.info("errDump.clientTask","err",luaErr~="" or libErr~="")
local host,port = addr:match("://(.+):(%d+)$")
if not host then log.error("errDump.request invalid host port") return end
local result, data, time
while true do
local sck = socket.udp()
data = string.char(0, 0) .. misc.getImei()
if sck:connect(host, port) then
if sck:send(data) then
result, data = sck:recv(5000)
if result then
data, result = json.decode(data)
if result then
if data.r == 1 then
switch = true
time = tonumber(data.expire_at)
end
end
end
else
switch = false
end
else
switch = false
end
sck:close()
if time then
local clk = time - os.time()
if clk < 7200 then
sys.timerStart(function() switch = false end, clk * 1000)
end
end
break
end
if first then
sys.publish("GET_SWITCH")
first = nil
end
sys.wait(7200000)
end
end
function clientTask(protocol,addr,period,flag)
sReporting = true
if flag then
sys.taskInit(checkSwitch, addr)
sys.waitUntil("GET_SWITCH")
end
while true do
if not socket.isReady() then sys.waitUntil("IP_READY_IND") end
--log.info("errDump.clientTask","err",luaErr~="" or libErr~="")
if luaErr~="" or libErr~="" or sNetworkLog~="" or firmwareAssertErr~="" then
local retryCnt,result,data = 0
while true do
if protocol=="http" or protocol=="https" then
http.request("POST",addr,nil,nil,reportData(),20000,httpPostCbFnc)
_,result = sys.waitUntil("ERRDUMP_HTTP_POST")
else
if flag and not switch then
break
end
local host,port = addr:match("://(.+):(%d+)$")
if not host then log.error("errDump.request invalid host port") return end
local sck = protocol=="udp" and socket.udp() or socket.tcp()
if sck:connect(host,port) then
result = sck:send(reportData())
if result and protocol=="udp" then
result,data = sck:recv(20000)
if result then
if not flag then
result = data=="OK"
else
data, result = json.decode(data)
if result then
result = data.r == 1 and true or false
end
end
end
end
end
sck:close()
end
if result then
libErr = ""
os.remove(LIB_ERR_FILE)
luaErr = ""
os.remove(LUA_ERR_FILE)
sNetworkLog = ""
stNetworkLog = {}
os.remove(LIB_NETWORK_ERR_FILE)
firmwareAssertErr = ""
if type(rtos.remove_fatal_info)=="function" then rtos.remove_fatal_info() end
break
else
if flag then
break
end
retryCnt = retryCnt+1
if retryCnt==3 then
break
end
sys.wait(5000)
end
end
end
if period then
--log.info("errDump.clientTask","wait",period)
sys.wait(period)
else
break
end
end
sReporting = false
end
function updateNetworkLog()
if sNetworkLogFlag then
sNetworkLog = ""
for k,v in pairs(stNetworkLog) do
if v and v~="" then
sNetworkLog = sNetworkLog.."\r\n"..k.."@"..v
end
end
if sNetworkLog~="" then
io.writeFile(LIB_NETWORK_ERR_FILE,sNetworkLog)
end
end
end
local onceGsmRegistered,onceGprsAttached
--- 配置网络错误日志开关
-- @bool[opt=nil] flag 是否打开网络错误日志开关true为打开false或者nil为关闭
-- @usage
-- errDump.setNetworkLog(true)
function setNetworkLog(flag)
sNetworkLogFlag = flag
local procer = flag and sys.subscribe or sys.unsubscribe
if not flag then
sNetworkLog,stNetworkLog = "",{}
end
local function getTimeStr()
local clk = os.date("*t")
return string.format("%02d_%02d:%02d:%02d",clk.day,clk.hour,clk.min,clk.sec)
end
procer("FLYMODE",function(value)
if value then
stNetworkLog["FLYMODE"] = getTimeStr()
updateNetworkLog()
end
end)
procer("SIM_IND",function(value)
if value~="RDY" then
stNetworkLog["SIM_IND"] = getTimeStr()..":"..value
updateNetworkLog()
end
end)
procer("NET_STATE_UNREGISTER",function()
if onceGsmRegistered then
stNetworkLog["NET_STATE_UNREGISTER"] = getTimeStr()
updateNetworkLog()
end
end)
procer("NET_STATE_REGISTERED",function() onceGsmRegistered=true end)
procer("GPRS_ATTACH",function(value)
if value then
onceGprsAttached = true
elseif onceGprsAttached then
stNetworkLog["GPRS_ATTACH"] = getTimeStr()..":0"
updateNetworkLog()
end
end)
procer("LIB_SOCKET_CONNECT_FAIL_IND",function(ssl,prot,addr,port)
stNetworkLog[(ssl and "ssl" or prot).."://"..addr..":"..port] = getTimeStr()..":connect fail"
updateNetworkLog()
end)
procer("LIB_SOCKET_SEND_FAIL_IND",function(ssl,prot,addr,port)
stNetworkLog[(ssl and "ssl" or prot).."://"..addr..":"..port] = getTimeStr()..":send fail"
updateNetworkLog()
end)
procer("LIB_SOCKET_CLOSE_IND",function(ssl,prot,addr,port)
stNetworkLog[(ssl and "ssl" or prot).."://"..addr..":"..port.." closed"] = getTimeStr()
updateNetworkLog()
end)
procer("PDP_DEACT_IND",function()
stNetworkLog["PDP_DEACT_IND"] = getTimeStr()
updateNetworkLog()
end)
procer("IP_SHUT_IND",function()
stNetworkLog["IP_SHUT_IND"] = getTimeStr()
updateNetworkLog()
end)
end
--- 配置调试服务器地址,启动错误信息上报给调试服务器的功能,上报成功后,会清除错误信息
-- @string addr 调试服务器地址信息支持httpudptcp
-- 1、如果调试服务器使用http协议终端将采用POST命令把错误信息上报到addr指定的URL中addr的格式如下
-- (除protocol和hostname外其余字段可选目前的实现不支持hash)
-- |------------------------------------------------------------------------------|
-- | protocol ||| auth | host | path | hash |
-- |----------|||-----------|-----------------|---------------------------|-------|
-- | ||| | hostname | port | pathname | search | |
-- | ||| |----------|------|----------|----------------| |
-- " http(s) :// user:pass @ host.com : 8080 /p/a/t/h ? query=string # hash "
-- | ||| | | | | | |
-- |------------------------------------------------------------------------------|
-- 2、如果调试服务器使用udp协议终端将错误信息直接上报给调试服务器调试服务器收到信息后要回复大写的OKaddr格式如下
-- |----------|||----------|------|
-- | protocol ||| hostname | port |
-- | |||----------|------|
-- " udp :// host.com : 8081 |
-- | ||| | |
-- |------------------------------|
-- 3、如果调试服务器使用tcp协议终端将错误信息直接上报给调试服务器addr格式如下
-- |----------|||----------|------|
-- | protocol ||| hostname | port |
-- | |||----------|------|
-- " tcp :// host.com : 8082 |
-- | ||| | |
-- |------------------------------|
-- @number[opt=600000] period 单位毫秒,定时检查错误信息并上报的间隔
-- @bool flag 当使用合宙调试服务器时此参数填为true使用自定义服务器时此参数可省略
-- @return bool result成功返回true失败返回nil
-- @usage
-- errDump.request("http://www.user_server.com/errdump")
-- errDump.request("udp://www.user_server.com:8081")
-- errDump.request("tcp://www.user_server.com:8082")
-- errDump.request("tcp://www.user_server.com:8082",6*3600*1000)
-- errDump.request("udp://www.hezhou_server.com:8083",6*3600*1000,true)
function request(addr,period,flag)
local protocol = addr:match("(%a+)://")
if protocol~="http" and protocol~="https" and protocol~="udp" and protocol~="tcp" then
log.error("errDump.request invalid protocol",protocol)
return
end
if flag and protocol ~= "udp" then
log.error("errDump.request invalid protocol",protocol)
return
end
if not sReporting then
sys.taskInit(clientTask,protocol,addr,period or 600000, flag)
end
return true
end
initErr()