--[[ @object-name: logread @object-desc: logread --]] local rpc = require "oui.rpc" local uci = require "uci" local utils = require "oui.utils" local M = {} --[[ @method-type: call @method-name: get_system_log @method-desc: 获取 system 日志 @in number ?lines 设置要获取的日志行数 [若不设置日志行数,则返回符合条件的全部日志] @in string ?module 设置要获取的日志模块名 [具体模块名需到后台查看,不设置模块名则不进行相关过滤] @out string ?log 输出的system日志 [不传参默认返回全部日志,log存在才返回] @out number ?err_code Error code @out string ?err_msg Error message @in-example: {\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"logread\",\"get_system_log\",{\"lines\":1,\"module\":\"oui-httpd\"}],\"id\":1} @out-example: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"log\":\"Mon Feb 15 18:48:34 2021 daemon.debug oui-httpd: (uhttpd.c:461) Listen on: [::]:443 with ssl\n\"}} --]] function M.get_system_log(params) local lines = params.lines local module = params.module local cmd = "logread" if lines and module then cmd = "logread -e " .. module .. " | tail -n " .. lines elseif lines then cmd = "logread | tail -n " .. lines elseif module then cmd = "logread -e " .. module end local log = "" local f = io.popen(cmd) if f then log = f:read("*a") f:close() end return { log = log } end --[[ @method-type: call @method-name: get_kernel_log @method-desc: 获取 kernel 日志 @out string ?log 输出的 kernel 日志 [全部返回] @out number ?err_code Error code @out string ?err_msg Error message @in-example: {\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"logread\",\"get_kernel_log\",{}],\"id\":1} @out-example: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"log\":\"[ 0.000000] Linux version 4.14.221 (glinet@ubuntu) (gcc version 7.5.0 (OpenWrt GCC 7.5.0 r11306-c4a6851c72)) #0 Mon Feb 15 15:22:37 2021\n\"}} --]] function M.get_kernel_log() local log = "" local f = io.popen("dmesg") if f then log = f:read("*a") f:close() end return { log = log } end --[[ @method-type: call @method-name: get_nginx_log @method-desc: 获取 nginx 日志 @in string type 设置要获取的日志类型 [ access | error ] @out string ?log 输出该日志类型的 nginx 日志 [返回最新的100行日志] @out number ?err_code Error code @out string ?err_msg Error message @in-example: {\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"logread\",\"get_nginx_log\",{\"type\":\"access\"}],\"id\":1} @out-example: {\"id\":1,\"jsonrpc\":\"2.0\",\"result\":{\"log\":\"192.168.8.3 - - [08\\/Aug\\/2022:07:02:39 +0000] \"}} --]] function M.get_nginx_log(params) local log = "" local type = params.type if type then local cmd = type == "access" and "cat /var/log/nginx/access.log | tail -n 100" or "cat /var/log/nginx/error.log | tail -n 100" local f = io.popen(cmd) if f then log = f:read("*a") f:close() end end return { log = log } end --[[ @method-type: call @method-name: get_crash_log @method-desc: 获取 crash 日志【崩溃日志】, 不传参默认返回全部日志 @in number ?mode 设置要获取的日志模式 [0 或 1] ( 0 是 选择最新一条日志 | 1 是 选择日志序号, 返回第几条日志,此模式下 log_number参数 必须有值) @in number ?log_number 设置要获取的日志序号 [mode为1模式时, log_number参数 必须有值] @out string ?log 输出 最新崩溃日志 [不传参默认返回全部日志,带参数仅输出指定的那一条日志] @out number ?sum 输出 现在总存的崩溃 总日志条数 @out number ?err_code Error code,-1:输入了错误的日志序号. @out string ?err_msg Error message @in-example: {\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"logread\",\"get_crash_log\",{\"mode\":1,\"log_number\":2}],\"id\":1} @out-example: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"log\":\"Log Entry 2 (at position 1)\n<6>[ 650.605535] br-lan: port 2(wlan0) entered forwarding state\n\n\",\"sum\":3}} --]] function M.get_crash_log(params) local mode = params.mode local log_number = params.log_number local mtd = utils.readfile("/proc/mtd"):match('(mtd%d+): %S+ %S+ "log"') if not mtd then return end local rs_file = assert(io.popen("oopslog /dev/" .. mtd .. " | grep 'Log Entry' | tail -n 1 |awk -F ' ' '{print $3}'")) local latest_str = rs_file:read() local latest_num = tonumber(latest_str) rs_file = assert(io.popen("oopslog /dev/" .. mtd .. " | grep 'Log Entry'| awk -F ' ' '{print $3}' | awk 'END{print NR}'")) local total_str = rs_file:read() local total_num = tonumber(total_str) local cmd = "oopslog /dev/" .. mtd if latest_str then if mode then if mode == 0 then cmd = "oopslog /dev/" .. mtd .. " | sed -n /'Log Entry ".. latest_str .. "'/,/'Log Entry ".. (latest_num + 1) .. "'/p | sed /'Log Entry " .. (latest_num + 1) .. "'/d" elseif mode == 1 then if log_number < latest_num + 1 and log_number > latest_num - total_num then cmd = "oopslog /dev/" .. mtd .. " | sed -n /'Log Entry ".. log_number .. "'/,/'Log Entry " .. (log_number + 1).."'/p | sed /'Log Entry ".. (log_number + 1) .. "'/d" else rs_file:close() return { err_code = -1, err_msg = "log_number error, log_number should be greater than " .. (latest_num - total_num) .." and be less than " .. (latest_num + 1)} end else rs_file:close() return rpc.ERROR_CODE_INVALID_PARAMS end end rs_file = assert(io.popen(cmd)) local log = rs_file:read("*a") rs_file:close() return { log = log } end end --[[ @method-type: call @method-name: remove_crash_log @method-desc: 删除崩溃日志 @out number ?err_code Error code @out string ?err_msg Error message @in-example: {\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"logread\",\"remove_crash_log\",{}],\"id\":1} @out-example: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{}} --]] function M.remove_crash_log() local mtd = utils.readfile("/proc/mtd"):match('(mtd%d+): %S+ %S+ "log"') if mtd then local rs_file = assert(io.popen("oopslog /dev/".. mtd .." | grep 'Log Entry'")) local log_exist = rs_file:read() if log_exist and string.find(log_exist, "Log") then ngx.pipe.spawn({"/sbin/mtd", "erase", "/dev/" .. mtd}):wait() end rs_file:close() end end --[[ @method-type: call @method-name: export_logs @method-desc: 导出日志 @out string file_name 文件名 @out string file_path 文件路径 @out number ?err_code Error code @out string ?err_msg Error message @in-example: {\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"logread\",\"export_logs\",{}],\"id\":1} @out-example: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"file_name\":\"logread.tar\",\"file_path\":\"/js/logread.tar\"}} --]] function M.export_logs() ngx.pipe.spawn({"/usr/bin/export_logs"}):wait() return { file_name = "logread.tar", file_path = "/js/logread.tar"} end --[[ @method-type: call @method-name: set_config @method-desc: 配置日志 [record_size 参数是设置崩溃日志功能,enable 和path 参数是设置本地日志保存功能,这里是两种情况共用一个接口,根据需要传参] @in number ?record_size 单次崩溃日志大小 [默认8192,最小4096,该参数为4096的整数倍] @in bool enable 启用或禁用openwrt本地日志保存 @in string ?path openwrt本地日志保存路径[不传默认路径 /usr/share/mylog] @out number ?err_code Error code @out string ?err_msg Error message @in-example: {\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"logread\",\"set_config\",{\"record_size\":4096,\"enable\":true,\"path\":\"/usr/share/mylog\"}],\"id\":1} @out-example: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":null} --]] function M.set_config(params) local c = uci.cursor() local enable = params.enable local record_size = params.record_size local path = params.path or "/usr/share/mylog" if record_size then c:set("gl_logread", "crash", "record_size", record_size) c:commit("gl_logread") os.execute("rmmod mtdoops") os.execute("insmod /lib/modules/4.14.221/mtdoops.ko record_size=" .. record_size .. " mtddev=log") end if type(enable) == "boolean" then if enable == true then c:set("system", "@system[0]", "log_file", path) c:set("system", "@system[0]", "log_remote", "0") c:commit("system") else c:delete("system", "@system[0]","log_file") c:delete("system", "@system[0]","log_remote") c:commit("system") end ngx.pipe.spawn({"/etc/init.d/system", "restart"}):wait() ngx.pipe.spawn({"/etc/init.d/log", "restart"}):wait() end end --[[ @method-type: call @method-name: get_config @method-desc: 读取日志配置 [record_size 参数是设置崩溃日志功能,enable 和path 参数是设置本地日志保存功能, 这里是两种情况共用一个接口] @in number ?record_size 单次崩溃日志大小 [默认8192,最小4096,该参数为4096的整数倍] @out bool enable 启用或禁用openwrt本地日志保存 @out string ?path openwrt本地日志保存路径 @out number ?err_code Error code @out string ?err_msg Error message @in-example: {\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"logread\",\"get_config\",{}],\"id\":1} @out-example: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"record_size\":8192,\"path\":\"/usr/share/mylog\",\"enable\":true}} --]] function M.get_config() local c = uci.cursor() local record_size = c:get("gl_logread", "crash", "record_size") local path = c:get("system", "@system[0]", "log_file") local log_remote = c:get("system", "@system[0]", "log_remote") return {record_size = tonumber(record_size), enable = type(path) == "string" and type(log_remote) == "string", path = path} end return M