Skip to content

Commit 68f4100

Browse files
committed
feat: improve console library to support two new features
1 parent 20552f5 commit 68f4100

File tree

2 files changed

+102
-18
lines changed

2 files changed

+102
-18
lines changed

packages/console/src/init.luau

Lines changed: 100 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,23 @@ local LOG_LEVEL_COLORS = table.freeze({
2222
local LOG_LEVEL_PRIORITIES = { "Debug", "Info", "Notice", "Warn", "Error", "Critical", "Alert", "Emergency" }
2323

2424
local configurations: { [string]: Configuration } = {}
25+
local globalHooks: { [LogLevel]: { (logger: string, configuration: Configuration, argments: { any }) -> boolean? } } =
26+
{}
2527
local globalConfiguration = {
2628
LogLevel = "Debug",
2729
Format = "%*[%*][%*][%*]: %*%*%*",
2830
Timestamps = false,
2931
ANSI = false,
3032
Tracebacks = false,
33+
34+
DebugTraceback = false,
35+
InfoTraceback = false,
36+
NoticeTraceback = false,
37+
WarnTraceback = true,
38+
ErrorTraceback = true,
39+
CriticalTraceback = true,
40+
AlertTraceback = true,
41+
EmergencyTraceback = true,
3142
}
3243

3344
--[=[
@@ -188,29 +199,57 @@ local function getScriptName(hash: string): string
188199
return hash
189200
end
190201

202+
--[[
203+
Essentially just a fancier debug.traceback, will allow us to see more details surrounding
204+
the traceback.
205+
]]
206+
local function traceback(level: number)
207+
local lines = {}
208+
local index = level + 1
209+
210+
while true do
211+
local script, line, name = debug.info(index, "sln")
212+
213+
if not script then
214+
break
215+
end
216+
217+
index += 1
218+
219+
table.insert(lines, `Script '{script}', Line {line}{name and ` - function {name}` or ""}`)
220+
end
221+
222+
return table.concat(lines, "\n")
223+
end
224+
191225
--[[
192226
Will construct the message that you'll see in the output.
193227
]]
194228
local function constructContent(hash: string, ...): string
195229
local configuration = getScriptConfiguration(hash)
196230
local scriptName = getScriptName(hash)
197231
local logLevel = debug.info(2, "n")
232+
local callingLine = debug.info(3, "l")
198233
local message = stringify(configuration, ...)
199234
local format = configuration.Format or globalConfiguration.Format
200235
local ansiEnabled = configuration.ANSI or globalConfiguration.ANSI
201236
local timestampsEnabled = configuration.Timestamps or globalConfiguration.Timestamps
202237
local tracebackEnabled = configuration.Tracebacks or globalConfiguration.Tracebacks
203238
local timestamp = os.date("%Y-%m-%d %H:%M:%S", os.time())
204239

240+
if not tracebackEnabled then
241+
tracebackEnabled = configuration[`{logLevel}Traceback`]
242+
end
243+
205244
local content = string.format(
206245
format,
207246
`{ansiEnabled and LOG_LEVEL_COLORS[logLevel] or ``}`,
208247
`{timestampsEnabled and `{timestamp}` or `-`}`,
209-
`{configuration.Name or scriptName}`,
248+
`{configuration.Name or scriptName}:{callingLine}`,
210249
logLevel,
211250
message,
212251
`{ansiEnabled and RESET or ""}`,
213-
`{tracebackEnabled and `\nstack traceback{debug.traceback("", 3)}` or ``}`
252+
`{tracebackEnabled and `\nStack Begin\n{traceback(3)}\nStack End` or ``}`
214253
)
215254

216255
return content
@@ -219,13 +258,23 @@ end
219258
--[[
220259
returns a boolean that defines if the API that calls this function is able to log to the output.
221260
]]
222-
local function meetsLogLevelRequirement(hash: string): boolean
261+
local function meetsLogLevelRequirement(hash: string, ...): boolean
223262
local configuration = getScriptConfiguration(hash)
224263
local targetLoglevel = configuration.LogLevel or globalConfiguration.LogLevel
225264
local targetLoglevelPriority = table.find(LOG_LEVEL_PRIORITIES, targetLoglevel)
226265
local currentLogLevel = debug.info(2, "n")
227266
local currentLogLevelPriority = table.find(LOG_LEVEL_PRIORITIES, currentLogLevel)
228267

268+
local hooks = globalHooks[currentLogLevel :: LogLevel]
269+
270+
if hooks then
271+
for _, callback in hooks do
272+
if callback(hash, configuration, ...) == false then
273+
return false
274+
end
275+
end
276+
end
277+
229278
return currentLogLevelPriority >= targetLoglevelPriority
230279
end
231280

@@ -240,7 +289,7 @@ end
240289
Documented within @console.docs.luau
241290
]]
242291
function Console.Prototype.Debug(self: Console, ...)
243-
if meetsLogLevelRequirement(self.Name) then
292+
if meetsLogLevelRequirement(self.Name, ...) then
244293
local content = constructContent(self.Name, ...)
245294

246295
print(content)
@@ -251,7 +300,7 @@ end
251300
Documented within @console.docs.luau
252301
]]
253302
function Console.Prototype.Info(self: Console, ...)
254-
if meetsLogLevelRequirement(self.Name) then
303+
if meetsLogLevelRequirement(self.Name, ...) then
255304
local content = constructContent(self.Name, ...)
256305

