|
| 1 | +-- Mostly it's a copy-paste from tarantool/tarantool log.lua: |
| 2 | +-- https://github.com/tarantool/tarantool/blob/29654ffe3638e5a218dd32f1788830ff05c1c05c/src/lua/log.lua |
| 3 | +-- |
| 4 | +-- We have three reasons for the copy-paste: |
| 5 | +-- 1. Tarantool has not log.crit() (a function for logging with CRIT level). |
| 6 | +-- 2. Only new versions of Tarantool have Ratelimit type. |
| 7 | +-- 3. We want own copy of Ratelimit in case the implementation in Tarantool |
| 8 | +-- changes. Less pain between Tarantool versions. |
| 9 | +local ffi = require('ffi') |
| 10 | + |
| 11 | +local S_CRIT = ffi.C.S_CRIT |
| 12 | +local S_WARN = ffi.C.S_WARN |
| 13 | + |
| 14 | +local function say(level, fmt, ...) |
| 15 | + if ffi.C.log_level < level then |
| 16 | + -- don't waste cycles on debug.getinfo() |
| 17 | + return |
| 18 | + end |
| 19 | + local type_fmt = type(fmt) |
| 20 | + local format = "%s" |
| 21 | + if select('#', ...) ~= 0 then |
| 22 | + local stat |
| 23 | + stat, fmt = pcall(string.format, fmt, ...) |
| 24 | + if not stat then |
| 25 | + error(fmt, 3) |
| 26 | + end |
| 27 | + elseif type_fmt == 'table' then |
| 28 | + -- An implementation in tarantool/tarantool supports encoding a table in |
| 29 | + -- JSON, but it requires more dependencies from FFI. So we just deleted |
| 30 | + -- it because we don't need such encoding in the module. |
| 31 | + error("table not supported", 3) |
| 32 | + elseif type_fmt ~= 'string' then |
| 33 | + fmt = tostring(fmt) |
| 34 | + end |
| 35 | + |
| 36 | + local debug = require('debug') |
| 37 | + local frame = debug.getinfo(3, "Sl") |
| 38 | + local line, file = 0, 'eval' |
| 39 | + if type(frame) == 'table' then |
| 40 | + line = frame.currentline or 0 |
| 41 | + file = frame.short_src or frame.src or 'eval' |
| 42 | + end |
| 43 | + |
| 44 | + ffi.C._say(level, file, line, nil, format, fmt) |
| 45 | +end |
| 46 | + |
| 47 | +local ratelimit_enabled = true |
| 48 | + |
| 49 | +local function ratelimit_enable() |
| 50 | + ratelimit_enabled = true |
| 51 | +end |
| 52 | + |
| 53 | +local function ratelimit_disable() |
| 54 | + ratelimit_enabled = false |
| 55 | +end |
| 56 | + |
| 57 | +local Ratelimit = { |
| 58 | + interval = 60, |
| 59 | + burst = 10, |
| 60 | + emitted = 0, |
| 61 | + suppressed = 0, |
| 62 | + start = 0, |
| 63 | +} |
| 64 | + |
| 65 | +local function ratelimit_new(object) |
| 66 | + return Ratelimit:new(object) |
| 67 | +end |
| 68 | + |
| 69 | +function Ratelimit:new(object) |
| 70 | + object = object or {} |
| 71 | + setmetatable(object, self) |
| 72 | + self.__index = self |
| 73 | + return object |
| 74 | +end |
| 75 | + |
| 76 | +function Ratelimit:check() |
| 77 | + if not ratelimit_enabled then |
| 78 | + return 0, true |
| 79 | + end |
| 80 | + |
| 81 | + local clock = require('clock') |
| 82 | + local now = clock.monotonic() |
| 83 | + local saved_suppressed = 0 |
| 84 | + if now > self.start + self.interval then |
| 85 | + saved_suppressed = self.suppressed |
| 86 | + self.suppressed = 0 |
| 87 | + self.emitted = 0 |
| 88 | + self.start = now |
| 89 | + end |
| 90 | + |
| 91 | + if self.emitted < self.burst then |
| 92 | + self.emitted = self.emitted + 1 |
| 93 | + return saved_suppressed, true |
| 94 | + end |
| 95 | + self.suppressed = self.suppressed + 1 |
| 96 | + return saved_suppressed, false |
| 97 | +end |
| 98 | + |
| 99 | +function Ratelimit:log_check(lvl) |
| 100 | + local suppressed, ok = self:check() |
| 101 | + if lvl >= S_WARN and suppressed > 0 then |
| 102 | + say(S_WARN, '%d messages suppressed due to rate limiting', suppressed) |
| 103 | + end |
| 104 | + return ok |
| 105 | +end |
| 106 | + |
| 107 | +function Ratelimit:log(lvl, fmt, ...) |
| 108 | + if self:log_check(lvl) then |
| 109 | + say(lvl, fmt, ...) |
| 110 | + end |
| 111 | +end |
| 112 | + |
| 113 | +local function log_ratelimited_closure(lvl) |
| 114 | + return function(self, fmt, ...) |
| 115 | + self:log(lvl, fmt, ...) |
| 116 | + end |
| 117 | +end |
| 118 | + |
| 119 | +Ratelimit.log_crit = log_ratelimited_closure(S_CRIT) |
| 120 | + |
| 121 | +return { |
| 122 | + new = ratelimit_new, |
| 123 | + enable = ratelimit_enable, |
| 124 | + disable = ratelimit_disable, |
| 125 | +} |
0 commit comments