-
Notifications
You must be signed in to change notification settings - Fork 53
Expand file tree
/
Copy pathinit.lua
More file actions
396 lines (344 loc) · 12.4 KB
/
init.lua
File metadata and controls
396 lines (344 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
-- vim: st=4 sts=4 sw=4 et:
--- Main Sentry reporting module.
-- This module contains the core of the reporting logic, it still depends on a
-- network layer to actually send the data to the Sentry server.
--
-- @module raven
-- @copyright 2014-2017 CloudFlare, Inc.
-- @license BSD 3-clause (see LICENSE file)
local util = require 'raven.util'
-- If cjson not avilable, attempt to use 'json' instead.
local _, cjson = pcall(require, 'cjson')
local _, jsonlib = pcall(require, 'json')
local _M = {}
_M._VERSION = util._VERSION
local debug_getinfo = debug.getinfo
local table_insert = table.insert
local unpack = unpack or table.unpack -- luacheck: ignore
local generate_event_id = util.generate_event_id
local iso8601 = util.iso8601
local json_encode = cjson.encode or jsonlib.encode
local catcher_trace_level = 4
--- Table describing main Sentry client settings.
-- @field sender Object used to send message, see `rave.senders.*` modules to
-- find a sender suitable with your application
-- @field level Set the message level (string), defaults to `"error"`
-- @field logger Sets the message logger (string), defaults to `"root"`
-- @field tags Defaults tags for sent messages, defaults to `{}`. Example:
-- `{ "foo"="bar", ... }`
-- @field extra Default extra data sent with messages, defaults to `{}`
-- @table sentry_conf
local raven_mt = { }
raven_mt.__index = raven_mt
-- utility function to deal errors, xpcall and stack traces
-- This metatable is associated with returned error object so the original
-- error message is returned when tostring is invoked on it. This allows to use
-- the error objects for logging purposes as well. This is mostly a workaround
-- of xpcall error handlers having a single result.
local err_mt = {
__tostring = function(self)
return self.message
end,
}
-- return a string detailing the function running at a stack level
local function get_culprit(level)
local culprit
level = level + 1
local info = debug_getinfo(level, "Snl")
if info.name then
culprit = info.name
else
culprit = info.short_src .. ":" .. info.linedefined
end
return culprit
end
local function backtrace(level)
local frames = {}
level = level + 1
while true do
local info = debug_getinfo(level, "Snl")
if not info then
break
end
table_insert(frames, 1, {
filename = info.short_src,
["function"] = info.name,
lineno = info.currentline,
})
level = level + 1
end
return { frames = frames }
end
-- error_catcher: used to catch an error from xpcall and return a correct
-- error message
local function error_catcher(err)
return {
message = err,
culprit = get_culprit(catcher_trace_level),
exception = { {
value = err,
stacktrace = backtrace(catcher_trace_level),
} },
}
end
-- a wrapper around error_catcher that will return something even if
-- error_catcher itself crashes
local function capture_error_handler(err)
local ok, json_exception = pcall(error_catcher, err)
if not ok then
-- when failed, json_exception is error message
util.errlog('failed to run exception catcher: ' .. tostring(json_exception))
-- try to return something anyway (error message with no culprit and
-- no stacktrace
json_exception = {
message = err,
culprit = '???',
exception = { { value=err } },
}
end
return setmetatable(json_exception, err_mt)
end
_M.capture_error_handler = capture_error_handler
--- Create a new Sentry client.
-- It takes a @{sentry_conf} table tune its behavior.
-- @param conf client configuration.
-- @return a new raven instance
-- @usage
-- local raven = require "raven"
-- local rvn = raven.new {
-- sender = require("raven.senders.luasocket").new {
-- dsn = "http://pub:secret@127.0.0.1:8080/sentry/proj-id",
-- },
-- tags = { foo = "bar", abc = "def" },
-- logger = "foo",
-- }
function _M.new(conf)
local obj = {
sender = assert(conf.sender, "sender is required"),
level = conf.level or "error",
logger = conf.logger or "root",
tags = conf.tags or nil,
extra = conf.extra or nil,
}
return setmetatable(obj, raven_mt)
end
--- This method is reponsible to return the `server_name` field.
-- The default implementation just returns `"undefined"`, users are encouraged
-- to override this to something more sensible.
function _M.get_server_name()
return "undefined"
end
--- This table can be used to tune the message reporting.
-- @field tags Tags for the message, they will be coalesced with the ones
-- provided in the @{sentry_conf} table used in the constructor if any. In
-- case of conflict, the message tags have precedence.
--
-- @field extra Extra data for the message. Like tags, data is merged with
-- the extra data passed to the constructor.
--
-- @field trace_level Starting stack level for the report (can be used to skip
-- useless frames. The internal raven frames are automatically skipped, so a
-- level of `1` is means that the direct caller will be reported as culprit.
--
-- @table report_conf
--- A Raven client instance is responsible to collect events and send them
-- using the associated sender object.
-- @type Raven
--- Send an exception to Sentry.
-- See [reference](https://docs.sentry.io/clientdev/interfaces/exception/).
-- Note that the stack trace will be filled automatically.
--
-- Note that the `conf` table will be modified by the function, therefore it
-- is not safe to reuse conf table across calls. Consider passing common
-- attributes to the client constructor instead.
--
-- @function Raven:captureException
-- @param exception a table describing the exception conforming to the Sentry
-- format described in the reference docs
-- @param conf capture configuration table, see @{report_conf}
-- @return On success, return event id. If not success, return nil and
-- an error string.
-- @usage
-- local rvn = ravennew(...)
-- local exception = { { -- beware, exceptions are arrays
-- type = "SyntaxError",
-- value = "Wattttt!",
-- module = "mymodule",
-- -- stacktrace is populated automatically
-- } }
-- local id, err = rvn:captureException(exception,
-- { tags = { foo = "bar", abc = "def" }})
function raven_mt:captureException(exception, conf)
local trace_level
if not conf then
conf = { trace_level = 2 }
elseif not conf.trace_level then
conf.trace_level = 2
else
conf.trace_level = conf.trace_level + 1
end
trace_level = conf.trace_level
exception[1].stacktrace = backtrace(trace_level)
local payload = {
exception = exception,
message = exception[1].value,
culprit = get_culprit(trace_level),
}
-- because whether tail call will or will not appear in the stack back trace
-- is different between PUC-lua or LuaJIT, so just avoid tail call
local id, err = self:send_report(payload, conf)
return id, err
end
--- Send a message to Sentry.
-- See [reference](https://docs.sentry.io/clientdev/interfaces/message/).
--
-- Note that the `conf` table will be modified by the function, therefore it
-- is not safe to reuse conf table across calls. Consider passing common
-- attributes to the client constructor instead.
--
-- @function Raven:captureMessage
-- @param message the message, usually a raw string
-- @param conf capture configuration table, see @{report_conf}
-- @return On success, return event id. If not success, return nil and
-- an error string.
-- @usage
-- local rvn = ravennew(...)
-- local id, err = rvn:captureMessage("simple message",
-- { tags = { foo = "bar", abc = "def" }})
function raven_mt:captureMessage(message, conf)
if not conf then
conf = { trace_level = 2 }
elseif not conf.trace_level then
conf.trace_level = 2
else
conf.trace_level = conf.trace_level + 1
end
local payload = {
message = message,
culprit = get_culprit(conf.trace_level),
}
local id, err = self:send_report(payload, conf)
return id, err
end
--- Alias of @{Raven:captureException}.
-- @function Raven:capture_exception
raven_mt.capture_exception = raven_mt.captureException
--- Ailas of @{Raven:captureMessage}.
-- @function Raven:capture_message
raven_mt.capture_message = raven_mt.captureMessage
local function merge_tables(msg, root)
if not root then
return msg
elseif not msg then
return root
end
-- both table exist, merge root into msg
for k, v in pairs(root) do
msg[k] = msg[k] or v
end
return msg
end
--- Send directly a report to Sentry.
-- This is an internal function, you should not call it directly, use
-- @{Raven:captureException} or @{Raven:captureMessage} instead.
--
-- Note that the `conf` table will be modified by the function, therefore it
-- is not safe to reuse conf table across calls. Consider passing common
-- attributes to the client constructor instead.
--
-- @function Raven:send_report
-- @param json table to be sent. Don't need to fill `event_id`, `timestamp`,
-- `tags` and `level`.
-- @param conf capture configuration table, see @{report_conf}
-- @return On success, return event id. If not success, return nil and an
-- error string.
function raven_mt:send_report(json, conf)
local event_id = generate_event_id()
if not json then
json = self.json
if not json then
return
end
end
json.event_id = event_id
json.timestamp = iso8601()
json.level = self.level
json.platform = "lua"
json.logger = self.logger
if conf then
json.tags = merge_tables(conf.tags, self.tags)
json.extra = merge_tables(conf.extra, self.extra)
if conf.level then
json.level = conf.level
end
else
json.tags = self.tags
json.extra = self.extra
end
json.server_name = _M.get_server_name()
local json_str = json_encode(json)
local ok, err = self.sender:send(json_str)
if not ok then
util.errlog("Failed to send to Sentry: ", err, " ", json_str)
return nil, err
end
return json.event_id
end
-- the above two function used to be exposed as method, but there is no reason
-- to do so as they don't need self at the end.
-- Get culprit using given level
function raven_mt:get_culprit(level) -- luacheck: ignore self
return get_culprit(level)
end
-- catcher: used to catch an error from xpcall.
function raven_mt:catcher(err) -- luacheck: ignore self
return error_catcher(err)
end
--- Call given function and report any errors to Sentry.
-- @function Raven:call
-- @param f function to be called
-- @param ... function's arguments
-- @return the same as @{xpcall}
-- @usage
-- function func(a, b, c)
-- return a * b + c
-- end
-- return rvn:call(func, 1, 'foo', true)
function raven_mt:call(f, ...)
-- When used with ngx_lua, connecting a tcp socket in xpcall error handler
-- will cause a "yield across C-call boundary" error. To avoid this, we
-- move all the network operations outside of the xpcall error handler.
local res = { xpcall(f, capture_error_handler, ...) }
if not res[1] then
self:send_report(res[2])
res[2] = res[2].message -- turn the error object back to its initial form
end
return unpack(res)
end
--- Return a error handler function to be used with @{xpcall}
-- This is a high performance version of the @{call} method, but it demands
-- some more work to be used. It return a function that will turn a Lua error
-- into a Sentry exception table. It is meant to be used as a @{xpcall} error
-- hander.
--
-- The error handler returns that table, the should then be sent with the
-- @{send_report} method when appropriate. This cannot be easily done in the
-- error handler itself as Lua disallow yielding form an error handler.
--
-- It has better performance than @{call} because it avoid creating temporary
-- objects at each invocation.
--
-- Note that the error table generated by the handler has a `__tostring`
-- metamethod returning the original error message for logging purposes.
--
-- @function Raven:gen_capture_err
-- @usage
-- local handler = rvn:gen_capture_err()
-- local ok, err = xpcall(function() error("boom") end, handler)
-- if not ok then
-- rvn:send_report(err, { tags = { "foo"="bar" } })
-- end
function raven_mt:gen_capture_err() -- luacheck: ignore self
return capture_error_handler
end
return _M