Provide a way to sink and read all data of output messages

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)
    image

  • Get an output message’s raw arguments, not just a simplified version of the message
    image

  • Get an output message’s level/environment
    image

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! :heart:
Please ask any questions if something wasn’t clear.

12 Likes

The Output must know the raw arguments to be able to render tables and make Instances clickable. The Developer Console doesn’t seem to know this information.

pcall() only catches errors in the main thread! I’ve used UI Labs and experienced asynchronous errors every time I changed the script, which became very unintuitive to look at.

2 Likes

5: Enable clicking through custom tracebacks

This is a major use case, probably for a lot of developers including myself.

Ever had this happen?

image

Cool, a full traceback! Now you’d have to search the explorer and find those scripts, then go to the lines, which is slow. A plugin would be able to give the ability to click these traces and automatically go to the scripts and line numbers (assuming custom output window). This extends use case #4.