@boshyxd/rosentry 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/index.d.ts +32 -0
- package/out/init.luau +229 -0
- package/out/internal/AutoCapture.d.ts +13 -0
- package/out/internal/AutoCapture.luau +74 -0
- package/out/internal/Capture.d.ts +17 -0
- package/out/internal/Capture.luau +142 -0
- package/out/internal/Config.d.ts +12 -0
- package/out/internal/Config.luau +116 -0
- package/out/internal/Queue.d.ts +19 -0
- package/out/internal/Queue.luau +120 -0
- package/out/internal/Scope.d.ts +25 -0
- package/out/internal/Scope.luau +155 -0
- package/out/internal/Trace.d.ts +21 -0
- package/out/internal/Trace.luau +299 -0
- package/out/internal/Transport.d.ts +13 -0
- package/out/internal/Transport.luau +140 -0
- package/out/internal/util.d.ts +9 -0
- package/out/internal/util.luau +68 -0
- package/out/types.d.ts +94 -0
- package/out/types.luau +2 -0
- package/package.json +34 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local Queue
|
|
3
|
+
do
|
|
4
|
+
Queue = setmetatable({}, {
|
|
5
|
+
__tostring = function()
|
|
6
|
+
return "Queue"
|
|
7
|
+
end,
|
|
8
|
+
})
|
|
9
|
+
Queue.__index = Queue
|
|
10
|
+
function Queue.new(...)
|
|
11
|
+
local self = setmetatable({}, Queue)
|
|
12
|
+
return self:constructor(...) or self
|
|
13
|
+
end
|
|
14
|
+
function Queue:constructor(config, transport)
|
|
15
|
+
self._config = config
|
|
16
|
+
self._transport = transport
|
|
17
|
+
self._queue = {}
|
|
18
|
+
self._lastFlush = tick()
|
|
19
|
+
self._flushLoopRunning = false
|
|
20
|
+
end
|
|
21
|
+
function Queue:_debug(...)
|
|
22
|
+
local args = { ... }
|
|
23
|
+
if self._config:get("debug") then
|
|
24
|
+
print("[RoSentry:Queue]", unpack(args))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
function Queue:_shouldSample()
|
|
28
|
+
local sampleRate = self._config:get("sampleRate")
|
|
29
|
+
return math.random() <= sampleRate
|
|
30
|
+
end
|
|
31
|
+
function Queue:push(errorData)
|
|
32
|
+
if not self._config:isEnabled() then
|
|
33
|
+
return false
|
|
34
|
+
end
|
|
35
|
+
if not self:_shouldSample() then
|
|
36
|
+
self:_debug("Event dropped due to sampling")
|
|
37
|
+
return false
|
|
38
|
+
end
|
|
39
|
+
local beforeSend = self._config:get("beforeSend")
|
|
40
|
+
if beforeSend then
|
|
41
|
+
local result = beforeSend(errorData)
|
|
42
|
+
if result == nil then
|
|
43
|
+
self:_debug("Event dropped by beforeSend hook")
|
|
44
|
+
return false
|
|
45
|
+
end
|
|
46
|
+
errorData = result
|
|
47
|
+
end
|
|
48
|
+
local __queue = self._queue
|
|
49
|
+
local _errorData = errorData
|
|
50
|
+
table.insert(__queue, _errorData)
|
|
51
|
+
self:_debug("Event queued, queue size:", #self._queue)
|
|
52
|
+
local maxQueueSize = self._config:get("maxQueueSize")
|
|
53
|
+
local flushInterval = self._config:get("flushInterval")
|
|
54
|
+
if #self._queue >= maxQueueSize or (tick() - self._lastFlush) >= flushInterval then
|
|
55
|
+
self:flush()
|
|
56
|
+
end
|
|
57
|
+
return true
|
|
58
|
+
end
|
|
59
|
+
function Queue:flush()
|
|
60
|
+
if #self._queue == 0 then
|
|
61
|
+
return nil
|
|
62
|
+
end
|
|
63
|
+
local maxQueueSize = self._config:get("maxQueueSize")
|
|
64
|
+
local errors = {}
|
|
65
|
+
local count = math.min(#self._queue, maxQueueSize)
|
|
66
|
+
do
|
|
67
|
+
local i = 0
|
|
68
|
+
local _shouldIncrement = false
|
|
69
|
+
while true do
|
|
70
|
+
if _shouldIncrement then
|
|
71
|
+
i += 1
|
|
72
|
+
else
|
|
73
|
+
_shouldIncrement = true
|
|
74
|
+
end
|
|
75
|
+
if not (i < count) then
|
|
76
|
+
break
|
|
77
|
+
end
|
|
78
|
+
local item = table.remove(self._queue, 1)
|
|
79
|
+
if item ~= nil then
|
|
80
|
+
table.insert(errors, item)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
self:_debug("Flushing", #errors, "errors")
|
|
85
|
+
self._transport:sendAsync({
|
|
86
|
+
errors = errors,
|
|
87
|
+
})
|
|
88
|
+
self._lastFlush = tick()
|
|
89
|
+
end
|
|
90
|
+
function Queue:startFlushLoop()
|
|
91
|
+
if self._flushLoopRunning then
|
|
92
|
+
return nil
|
|
93
|
+
end
|
|
94
|
+
self._flushLoopRunning = true
|
|
95
|
+
task.spawn(function()
|
|
96
|
+
while self._flushLoopRunning do
|
|
97
|
+
local flushInterval = self._config:get("flushInterval")
|
|
98
|
+
task.wait(flushInterval)
|
|
99
|
+
if #self._queue > 0 then
|
|
100
|
+
self:flush()
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end)
|
|
104
|
+
self:_debug("Flush loop started")
|
|
105
|
+
end
|
|
106
|
+
function Queue:stopFlushLoop()
|
|
107
|
+
self._flushLoopRunning = false
|
|
108
|
+
self:_debug("Flush loop stopped")
|
|
109
|
+
end
|
|
110
|
+
function Queue:getSize()
|
|
111
|
+
return #self._queue
|
|
112
|
+
end
|
|
113
|
+
function Queue:clear()
|
|
114
|
+
self._queue = {}
|
|
115
|
+
self:_debug("Queue cleared")
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
return {
|
|
119
|
+
Queue = Queue,
|
|
120
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Breadcrumb, UserContext } from "../types";
|
|
2
|
+
import { Config } from "./Config";
|
|
3
|
+
export declare class Scope {
|
|
4
|
+
private _config;
|
|
5
|
+
private _user?;
|
|
6
|
+
private _breadcrumbs;
|
|
7
|
+
private _tags;
|
|
8
|
+
private _extras;
|
|
9
|
+
constructor(config: Config);
|
|
10
|
+
private _debug;
|
|
11
|
+
setUser(userId: number, playerName?: string, data?: Map<string, unknown>): void;
|
|
12
|
+
getUser(): UserContext | undefined;
|
|
13
|
+
clearUser(): void;
|
|
14
|
+
addBreadcrumb(category: string, message: string, data?: Map<string, unknown>): void;
|
|
15
|
+
getBreadcrumbs(): Breadcrumb[];
|
|
16
|
+
clearBreadcrumbs(): void;
|
|
17
|
+
setTag(key: string, value: string): void;
|
|
18
|
+
getTags(): Map<string, string>;
|
|
19
|
+
removeTag(key: string): void;
|
|
20
|
+
setExtra(key: string, value: unknown): void;
|
|
21
|
+
getExtras(): Map<string, unknown>;
|
|
22
|
+
removeExtra(key: string): void;
|
|
23
|
+
clear(): void;
|
|
24
|
+
clone(): Scope;
|
|
25
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local Scope
|
|
3
|
+
do
|
|
4
|
+
Scope = setmetatable({}, {
|
|
5
|
+
__tostring = function()
|
|
6
|
+
return "Scope"
|
|
7
|
+
end,
|
|
8
|
+
})
|
|
9
|
+
Scope.__index = Scope
|
|
10
|
+
function Scope.new(...)
|
|
11
|
+
local self = setmetatable({}, Scope)
|
|
12
|
+
return self:constructor(...) or self
|
|
13
|
+
end
|
|
14
|
+
function Scope:constructor(config)
|
|
15
|
+
self._config = config
|
|
16
|
+
self._breadcrumbs = {}
|
|
17
|
+
self._tags = {}
|
|
18
|
+
self._extras = {}
|
|
19
|
+
end
|
|
20
|
+
function Scope:_debug(...)
|
|
21
|
+
local args = { ... }
|
|
22
|
+
if self._config:get("debug") then
|
|
23
|
+
print("[RoSentry:Scope]", unpack(args))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
function Scope:setUser(userId, playerName, data)
|
|
27
|
+
self._user = {
|
|
28
|
+
id = userId,
|
|
29
|
+
name = playerName,
|
|
30
|
+
data = data or {},
|
|
31
|
+
}
|
|
32
|
+
self:addBreadcrumb("user", "User set", {
|
|
33
|
+
userId = userId,
|
|
34
|
+
playerName = playerName,
|
|
35
|
+
})
|
|
36
|
+
local _self = self
|
|
37
|
+
local _exp = userId
|
|
38
|
+
local _condition = playerName
|
|
39
|
+
if _condition == nil then
|
|
40
|
+
_condition = "(no name)"
|
|
41
|
+
end
|
|
42
|
+
_self:_debug("User set:", _exp, _condition)
|
|
43
|
+
end
|
|
44
|
+
function Scope:getUser()
|
|
45
|
+
return self._user
|
|
46
|
+
end
|
|
47
|
+
function Scope:clearUser()
|
|
48
|
+
self._user = nil
|
|
49
|
+
self:_debug("User cleared")
|
|
50
|
+
end
|
|
51
|
+
function Scope:addBreadcrumb(category, message, data)
|
|
52
|
+
local maxBreadcrumbs = self._config:get("maxBreadcrumbs")
|
|
53
|
+
local breadcrumb = {
|
|
54
|
+
category = category,
|
|
55
|
+
message = message,
|
|
56
|
+
data = data,
|
|
57
|
+
timestamp = os.time(),
|
|
58
|
+
}
|
|
59
|
+
local _exp = self._breadcrumbs
|
|
60
|
+
table.insert(_exp, breadcrumb)
|
|
61
|
+
while #self._breadcrumbs > maxBreadcrumbs do
|
|
62
|
+
table.remove(self._breadcrumbs, 1)
|
|
63
|
+
end
|
|
64
|
+
self:_debug("Breadcrumb added:", category, message)
|
|
65
|
+
end
|
|
66
|
+
function Scope:getBreadcrumbs()
|
|
67
|
+
return self._breadcrumbs
|
|
68
|
+
end
|
|
69
|
+
function Scope:clearBreadcrumbs()
|
|
70
|
+
self._breadcrumbs = {}
|
|
71
|
+
self:_debug("Breadcrumbs cleared")
|
|
72
|
+
end
|
|
73
|
+
function Scope:setTag(key, value)
|
|
74
|
+
local __tags = self._tags
|
|
75
|
+
local _key = key
|
|
76
|
+
local _value = value
|
|
77
|
+
__tags[_key] = _value
|
|
78
|
+
self:_debug("Tag set:", key, value)
|
|
79
|
+
end
|
|
80
|
+
function Scope:getTags()
|
|
81
|
+
return self._tags
|
|
82
|
+
end
|
|
83
|
+
function Scope:removeTag(key)
|
|
84
|
+
local __tags = self._tags
|
|
85
|
+
local _key = key
|
|
86
|
+
__tags[_key] = nil
|
|
87
|
+
end
|
|
88
|
+
function Scope:setExtra(key, value)
|
|
89
|
+
local __extras = self._extras
|
|
90
|
+
local _key = key
|
|
91
|
+
local _value = value
|
|
92
|
+
__extras[_key] = _value
|
|
93
|
+
self:_debug("Extra set:", key)
|
|
94
|
+
end
|
|
95
|
+
function Scope:getExtras()
|
|
96
|
+
return self._extras
|
|
97
|
+
end
|
|
98
|
+
function Scope:removeExtra(key)
|
|
99
|
+
local __extras = self._extras
|
|
100
|
+
local _key = key
|
|
101
|
+
__extras[_key] = nil
|
|
102
|
+
end
|
|
103
|
+
function Scope:clear()
|
|
104
|
+
self._user = nil
|
|
105
|
+
self._breadcrumbs = {}
|
|
106
|
+
self._tags = {}
|
|
107
|
+
self._extras = {}
|
|
108
|
+
self:_debug("Scope cleared")
|
|
109
|
+
end
|
|
110
|
+
function Scope:clone()
|
|
111
|
+
local cloned = Scope.new(self._config)
|
|
112
|
+
if self._user then
|
|
113
|
+
local _object = table.clone(self._user)
|
|
114
|
+
setmetatable(_object, nil)
|
|
115
|
+
cloned._user = _object
|
|
116
|
+
end
|
|
117
|
+
local _array = {}
|
|
118
|
+
local _length = #_array
|
|
119
|
+
local _array_1 = self._breadcrumbs
|
|
120
|
+
table.move(_array_1, 1, #_array_1, _length + 1, _array)
|
|
121
|
+
cloned._breadcrumbs = _array
|
|
122
|
+
local tagsCopy = {}
|
|
123
|
+
local _exp = self._tags
|
|
124
|
+
-- ▼ ReadonlyMap.forEach ▼
|
|
125
|
+
local _callback = function(v, k)
|
|
126
|
+
local _k = k
|
|
127
|
+
local _v = v
|
|
128
|
+
tagsCopy[_k] = _v
|
|
129
|
+
return tagsCopy
|
|
130
|
+
end
|
|
131
|
+
for _k, _v in _exp do
|
|
132
|
+
_callback(_v, _k, _exp)
|
|
133
|
+
end
|
|
134
|
+
-- ▲ ReadonlyMap.forEach ▲
|
|
135
|
+
cloned._tags = tagsCopy
|
|
136
|
+
local extrasCopy = {}
|
|
137
|
+
local _exp_1 = self._extras
|
|
138
|
+
-- ▼ ReadonlyMap.forEach ▼
|
|
139
|
+
local _callback_1 = function(v, k)
|
|
140
|
+
local _k = k
|
|
141
|
+
local _v = v
|
|
142
|
+
extrasCopy[_k] = _v
|
|
143
|
+
return extrasCopy
|
|
144
|
+
end
|
|
145
|
+
for _k, _v in _exp_1 do
|
|
146
|
+
_callback_1(_v, _k, _exp_1)
|
|
147
|
+
end
|
|
148
|
+
-- ▲ ReadonlyMap.forEach ▲
|
|
149
|
+
cloned._extras = extrasCopy
|
|
150
|
+
return cloned
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
return {
|
|
154
|
+
Scope = Scope,
|
|
155
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Trace, TraceOptions, TraceStatus } from "../types";
|
|
2
|
+
import { Config } from "./Config";
|
|
3
|
+
import { Scope } from "./Scope";
|
|
4
|
+
import { Queue } from "./Queue";
|
|
5
|
+
export declare class TraceManager {
|
|
6
|
+
private _config;
|
|
7
|
+
private _scope;
|
|
8
|
+
private _queue;
|
|
9
|
+
private _activeTraces;
|
|
10
|
+
constructor(config: Config, scope: Scope, queue: Queue);
|
|
11
|
+
private _debug;
|
|
12
|
+
private _cleanupExpired;
|
|
13
|
+
getActiveCount(): number;
|
|
14
|
+
startTrace(traceName: string, options?: TraceOptions): Trace | undefined;
|
|
15
|
+
private _createTraceInterface;
|
|
16
|
+
private _logToTrace;
|
|
17
|
+
private _finishTrace;
|
|
18
|
+
getTrace(traceId: string): Trace | undefined;
|
|
19
|
+
endAllTraces(status?: TraceStatus): void;
|
|
20
|
+
setScope(scope: Scope): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local HttpService = TS.import(script, TS.getModule(script, "@rbxts", "services")).HttpService
|
|
4
|
+
local _util = TS.import(script, script.Parent, "util")
|
|
5
|
+
local getJobId = _util.getJobId
|
|
6
|
+
local getPlaceId = _util.getPlaceId
|
|
7
|
+
local getGameVersion = _util.getGameVersion
|
|
8
|
+
local isServer = _util.isServer
|
|
9
|
+
local getTimestamp = _util.getTimestamp
|
|
10
|
+
local DEFAULT_MAX_EVENTS = 100
|
|
11
|
+
local DEFAULT_EXPIRES_IN = 3600
|
|
12
|
+
local MAX_ACTIVE_TRACES = 50
|
|
13
|
+
local function isoFromUnixSeconds(seconds)
|
|
14
|
+
return DateTime.fromUnixTimestamp(seconds):ToIsoDate()
|
|
15
|
+
end
|
|
16
|
+
local TraceManager
|
|
17
|
+
do
|
|
18
|
+
TraceManager = setmetatable({}, {
|
|
19
|
+
__tostring = function()
|
|
20
|
+
return "TraceManager"
|
|
21
|
+
end,
|
|
22
|
+
})
|
|
23
|
+
TraceManager.__index = TraceManager
|
|
24
|
+
function TraceManager.new(...)
|
|
25
|
+
local self = setmetatable({}, TraceManager)
|
|
26
|
+
return self:constructor(...) or self
|
|
27
|
+
end
|
|
28
|
+
function TraceManager:constructor(config, scope, queue)
|
|
29
|
+
self._config = config
|
|
30
|
+
self._scope = scope
|
|
31
|
+
self._queue = queue
|
|
32
|
+
self._activeTraces = {}
|
|
33
|
+
end
|
|
34
|
+
function TraceManager:_debug(...)
|
|
35
|
+
local args = { ... }
|
|
36
|
+
if self._config:get("debug") then
|
|
37
|
+
print("[RoSentry:Trace]", unpack(args))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
function TraceManager:_cleanupExpired()
|
|
41
|
+
local now = os.time()
|
|
42
|
+
local expired = {}
|
|
43
|
+
local _exp = self._activeTraces
|
|
44
|
+
-- ▼ ReadonlyMap.forEach ▼
|
|
45
|
+
local _callback = function(trace, id)
|
|
46
|
+
if now >= trace.expiresAt then
|
|
47
|
+
self:_debug("Trace expired:", trace.name, id)
|
|
48
|
+
local _id = id
|
|
49
|
+
table.insert(expired, _id)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
for _k, _v in _exp do
|
|
53
|
+
_callback(_v, _k, _exp)
|
|
54
|
+
end
|
|
55
|
+
-- ▲ ReadonlyMap.forEach ▲
|
|
56
|
+
for _, id in expired do
|
|
57
|
+
self._activeTraces[id] = nil
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
function TraceManager:getActiveCount()
|
|
61
|
+
self:_cleanupExpired()
|
|
62
|
+
local count = 0
|
|
63
|
+
local _exp = self._activeTraces
|
|
64
|
+
-- ▼ ReadonlyMap.forEach ▼
|
|
65
|
+
local _callback = function()
|
|
66
|
+
count += 1
|
|
67
|
+
end
|
|
68
|
+
for _k, _v in _exp do
|
|
69
|
+
_callback(_v, _k, _exp)
|
|
70
|
+
end
|
|
71
|
+
-- ▲ ReadonlyMap.forEach ▲
|
|
72
|
+
return count
|
|
73
|
+
end
|
|
74
|
+
function TraceManager:startTrace(traceName, options)
|
|
75
|
+
if not self._config:isEnabled() then
|
|
76
|
+
return nil
|
|
77
|
+
end
|
|
78
|
+
self:_cleanupExpired()
|
|
79
|
+
if self:getActiveCount() >= MAX_ACTIVE_TRACES then
|
|
80
|
+
warn(`[RoSentry] Max active traces reached ({MAX_ACTIVE_TRACES}). Cannot start new trace.`)
|
|
81
|
+
return nil
|
|
82
|
+
end
|
|
83
|
+
local opts = options or {}
|
|
84
|
+
local now = os.time()
|
|
85
|
+
local _condition = opts.maxEvents
|
|
86
|
+
if _condition == nil then
|
|
87
|
+
_condition = DEFAULT_MAX_EVENTS
|
|
88
|
+
end
|
|
89
|
+
local maxEvents = _condition
|
|
90
|
+
local _condition_1 = opts.expiresIn
|
|
91
|
+
if _condition_1 == nil then
|
|
92
|
+
_condition_1 = DEFAULT_EXPIRES_IN
|
|
93
|
+
end
|
|
94
|
+
local expiresIn = _condition_1
|
|
95
|
+
maxEvents = math.min(maxEvents, 500)
|
|
96
|
+
expiresIn = math.min(expiresIn, 7200)
|
|
97
|
+
local traceId = HttpService:GenerateGUID(false)
|
|
98
|
+
local instance = {
|
|
99
|
+
id = traceId,
|
|
100
|
+
name = traceName,
|
|
101
|
+
status = "active",
|
|
102
|
+
eventCount = 0,
|
|
103
|
+
maxEvents = maxEvents,
|
|
104
|
+
startedAt = now,
|
|
105
|
+
expiresAt = now + expiresIn,
|
|
106
|
+
tags = opts.tags or {},
|
|
107
|
+
metadata = opts.metadata or {},
|
|
108
|
+
}
|
|
109
|
+
self._activeTraces[traceId] = instance
|
|
110
|
+
self:_debug("Started trace:", traceName, traceId, "maxEvents:", maxEvents, "expiresIn:", expiresIn)
|
|
111
|
+
return self:_createTraceInterface(instance)
|
|
112
|
+
end
|
|
113
|
+
function TraceManager:_createTraceInterface(instance)
|
|
114
|
+
local manager = self
|
|
115
|
+
local traceId = instance.id
|
|
116
|
+
local trace
|
|
117
|
+
trace = {
|
|
118
|
+
id = traceId,
|
|
119
|
+
name = instance.name,
|
|
120
|
+
log = function(message, data)
|
|
121
|
+
trace.info(message, data)
|
|
122
|
+
end,
|
|
123
|
+
debug = function(message, data)
|
|
124
|
+
manager:_logToTrace(traceId, "debug", message, data)
|
|
125
|
+
end,
|
|
126
|
+
info = function(message, data)
|
|
127
|
+
manager:_logToTrace(traceId, "info", message, data)
|
|
128
|
+
end,
|
|
129
|
+
warn = function(message, data)
|
|
130
|
+
manager:_logToTrace(traceId, "warn", message, data)
|
|
131
|
+
end,
|
|
132
|
+
error = function(message, data)
|
|
133
|
+
manager:_logToTrace(traceId, "error", message, data)
|
|
134
|
+
end,
|
|
135
|
+
isActive = function()
|
|
136
|
+
local inst = manager._activeTraces[traceId]
|
|
137
|
+
if not inst then
|
|
138
|
+
return false
|
|
139
|
+
end
|
|
140
|
+
if inst.status ~= "active" then
|
|
141
|
+
return false
|
|
142
|
+
end
|
|
143
|
+
if os.time() >= inst.expiresAt then
|
|
144
|
+
return false
|
|
145
|
+
end
|
|
146
|
+
return true
|
|
147
|
+
end,
|
|
148
|
+
finish = function(status)
|
|
149
|
+
manager:_finishTrace(traceId, status or "completed")
|
|
150
|
+
end,
|
|
151
|
+
}
|
|
152
|
+
return trace
|
|
153
|
+
end
|
|
154
|
+
function TraceManager:_logToTrace(traceId, level, message, data)
|
|
155
|
+
local __activeTraces = self._activeTraces
|
|
156
|
+
local _traceId = traceId
|
|
157
|
+
local instance = __activeTraces[_traceId]
|
|
158
|
+
if not instance then
|
|
159
|
+
self:_debug("Trace not found:", traceId)
|
|
160
|
+
return nil
|
|
161
|
+
end
|
|
162
|
+
if instance.status ~= "active" then
|
|
163
|
+
self:_debug("Trace not active:", traceId, instance.status)
|
|
164
|
+
return nil
|
|
165
|
+
end
|
|
166
|
+
if os.time() >= instance.expiresAt then
|
|
167
|
+
self:_debug("Trace expired:", traceId)
|
|
168
|
+
self:_finishTrace(traceId, "timeout")
|
|
169
|
+
return nil
|
|
170
|
+
end
|
|
171
|
+
if instance.eventCount >= instance.maxEvents then
|
|
172
|
+
self:_debug("Trace event limit reached:", traceId, instance.eventCount)
|
|
173
|
+
self:_finishTrace(traceId, "completed")
|
|
174
|
+
return nil
|
|
175
|
+
end
|
|
176
|
+
instance.eventCount += 1
|
|
177
|
+
local user = self._scope:getUser()
|
|
178
|
+
local errorData = {
|
|
179
|
+
message = message,
|
|
180
|
+
level = level,
|
|
181
|
+
stack_trace = nil,
|
|
182
|
+
user_id = if user then user.id else nil,
|
|
183
|
+
player_name = if user then user.name else nil,
|
|
184
|
+
trace_id = traceId,
|
|
185
|
+
job_id = getJobId(),
|
|
186
|
+
place_id = getPlaceId(),
|
|
187
|
+
game_version = getGameVersion(),
|
|
188
|
+
environment = self._config:get("environment"),
|
|
189
|
+
context = {
|
|
190
|
+
userData = if user then user.data else nil,
|
|
191
|
+
custom = data,
|
|
192
|
+
breadcrumbs = nil,
|
|
193
|
+
isServer = isServer(),
|
|
194
|
+
script = nil,
|
|
195
|
+
trace = {
|
|
196
|
+
id = traceId,
|
|
197
|
+
name = instance.name,
|
|
198
|
+
status = instance.status,
|
|
199
|
+
eventCount = instance.eventCount,
|
|
200
|
+
maxEvents = instance.maxEvents,
|
|
201
|
+
startedAt = isoFromUnixSeconds(instance.startedAt),
|
|
202
|
+
expiresAt = isoFromUnixSeconds(instance.expiresAt),
|
|
203
|
+
metadata = instance.metadata,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
tags = instance.tags,
|
|
207
|
+
timestamp = getTimestamp(),
|
|
208
|
+
}
|
|
209
|
+
self._queue:push(errorData)
|
|
210
|
+
self:_debug("Logged to trace:", traceId, level, message, `({instance.eventCount}/{instance.maxEvents})`)
|
|
211
|
+
end
|
|
212
|
+
function TraceManager:_finishTrace(traceId, status)
|
|
213
|
+
local __activeTraces = self._activeTraces
|
|
214
|
+
local _traceId = traceId
|
|
215
|
+
local instance = __activeTraces[_traceId]
|
|
216
|
+
if not instance then
|
|
217
|
+
return nil
|
|
218
|
+
end
|
|
219
|
+
instance.status = status
|
|
220
|
+
self:_debug("Finished trace:", instance.name, traceId, "status:", status, "events:", instance.eventCount)
|
|
221
|
+
local user = self._scope:getUser()
|
|
222
|
+
local finishEvent = {
|
|
223
|
+
message = "trace:finish",
|
|
224
|
+
level = "debug",
|
|
225
|
+
stack_trace = nil,
|
|
226
|
+
user_id = if user then user.id else nil,
|
|
227
|
+
player_name = if user then user.name else nil,
|
|
228
|
+
trace_id = traceId,
|
|
229
|
+
job_id = getJobId(),
|
|
230
|
+
place_id = getPlaceId(),
|
|
231
|
+
game_version = getGameVersion(),
|
|
232
|
+
environment = self._config:get("environment"),
|
|
233
|
+
context = {
|
|
234
|
+
userData = if user then user.data else nil,
|
|
235
|
+
custom = {
|
|
236
|
+
__rosentry = {
|
|
237
|
+
kind = "trace_finish",
|
|
238
|
+
status = status,
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
breadcrumbs = nil,
|
|
242
|
+
isServer = isServer(),
|
|
243
|
+
script = nil,
|
|
244
|
+
trace = {
|
|
245
|
+
id = traceId,
|
|
246
|
+
name = instance.name,
|
|
247
|
+
status = status,
|
|
248
|
+
eventCount = instance.eventCount,
|
|
249
|
+
maxEvents = instance.maxEvents,
|
|
250
|
+
startedAt = isoFromUnixSeconds(instance.startedAt),
|
|
251
|
+
expiresAt = isoFromUnixSeconds(instance.expiresAt),
|
|
252
|
+
endedAt = getTimestamp(),
|
|
253
|
+
metadata = instance.metadata,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
tags = instance.tags,
|
|
257
|
+
timestamp = getTimestamp(),
|
|
258
|
+
}
|
|
259
|
+
self._queue:push(finishEvent)
|
|
260
|
+
local __activeTraces_1 = self._activeTraces
|
|
261
|
+
local _traceId_1 = traceId
|
|
262
|
+
__activeTraces_1[_traceId_1] = nil
|
|
263
|
+
end
|
|
264
|
+
function TraceManager:getTrace(traceId)
|
|
265
|
+
local __activeTraces = self._activeTraces
|
|
266
|
+
local _traceId = traceId
|
|
267
|
+
local instance = __activeTraces[_traceId]
|
|
268
|
+
if not instance then
|
|
269
|
+
return nil
|
|
270
|
+
end
|
|
271
|
+
if instance.status ~= "active" or os.time() >= instance.expiresAt then
|
|
272
|
+
local __activeTraces_1 = self._activeTraces
|
|
273
|
+
local _traceId_1 = traceId
|
|
274
|
+
__activeTraces_1[_traceId_1] = nil
|
|
275
|
+
return nil
|
|
276
|
+
end
|
|
277
|
+
return self:_createTraceInterface(instance)
|
|
278
|
+
end
|
|
279
|
+
function TraceManager:endAllTraces(status)
|
|
280
|
+
local finalStatus = status or "completed"
|
|
281
|
+
local _exp = self._activeTraces
|
|
282
|
+
-- ▼ ReadonlyMap.forEach ▼
|
|
283
|
+
local _callback = function(instance, id)
|
|
284
|
+
self:_debug("Ending trace:", instance.name, id)
|
|
285
|
+
instance.status = finalStatus
|
|
286
|
+
end
|
|
287
|
+
for _k, _v in _exp do
|
|
288
|
+
_callback(_v, _k, _exp)
|
|
289
|
+
end
|
|
290
|
+
-- ▲ ReadonlyMap.forEach ▲
|
|
291
|
+
table.clear(self._activeTraces)
|
|
292
|
+
end
|
|
293
|
+
function TraceManager:setScope(scope)
|
|
294
|
+
self._scope = scope
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
return {
|
|
298
|
+
TraceManager = TraceManager,
|
|
299
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Config } from "./Config";
|
|
2
|
+
export declare class Transport {
|
|
3
|
+
private _config;
|
|
4
|
+
private _remoteEvent?;
|
|
5
|
+
constructor(config: Config);
|
|
6
|
+
private _debug;
|
|
7
|
+
private _getRemoteEvent;
|
|
8
|
+
setupServerRelay(): void;
|
|
9
|
+
private _sendHttp;
|
|
10
|
+
private _sendRemote;
|
|
11
|
+
send(payload: Record<string, unknown>): boolean;
|
|
12
|
+
sendAsync(payload: Record<string, unknown>): void;
|
|
13
|
+
}
|