@@ -22,12 +22,23 @@ local LOG_LEVEL_COLORS = table.freeze({
2222local LOG_LEVEL_PRIORITIES = { "Debug" , "Info" , "Notice" , "Warn" , "Error" , "Critical" , "Alert" , "Emergency" }
2323
2424local configurations : { [string ]: Configuration } = {}
25+ local globalHooks : { [LogLevel ]: { (logger : string , configuration : Configuration , argments : { any }) -> boolean ? } } =
26+ {}
2527local 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
189200end
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]]
194228local 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 `\n stack traceback{ debug. traceback ("" , 3 )}` or `` }`
252+ `{tracebackEnabled and `\n Stack Begin \n { traceback (3 )}\n Stack 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
230279end
231280
240289 Documented within @console .docs.luau
241290]]
242291function 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 )
251300 Documented within @console .docs.luau
252301]]
253302function 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 )
262311 Documented within @console .docs.luau
263312]]
264313function 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 )
273322 Documented within @console .docs.luau
274323]]
275324function 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]]
286335function 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
292341end
293342
294343--[[
295344 Documented within @console .docs.luau
296345]]
297346function 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
303352end
304353
305354--[[
306355 Documented within @console .docs.luau
307356]]
308357function 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
314363end
315364
316365--[[
317366 Documented within @console .docs.luau
318367]]
319368function 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
325374end
326375
@@ -335,6 +384,31 @@ function Console.Prototype.Configure(self: Console, config: Configuration)
335384 end
336385end
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}
394477export type Console = typeof (Console .Prototype ) & {
395478 Name : string ,
479+ Hooks : {},
396480}
397481
398482return Console .Public
0 commit comments