Example

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/23db8443-f964-4968-918b-dae5225c2538/Desktop_2020.02.04_-_11.18.59.01-1_Trim-1_trim.mp4

Setup

The logger itself is very basic, it just takes textField UIText property and logs stuff into it, but it requires some setup in the graphs you're using.

<aside> ☝ I have a scaffolding snippet prepared that includes logging into this logger out of the box so you don't need to set it all up yourself. Check it out here: UdonPie Scaffold with external Logger

</aside>

logger = VRCUdonCommonInterfacesIUdonEventReceiver(UnityEngineGameObject.Find("Logger").GetComponent("UdonBehaviour"))
def logExternal(logInput: SystemObject) -> SystemVoid:
  logger.SetProgramVariable("fromGraph", SystemObject(graphName))
  logger.SetProgramVariable("toLog", logInput)
  logger.SendCustomEvent("_log")

<aside> ☝ Since we're logging into UIText - you want to log stuff that can be cast to string

</aside>

# log directly to UIText
logExternal(SystemObject("Running in game"))
def logContexted(input: SystemObject) -> SystemVoid:
  UnityEngineDebug.Log(SystemObject("[" + graphName + "]: " + SystemConvert.ToString(input)))
  logExternal(input) # log to UIText alongside game log

# ...

 def _update():
	logContexted(SystemObject("Logging to both"))

Logger source

To not have a giant text string, logger will automatically cut everything beyond a 1000 characters, since the log is adding new lines on the top - this shouldn't be an issue

from udon_classes import * # IGNORE_LINE
SystemVoid = SystemVoid # IGNORE_LINE # pylint: disable=undefined-variable
this_trans = this_trans # IGNORE_LINE # pylint: disable=undefined-variable
this_gameObj = this_gameObj # IGNORE_LINE # pylint: disable=undefined-variable

global toLog
global fromGraph
global textField
global maxLength

toLog = SystemObject
fromGraph = ""
textField = UnityEngineUIText
maxLength = 1000

def initGraphVars() -> SystemVoid:
  version = 1
  graphName = "Logger"

def logContexted(input: SystemObject) -> SystemVoid:
  UnityEngineDebug.Log(SystemObject("[" + graphName + "]: " + SystemConvert.ToString(input)))

def _start():
  initGraphVars()
  logContexted(SystemObject("v " + SystemConvert.ToString(version)))

def _log() -> SystemVoid:
  if SystemObject(textField) != None:
    currText = textField.get_text()
    if currText.get_Length() > maxLength:
      currText = currText.Substring(0, maxLength)
    newText = "[" + fromGraph + "]: " + SystemConvert.ToString(toLog) + "\\n" + currText
    textField.set_text(newText)