errDump.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. --- 模块功能:系统错误日志管理(强烈建议用户开启此模块的“错误日志上报调试服务器”功能).
  2. -- 错误日志包括四种:
  3. -- 1、系统主任务运行时的错误日志
  4. -- 此类错误会导致软件重启,错误日志保存在/luaerrinfo.txt文件中
  5. -- 2、调用sys.taskInit创建的协程运行过程中的错误日志
  6. -- 此类错误会终止当前协程的运行,但是不会导致软件重启,错误日志保存在/lib_err.txt中
  7. -- 3、调用errDump.appendErr或者sys.restart接口保存的错误日志
  8. -- 此类错误日志保存在/lib_err.txt中
  9. -- 4、调用errDump.setNetworkLog接口打开网络异常日志功能后,会自动保存最近几种网络异常日志
  10. -- 此类异常日志保存在内存中
  11. --
  12. -- 其中2和3保存的错误日志,最多支持5K字节
  13. -- 每次上报错误日志给调试服务器之后,会清空已保存的日志
  14. -- @module errDump
  15. -- @author openLuat
  16. -- @license MIT
  17. -- @copyright openLuat
  18. -- @release 2017.09.26
  19. require"socket"
  20. require"misc"
  21. module(..., package.seeall)
  22. --错误信息文件以及错误信息内容
  23. local LIB_ERR_FILE,libErr,LIB_ERR_MAX_LEN = "/lib_err.txt","",5*1024
  24. local LUA_ERR_FILE,luaErr = "/luaerrinfo.txt",""
  25. local sReporting,sProtocol
  26. local sNetworkLog,stNetworkLog,sNetworkLogFlag = "",{}
  27. -- 初始化LIB_ERR_FILE文件中的错误信息(读取到内存中,并且打印出来)
  28. -- @return nil
  29. -- @usage readTxt.initErr()
  30. local function initErr()
  31. libErr = io.readFile(LIB_ERR_FILE) or ""
  32. if libErr~="" then
  33. log.error("errDump.libErr", libErr)
  34. end
  35. luaErr = io.readFile(LUA_ERR_FILE) or ""
  36. if luaErr~="" then
  37. log.error("errDump.luaErr", luaErr)
  38. end
  39. updateNetworkLog()
  40. end
  41. --- 追加错误信息到LIB_ERR_FILE文件中(文件最多允许存储5K字节的数据)
  42. -- @string s:用户自定义的错误信息,errDump功能模块会对此错误信息做如下处理:
  43. -- 1、重启后会通过Luat下载调试工具输出,在trace中搜索errDump.libErr,可以搜索到错误信息
  44. -- 2、如果用户调用errDump.request接口设置了错误信息要上报的调试服务器地址和端口,则每次重启会自动上报错误信息到调试服务器
  45. -- 3、如果用户调用errDump.request接口设置了定时上报,则定时上报时会上报错误信息到调试服务器
  46. -- 其中第2和第3种情况,上报成功后,会自动清除错误信息
  47. -- @return bool result,true表示成功,false或者nil表示失败
  48. -- @usage errDump.appendErr("net working timeout!")
  49. function appendErr(s)
  50. if s then
  51. s=s.."\r\n"
  52. log.error("errDump.appendErr",s)
  53. if (s:len()+libErr:len())<=LIB_ERR_MAX_LEN then
  54. libErr = libErr..s
  55. return io.writeFile(LIB_ERR_FILE, libErr)
  56. end
  57. end
  58. end
  59. local function reportData()
  60. return _G.PROJECT.."_"..rtos.get_version()..",".._G.VERSION..","..misc.getImei()..","..misc.getSn()..","..luaErr..(luaErr:len()>0 and "\r\n" or "")..libErr..sNetworkLog
  61. end
  62. local function httpPostCbFnc(result,statusCode)
  63. log.info("errDump.httpPostCbFnc",result,statusCode)
  64. sys.publish("ERRDUMP_HTTP_POST",result,statusCode)
  65. end
  66. function clientTask(protocol,addr,period)
  67. sReporting = true
  68. while true do
  69. if not socket.isReady() then sys.waitUntil("IP_READY_IND") end
  70. --log.info("errDump.clientTask","err",luaErr~="" or libErr~="")
  71. if luaErr~="" or libErr~="" or sNetworkLog~="" then
  72. local retryCnt,result,data = 0
  73. while true do
  74. if protocol=="http" then
  75. http.request("POST",addr,nil,nil,reportData(),20000,httpPostCbFnc)
  76. _,result = sys.waitUntil("ERRDUMP_HTTP_POST")
  77. else
  78. local host,port = addr:match("://(.+):(%d+)$")
  79. if not host then log.error("errDump.request invalid host port") return end
  80. local sck = protocol=="udp" and socket.udp() or socket.tcp()
  81. if sck:connect(host,port) then
  82. result = sck:send(reportData())
  83. if result and protocol=="udp" then
  84. result,data = sck:recv(20000)
  85. if result then
  86. result = data=="OK"
  87. end
  88. end
  89. end
  90. sck:close()
  91. end
  92. if result then
  93. libErr = ""
  94. os.remove(LIB_ERR_FILE)
  95. luaErr = ""
  96. os.remove(LUA_ERR_FILE)
  97. sNetworkLog = ""
  98. stNetworkLog = {}
  99. break
  100. else
  101. retryCnt = retryCnt+1
  102. if retryCnt==3 then
  103. break
  104. end
  105. sys.wait(5000)
  106. end
  107. end
  108. end
  109. if period then
  110. --log.info("errDump.clientTask","wait",period)
  111. sys.wait(period)
  112. else
  113. break
  114. end
  115. end
  116. sReporting = false
  117. end
  118. function updateNetworkLog()
  119. if sNetworkLogFlag then
  120. sNetworkLog = ""
  121. for k,v in pairs(stNetworkLog) do
  122. if v and v~="" then
  123. sNetworkLog = sNetworkLog.."\r\n"..k.."@"..v
  124. end
  125. end
  126. end
  127. end
  128. local onceGsmRegistered,onceGprsAttached
  129. --- 配置网络错误日志开关
  130. -- @bool[opt=nil] flag,是否打开网络错误日志开关,true为打开,false或者nil为关闭
  131. -- @usage
  132. -- errDump.setNetworkLog(true)
  133. function setNetworkLog(flag)
  134. sNetworkLogFlag = flag
  135. local procer = flag and sys.subscribe or sys.unsubscribe
  136. if not flag then
  137. sNetworkLog,stNetworkLog = "",{}
  138. end
  139. local function getTimeStr()
  140. local clk = os.date("*t")
  141. return string.format("%02d_%02d:%02d:%02d",clk.day,clk.hour,clk.min,clk.sec)
  142. end
  143. procer("FLYMODE",function(value)
  144. if value then
  145. stNetworkLog["FLYMODE"] = getTimeStr()
  146. updateNetworkLog()
  147. end
  148. end)
  149. procer("SIM_IND",function(value)
  150. if value~="RDY" then
  151. stNetworkLog["SIM_IND"] = getTimeStr()..":"..value
  152. updateNetworkLog()
  153. end
  154. end)
  155. procer("NET_STATE_UNREGISTER",function()
  156. if onceGsmRegistered then
  157. stNetworkLog["NET_STATE_UNREGISTER"] = getTimeStr()
  158. updateNetworkLog()
  159. end
  160. end)
  161. procer("NET_STATE_REGISTERED",function() onceGsmRegistered=true end)
  162. procer("GPRS_ATTACH",function(value)
  163. if value then
  164. onceGprsAttached = true
  165. elseif onceGprsAttached then
  166. stNetworkLog["GPRS_ATTACH"] = getTimeStr()..":0"
  167. updateNetworkLog()
  168. end
  169. end)
  170. procer("LIB_SOCKET_CONNECT_FAIL_IND",function(ssl,prot,addr,port)
  171. stNetworkLog[(ssl and "ssl" or prot).."://"..addr..":"..port] = getTimeStr()..":connect fail"
  172. updateNetworkLog()
  173. end)
  174. procer("LIB_SOCKET_SEND_FAIL_IND",function(ssl,prot,addr,port)
  175. stNetworkLog[(ssl and "ssl" or prot).."://"..addr..":"..port] = getTimeStr()..":send fail"
  176. updateNetworkLog()
  177. end)
  178. procer("PDP_DEACT_IND",function()
  179. stNetworkLog["PDP_DEACT_IND"] = getTimeStr()
  180. updateNetworkLog()
  181. end)
  182. procer("IP_SHUT_IND",function()
  183. stNetworkLog["IP_SHUT_IND"] = getTimeStr()
  184. updateNetworkLog()
  185. end)
  186. end
  187. --- 配置调试服务器地址,启动错误信息上报给调试服务器的功能,上报成功后,会清除错误信息
  188. -- @string addr,调试服务器地址信息,支持http,udp,tcp
  189. -- 1、如果调试服务器使用http协议,终端将采用POST命令,把错误信息上报到addr指定的URL中,addr的格式如下
  190. -- (除protocol和hostname外,其余字段可选;目前的实现不支持hash)
  191. -- |------------------------------------------------------------------------------|
  192. -- | protocol ||| auth | host | path | hash |
  193. -- |----------|||-----------|-----------------|---------------------------|-------|
  194. -- | ||| | hostname | port | pathname | search | |
  195. -- | ||| |----------|------|----------|----------------| |
  196. -- " http :// user:pass @ host.com : 8080 /p/a/t/h ? query=string # hash "
  197. -- | ||| | | | | | |
  198. -- |------------------------------------------------------------------------------|
  199. -- 2、如果调试服务器使用udp协议,终端将错误信息,直接上报给调试服务器,调试服务器收到信息后,要回复大写的OK;addr格式如下:
  200. -- |----------|||----------|------|
  201. -- | protocol ||| hostname | port |
  202. -- | |||----------|------|
  203. -- " udp :// host.com : 8081 |
  204. -- | ||| | |
  205. -- |------------------------------|
  206. -- 3、如果调试服务器使用tcp协议,终端将错误信息,直接上报给调试服务器;addr格式如下:
  207. -- |----------|||----------|------|
  208. -- | protocol ||| hostname | port |
  209. -- | |||----------|------|
  210. -- " tcp :// host.com : 8082 |
  211. -- | ||| | |
  212. -- |------------------------------|
  213. -- @number[opt=600000] period,单位毫秒,定时检查错误信息并上报的间隔
  214. -- @return bool result,成功返回true,失败返回nil
  215. -- @usage
  216. -- errDump.request("http://www.user_server.com/errdump")
  217. -- errDump.request("udp://www.user_server.com:8081")
  218. -- errDump.request("tcp://www.user_server.com:8082")
  219. -- errDump.request("tcp://www.user_server.com:8082",6*3600*1000)
  220. function request(addr,period)
  221. local protocol = addr:match("(%a+)://")
  222. if protocol~="http" and protocol~="udp" and protocol~="tcp" then
  223. log.error("errDump.request invalid protocol",protocol)
  224. return
  225. end
  226. if not sReporting then
  227. sys.taskInit(clientTask,protocol,addr,period or 600000)
  228. end
  229. return true
  230. end
  231. initErr()