As a Roblox developer, it is not currently possible to traceback every type of output message. It is also not possible to sink messages and prevent them from showing.
Problem
These two events are limited and disconnected, despite them being similar.
ScriptContext.Error
Fired when an error occurs.
message: string, stackTrace: string, script: Instance
LogService.MessageOut
Fires when the client outputs text.
message: string, messageType: Enum.MessageType
I’m requesting that scripts have the ability to:
-
Decide to sink or pass output messages before they are used (before MessageOut, Error Report)
-
Get an output message’s traceback and call stack (includes line + character numbers)
-
Get an output message’s raw arguments, not just a simplified version of the message
-
Get an output message’s level/environment
Having these would be greatly beneficial to the following use cases.
Use Cases
1: In-game Output
Games or plugins would be able to mimic the Output window for benefits. They could use the new output message information for various tooling, such as sorting/filtering messages based on the traceback.
2: Extended Output Reactivity
Games or plugins would be able to react to output messages in new ways. There are some games out there that detect errors from a script and tell the user (or admins) that something is wrong. Currently, this is not possible with warnings or prints. This request would effectively allow this to happen!
3: Simplification
ScriptContext provides a way to detect and get the traceback of errors. LogService provides a way to detect any output message, but not where they came from. LogService also provides a way to get the log history, which also provides a timestamp in each message.
This request would unify all of these into one system and provide a way to get the traceback and call stacks of all types of output messages. It would also let you sink messages to have them not be used.
4: Output inside of Plugins
This is my favorite use case, and mainly why I am requesting this feature. It would allow plugin developers like me to create a miniature customizable output window for benefits.
For example, a plugin like UI Labs could capture errors that originate from the components it tests, sink them, and isolate them to a window only the plugin shows to avoid cluttered outputs.
Or even better, the entire plugin could be an improved output window that improves workflows and makes data more readable. Providing access to more APIs has helped Roblox become a much better platform thanks to the plugin developers that used those APIs. For this to happen, all of the features I mentioned would need to be included so that this plugin could be a full replacement for the built-in Output.
Replacement plugins have been made before, so another one shouldn’t be too surprising. For example, Lasso aims to be a better Asset Manager. Moon Animator 2 aims to be a better Animation Editor. Let’s keep that trend going!
Proposed API
Sharing my thought process behind the ideal API for this feature may help reduce confusion. There are many possible ways to get the information if this request were to be fulfilled, but this is one way I’d like to propose.
Details
Since there’s a lot of details we might have, it would be best to put it in a dictionary instead of passing it all as arguments. This way, we don’t need variables for all of them to use only want we need.
LogService:BindToMessageOut(name: string, priority: number, callback: (OutputMessageData) → (Enum.OutputResult?))
Fires the callback when a message gets outputted.
LogService:UnbindFromMessageOut(name: string)
Removes the bind to output messages given a name.
The API is inspired by ContextActionService, since you would be able to sink messages and prevent them from showing in the Developer Console and Output. It is also inspired by RunService since you can set the priority and name of the output bindings.
-- New Globals
type CallLevel = "Studio" | "Edit" | "Client" | "Server"
type CallData = {
Source: LuaSourceContainer,
Line: number,
Character: number,
}
export type OutputMessageData = {
Type: Enum.MessageType,
Traceback: string,
Stack: {CallData},
Level: CallLevel,
Timestamp: number,
}
Enum.OutputResult = {
Sink = 1,
Pass = 2, -- Default
}
Example Usage
local LogService = game:GetService("LogService")
LogService:BindToMessageOut(function(message: OutputMessageData)
if message.Type == Enum.MessageType.MessageOutput then
local first = message.CallStack[1]
warn(`Printed at '{first.Source}' on Line {first.Line}`)
return Enum.OutputResult.Sink
end
return Enum.OutputResult.Pass
end)
Thanks for considering this!
Please ask any questions if something wasn’t clear.