257306
print(content)
@@ -262,7 +311,7 @@ end
262311
Documented within @console.docs.luau
263312
]]
264313
function Console.Prototype.Notice(self: Console, ...)
265-
if meetsLogLevelRequirement(self.Name) then
314+
if meetsLogLevelRequirement(self.Name, ...) then
266315
local content = constructContent(self.Name, ...)
267316

268317
print(content)
@@ -273,7 +322,7 @@ end
273322
Documented within @console.docs.luau
274323
]]
275324
function Console.Prototype.Warn(self: Console, ...)
276-
if meetsLogLevelRequirement(self.Name) then
325+
if meetsLogLevelRequirement(self.Name, ...) then
277326
local content = constructContent(self.Name, ...)
278327

279328
warn(content)
@@ -284,43 +333,43 @@ end
284333
Documented within @console.docs.luau
285334
]]
286335
function Console.Prototype.Error(self: Console, ...)
287-
if meetsLogLevelRequirement(self.Name) then
336+
if meetsLogLevelRequirement(self.Name, ...) then
288337
local content = constructContent(self.Name, ...)
289338

290-
error(content, math.huge)
339+
error(content, -1)
291340
end
292341
end
293342

294343
--[[
295344
Documented within @console.docs.luau
296345
]]
297346
function Console.Prototype.Critical(self: Console, ...)
298-
if meetsLogLevelRequirement(self.Name) then
347+
if meetsLogLevelRequirement(self.Name, ...) then
299348
local content = constructContent(self.Name, ...)
300349

301-
error(content, math.huge)
350+
error(content, -1)
302351
end
303352
end
304353

305354
--[[
306355
Documented within @console.docs.luau
307356
]]
308357
function Console.Prototype.Alert(self: Console, ...)
309-
if meetsLogLevelRequirement(self.Name) then
358+
if meetsLogLevelRequirement(self.Name, ...) then
310359
local content = constructContent(self.Name, ...)
311360

312-
error(content, math.huge)
361+
error(content, -1)
313362
end
314363
end
315364

316365
--[[
317366
Documented within @console.docs.luau
318367
]]
319368
function Console.Prototype.Emergency(self: Console, ...)
320-
if meetsLogLevelRequirement(self.Name) then
369+
if meetsLogLevelRequirement(self.Name, ...) then
321370
local content = constructContent(self.Name, ...)
322371

323-
error(content, math.huge)
372+
error(content, -1)
324373
end
325374
end
326375

@@ -335,6 +384,31 @@ function Console.Prototype.Configure(self: Console, config: Configuration)
335384
end
336385
end
337386

387+
--[[
388+
@function hook
389+
@within Console
390+
391+
@param logLevel LogLevel
392+
@param callback (configuration: Configuration, argments: { any }) -> ()
393+
@return ()
394+
395+
Will allow a developer to hook into a log level, this can be used for reporting logs elsewhere, such as to an
396+
external service, or enabling further control over the library.
397+
398+
returning `false`, will disable the log. Returning `true`, or `nil` will let it output - if the log level is
399+
allowed.
400+
]]
401+
function Console.Public.hook(
402+
logLevel: LogLevel,
403+
callback: (logger: string, configuration: Configuration, argments: { any }) -> boolean?
404+
)
405+
if not globalHooks[logLevel] then
406+
globalHooks[logLevel] = {}
407+
end
408+
409+
table.insert(globalHooks[logLevel], callback)
410+
end
411+
338412
--[=[
339413
@function setDefaults
340414
@within Console
@@ -387,12 +461,22 @@ export type Configuration = {
387461
Format: string?,
388462
Name: string?,
389463

390-
Tracebacks: boolean?,
391464
Timestamps: boolean?,
392465
ANSI: boolean?,
466+
Tracebacks: boolean?,
467+
468+
DebugTraceback: boolean?,
469+
InfoTraceback: boolean?,
470+
NoticeTraceback: boolean?,
471+
WarnTraceback: boolean?,
472+
ErrorTraceback: boolean?,
473+
CriticalTraceback: boolean?,
474+
AlertTraceback: boolean?,
475+
EmergencyTraceback: boolean?,
393476
}
394477
export type Console = typeof(Console.Prototype) & {
395478
Name: string,
479+
Hooks: {},
396480
}
397481

398482
return Console.Public

packages/console/wally.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "4x8matrix/console"
3-
version = "3.0.0"
3+
version = "3.1.0"
44
registry = "https://github.com/UpliftGames/wally-index"
55
realm = "shared"
66

@@ -9,4 +9,4 @@ shared-packages = "game.ReplicatedStorage.Packages"
99

1010
[dependencies]
1111

12-
[server-dependencies]
12+
[server-dependencies]

0 commit comments

Comments
 (0)