大家一般是如何调试openresty的

1,306 views
Skip to first unread message

朱茂海

unread,
Nov 3, 2013, 10:00:05 PM11/3/13
to open...@googlegroups.com
我对lua不太熟悉,每次调试都是print,大家一般是如何调试lua的,有没有更方便的方法,最好能给出事例,或者教程。谢谢!

Yichun Zhang (agentzh)

unread,
Nov 3, 2013, 11:06:47 PM11/3/13
to openresty
Hello!

On Sun, Nov 3, 2013 at 7:00 PM, 朱茂海 wrote:
> 我对lua不太熟悉,每次调试都是print,大家一般是如何调试lua的,有没有更方便的方法,最好能给出事例,或者教程。谢谢!
>

我一直觉得在关键代码路径上添加调试输出语句是效率很高的调试方法。而仔细写出的自动化测试用例有助于缩小调试的代码范围,方便反复试验。这也是为什么我的项目一般都会有比较完整的测试集。

我也会经常在 Lua VM 或者 Lua 调用的 C 函数的级别上,使用 systemtap
这样的工具进行动态追踪,调试一些难于复现的线上问题。我打算未来在 Lua 代码级别上添加类似 C 那样的比较完整的动态追踪支持。

我自己对 gdb 风格的交互式调试方式并不热衷,因为容易迷失上下文,或者干扰代码运行时的正常时序。当然,gdb 在分析 core dump
文件时倒是无价之宝 :)

Best regards,
-agentzh

dualface

unread,
Nov 3, 2013, 11:39:00 PM11/3/13
to open...@googlegroups.com
我们都是用 log 来查找错误信息。然后会写一些测试代码,用于测试服务端的功能接口。

交互式调试几乎没用过,感觉对动态语言来说意义不大。

--

Liao Yu Lei

--
--
邮件来自列表“openresty”,专用于技术讨论!
订阅: 请发空白邮件到 openresty...@googlegroups.com
发言: 请发邮件到 open...@googlegroups.com
退订: 请发邮件至 openresty+...@googlegroups.com

wgm

unread,
Nov 3, 2013, 11:51:10 PM11/3/13
to open...@googlegroups.com
我们现在用openresty开发项目时也是采用在代码中增加专门的日志文件和日志输出,在代码中日志做一个输出级别比如debug,info,error等,后期项目测试和上线后,可以通过调整日志输出级别可以屏蔽调试信息。
我把我们的代码给你参考一下:
调用方法:
until:gcs_log("info","download-->path is NULL")
until:gcs_log("error","download-->path is NULL")
--loglevel 是一个变量,由系统启动是传入的值,它来标识日志级别,系统上线后可以把级别改成error或not。这样就可以不用打日志。
-------save log
function gcs_log(info,text)
--loglevel 是一个变量,由系统启动是传入的值
if loglevel == "not" then 
return nil
end  

local infonum = 0
local loglevelnum = 0
if info == "debug" then
infonum = 5
elseif info == "info" then
infonum = 3
elseif info == "error" then
infonum = 1
end
if loglevel == "debug"  then
loglevelnum = 5
elseif loglevel == "info" then
loglevelnum = 3
elseif loglevel == "error" then
loglevelnum = 1
end

if infonum <= loglevelnum then
local file = io.open(logfile,"a")
local timenum = ngx.now()
local timestr = tostring(timenum)
timestr = string.sub(timestr,11)--取ms
timestr = "["..os.date("%Y-%m-%d %H:%M:%S",timenum)..timestr.."]"
file:write(timestr..":["..info.."]:"..text)
file:write("\r\n")
file:close()
file = nil
end

end
----------------------
不过我们的代码还有几个问题主要是性能的问题,我们每次记录日志都需要打开和关闭一次文件,这样不太好,后期希望把文件打开的句柄放在openresty中,听春哥说过一次,不过不知道怎么实现?
还可以采用ffi,调用C的接口去写文件,这样效果会更好些?
还可以增加一些代码做日志的截断之类的工作。

dualface

unread,
Nov 3, 2013, 11:54:45 PM11/3/13
to open...@googlegroups.com
我们也是类似的做法。不过这样其实还是有性能问题的,比如:

log(log.DEBUG, string.format("client %s connected", cliend_id))

在调用 log() 时,string.format() 就已经执行了。进入 log() 后再根据第一个参数决定是否写日志。

