dot 快速发展的未来正在您所在的城市举行活动。

加入我们参加 Redis 发布会

打开红盒子:Redis Lua 调试器

更新:redis-lua-debugger 不兼容 Redis v3 及更高版本

想象一个红盒子。您将东西放入盒子,然后等待其他东西出来(或不出来)。盒子中出来什么(或不出来什么)完全取决于您告诉盒子您想让它做什么。盒子起源于 Terrah 的月亮,因此它只理解月球语言,这是一种既陌生又类似于您已经习惯的语言。为了让它按照您的指示行动,您需要详细地解释您希望它执行的每件事,否则盒子根本无法工作。当盒子确实工作时,您得到的并不总是您以为会得到的。红盒子有古怪的幽默感,似乎喜欢您越来越沮丧的心情,因为您试图窥视其内部运作来理解出了什么问题。

有时我在尝试编写即使是最简单的 Redis Lua 脚本时也是这种感觉。这并非盒子坏了,一点也不。问题完全在于我的编程技能和我有在代码中到处撒播 bug 的独特倾向。我最近写了一篇关于 调试 Redis Lua 脚本的 5 种方法 的文章,但所有这些方法(除了最后一种)都要求您在代码中反复添加和删除调试打印信息。因此,我找到了更好的方法,结果就是我们的 Redis Lua 调试器(或简称“rld”),它具有以下特点:

  • 一个完全非交互式调试器
  • 简单且原生安装,仅约 6KB 的有效负载
  • 将输出打印到本地和远程控制台
  • 代码行执行跟踪
  • 最先进的自动监视机制,它报告新变量和值变化
  • 报告函数调用、返回值和参数,并进行实时分析

关于 Redis Lua 调试器,首先要了解的是它是用 Lua 编写的,并在 Redis 中运行。这意味着除了脚本和 Redis 之外,您不需要任何其他东西来使用 rld。要使用 rld,您首先需要通过像运行任何常规脚本一样运行它来将其加载到 Redis 中——这将导致 rld 潜入您的 Redis 的 Lua 环境,直到下一次服务器重启或调用 SCRIPT FLUSH

rld 可以使用 Redis 的日志文件和发布/订阅(分别为上述文章中的方法 1 和 4)来输出。要跟踪它,只需尾随日志文件和/或订阅 `rld` 通道。请注意,在您加载 rld 之后,它将处于休眠状态,直到您的 Lua 脚本明确激活它,并且只要脚本运行或 rld 停止,它就会保持活动状态。

因此,要实际使用 rld 调试您的脚本,您需要通过调用其 `start` 函数来打开它——只需在 Lua 脚本的开头添加以下行:

rld.start()

现在您可以运行您的脚本。它将正常执行(或更确切地说是非正常执行,因为我们毕竟是在讨论调试),但同时 rld 将跟踪它并打印有关其执行的信息。仔细阅读输出,直到您找到 Aha!/#facepalm 时刻。

这是一个示例——考虑以下名为 prog.lua 的 Lua 脚本:

rld.start()

local function isanswer(n)
  local answer = 42
  if n == answer then
    return true
  else
    return false
  end
end

local t = isanswer(ARGV[1])

return ARGV[1] .. " " .. (t and "is" or "isn't") .. " the answer"

现在,加载 rld(如果您还没有加载)并运行脚本——由于我将 42 作为参数传递,我期望它是正确答案:

foo@bar:~$ redis-cli --eval rld.lua
"rld v0.1.0 loaded to Redis"
foo@bar:~$ redis-cli --eval prog.lua , 42
"42 isn't the answer"

哇!等等!刚刚发生了什么?简单,只需查看您的日志:

foo@bar:~$ tail -n 19 /var/log/redis_6379.log 
[891] 31 Dec 06:37:09.001 # [rld] -- rld v0.1.0 loaded to Redis
[891] 31 Dec 06:37:09.492 # [rld] -- rld v0.1.0 started
[891] 31 Dec 06:37:09.492 # [rld] ARGV[1] = '42'
[891] 31 Dec 06:37:09.492 # [rld] at Lua:@user_script (line: 10)
[891] 31 Dec 06:37:09.493 # [rld] at Lua:@user_script (line: 12)
[891] 31 Dec 06:37:09.493 # [rld] new [isanswer] = (function)
[891] 31 Dec 06:37:09.493 # [rld] call to function Lua:isanswer
[891] 31 Dec 06:37:09.493 # [rld]     args [n] = '42'
[891] 31 Dec 06:37:09.493 # [rld]     at Lua:isanswer (line: 4)
[891] 31 Dec 06:37:09.493 # [rld]     at Lua:isanswer (line: 5)
[891] 31 Dec 06:37:09.493 # [rld]     new [answer] = 42
[891] 31 Dec 06:37:09.493 # [rld]     at Lua:isanswer (line: 8)
[891] 31 Dec 06:37:09.493 # [rld] return from function Lua:isanswer
[891] 31 Dec 06:37:09.493 # [rld] at Lua:@user_script (line: 14)
[891] 31 Dec 06:37:09.493 # [rld] new [t] is false
[891] 31 Dec 06:37:09.493 # [rld] return from function Lua:@user_script
[891] 31 Dec 06:37:09.494 # [rld] -- profiler summary:
[891] 31 Dec 06:37:09.494 # [rld] --   Lua:isanswer called x1 times
[891] 31 Dec 06:37:09.494 # [rld] -- rld exited: end of script.

你能说 Halle-Lua 吗?这不是真的 va-Lua-ble 吗?rld 可以从 https://github.com/RedisLabs/redis-lua-debugger 下载。我希望您能像我制作它一样觉得它有用。当然,还有很多可以改进和扩展 rld 的地方,但我认为它已经是一个 MVP 了,所以我现在发布它。至于 prog.lua...好吧,我仍然试图记住我忘记了什么以便修复它。如果您发现了 bug,请拨打电话:555-1234。

有任何问题或反馈?请随时给我发送电子邮件 (itamar@redis.com) 或在 Twitter 上私信我 (@itamarhaber) 任何事情——我很乐意为您服务 🙂