--

Liao Yu Lei

Yichun Zhang (agentzh)

unread,
Nov 4, 2013, 12:06:27 AM11/4/13
to openresty
Hello!

2013/11/3 wgm:
> 不过我们的代码还有几个问题主要是性能的问题,我们每次记录日志都需要打开和关闭一次文件,这样不太好,后期希望把文件打开的句柄放在openresty中,听春哥说过一次,不过不知道怎么实现?
> 还可以采用ffi,调用C的接口去写文件,这样效果会更好些?
> 还可以增加一些代码做日志的截断之类的工作。
>

生产上不应写调试日志。这就类似在生产上不应挂着 gdb 跑进程一样。写日志记文件是绝对的高代价操作。

Regards,
-agentzh

朱茂海

unread,
Nov 4, 2013, 12:08:55 AM11/4/13
to open...@googlegroups.com
多谢各位的回答,我决定封装一个log函数,像春哥说的在关键路径上输出日志,找到了如下函数:
local bit = require "bit"
local ffi = require "ffi"
local C = ffi.C
local bor = bit.bor
ffi.cdef[[
int write(int fd, const char *buf, int nbyte);
int open(const char *path, int access, int mode);
int close(int fd);
]]

local O_RDWR = 0X0002;
local O_CREAT = 0x0040;
local O_APPEND = 0x0400;
local S_IRUSR = 0x0100;
local S_IWUSR = 0x0080;
function write(logfile,msg)
            local logger_fd = C.open(logfile, bor(O_RDWR, O_CREAT, O_APPEND), bor(S_IRUSR,S_IWUSR));
            local c = msg;
            C.write(logger_fd, c, #c);
            C.close(logger_fd)
end

在 2013年11月4日星期一UTC+8上午11时00分05秒,朱茂海写道:

Yichun Zhang (agentzh)

unread,
Nov 4, 2013, 12:16:02 AM11/4/13
to openresty
Hello!

2013/11/3 朱茂海:
> function write(logfile,msg)
> local logger_fd = C.open(logfile, bor(O_RDWR, O_CREAT,
> O_APPEND), bor(S_IRUSR,S_IWUSR));
> local c = msg;
> C.write(logger_fd, c, #c);
> C.close(logger_fd)
> end
>

为什么不直接使用 ngx.log() 或者 print() 写到 nginx 的错误日志文件中去呢?这样你可以同时得到 nginx
自身生成的调试日志作为上下文。

https://github.com/chaoslawful/lua-nginx-module#print
https://github.com/chaoslawful/lua-nginx-module#ngxlog

另外,可以通过 ngx.config.debug 来控制是否调用你的日志函数本身,例如

local dbg = ngx.config.debug

...
if dbg then print("reached here: ", some_var) end

这样在生产环境中,print() 和 ngx.log() 函数也几乎不会引入任何开销,也不会影响当前 Lua 代码路径的 JIT
编译。注意,print() 和 ngx.log() (目前)都是不能被 JIT 编译的。

当然,你也可以在结束调试之后手工用 -- 注释掉 print() 这样的调试输出语句。

Regards,
-agentzh

志育

unread,
Nov 4, 2013, 12:40:11 AM11/4/13
to open...@googlegroups.com
ngx.log可以打出请求的URL等,用起来还是很方便的。不过要是项目希望在日志里屏蔽
password这种URL参数的话就比较尴尬了,打个ERR日志,什么密码都被人看见了。
access日志倒是可以自己去掉password。

-----邮件原件-----
发件人: open...@googlegroups.com [mailto:open...@googlegroups.com] 代表
Yichun Zhang (agentzh)
发送时间: 2013年11月4日 13:16
收件人: openresty
主题: Re: [openresty] Re: 大家一般是如何调试openresty的

Yichun Zhang (agentzh)

unread,
Nov 4, 2013, 1:02:26 AM11/4/13
to openresty
Hello!

2013/11/3 志育:
> ngx.log可以打出请求的URL等,用起来还是很方便的。不过要是项目希望在日志里屏蔽
> password这种URL参数的话就比较尴尬了,打个ERR日志,什么密码都被人看见了。

这里介绍的记 nginx 错误日志的办法主要是针对离线调试(比如在产品开发过程中)。在线调试推荐使用动态追踪技术(例如 systemtap 和 dtrace)。

另外,使用 ngx.log(ngx.DEBUG, "...") 输出调试日志的时候,默认也不会带上 URL,例如

[lua] [string "content_by_lua"]:3: hello, log12343.14159

最后,明文密码也不该直接作为 URL 参数,即使是使用 https.

Regards,
-agentzh

志育

unread,
Nov 4, 2013, 1:52:54 AM11/4/13
to open...@googlegroups.com
THX~,刚试了下,原来DEBUG级别不会打出URL。
我们打日志也不仅是为了调试,而是记录程序运行结果,便于后续分析。
  
-----邮件原件-----
发件人: open...@googlegroups.com [mailto:open...@googlegroups.com] 代表
Yichun Zhang (agentzh)
发送时间: 2013年11月4日 14:02
收件人: openresty
主题: Re: 答复: [openresty] Re: 大家一般是如何调试openresty的

Yichun Zhang (agentzh)

unread,
Nov 4, 2013, 1:59:09 AM11/4/13
to openresty
Hello!

2013/11/3 志育:
> THX~,刚试了下,原来DEBUG级别不会打出URL。
> 我们打日志也不仅是为了调试,而是记录程序运行结果,便于后续分析。
>

在线分析日志推荐使用非阻塞 + 缓存写的方式发送到 syslog-ng 这样的日志服务。例如使用下面这个 lua-resty-logger-socket 库:

https://github.com/cloudflare/lua-resty-logger-socket

Regards,
-agentzh

志育

unread,
Nov 4, 2013, 2:36:18 AM11/4/13
to open...@googlegroups.com
lua-resty-logger-socket这个不错,减少了写日志的性能损坏,赞美~

-----邮件原件-----
发件人: open...@googlegroups.com [mailto:open...@googlegroups.com] 代表
Yichun Zhang (agentzh)
发送时间: 2013年11月4日 14:59
收件人: openresty
主题: Re: 答复: 答复: [openresty] Re: 大家一般是如何调试openresty的

wgm

unread,
Nov 10, 2013, 8:45:38 AM11/10/13
to open...@googlegroups.com
春哥:
你好!
我们在做第三方数据交换,需要记录接收到的数据日志和发送的数据日志,我们需要把交换的记录保存下来,我们考虑用文本文件来保存交换日志,现在需要有两点不知道如何实现?
1.如果在openresy启动时打开日志文件,并把句柄保存在openresty中?避免每次写日志时打开文件和关闭文件。
2.希望能够每个日志保存为1M,对超过1M后的日志就重新再新生成一个文件,并关闭原文件

Yichun Zhang (agentzh)

unread,
Nov 10, 2013, 2:54:38 PM11/10/13
to openresty
Hello!

2013/11/10 wgm:
> 我们在做第三方数据交换,需要记录接收到的数据日志和发送的数据日志,我们需要把交换的记录保存下来,我们考虑用文本文件来保存交换日志,现在需要有两点不知道如何实现?
> 1.如果在openresy启动时打开日志文件,并把句柄保存在openresty中?避免每次写日志时打开文件和关闭文件。
> 2.希望能够每个日志保存为1M,对超过1M后的日志就重新再新生成一个文件,并关闭原文件
>

不推荐在 ngx_lua 中直接写普通文件,因为普通文件的写操作几乎总是会阻塞 nginx worker 进程。推荐使用
lua-resty-logger-socket 库以非阻塞方式把日志写入 syslog-ng 这样的专门的日志服务上去。见

https://github.com/cloudflare/lua-resty-logger-socket
http://www.balabit.com/network-security/syslog-ng

Regards,
-agentzh

wgm

unread,
Nov 10, 2013, 9:53:39 AM11/10/13
to open...@googlegroups.com
春哥:
你好!
我们在做第三方数据交换,需要记录接收到的数据日志和发送的数据日志,我们需要把交换的记录保存下来,我们考虑用文本文件来保存交换日志,现在需要有两点不知道如何实现:
1.如果在openresy启动时打开日志文件,并把句柄保存在openresty中?避免每次写日志时打开文件和关闭文件。
2.希望能够每个日志保存为1M,对超过1M

wgm

unread,
Nov 10, 2013, 7:56:34 PM11/10/13
to open...@googlegroups.com
收到,我试试再新建一个syslog-ng服务,把日志集中收集一下。
Reply all
Reply to author
Forward
0 new messages