Calls to Debugger.Log in a debuggee can generate a ICorDebugManagedCallback::LogMessage managed debug event.  This is the managed equivalent of kernel32!OutputDebugString. Other managed functions (like Debug.Write) may degenerate to calls to Debugger.Log too.
Your debugger needs to subscribe log messages by calling ICorDebugProcess::EnableLogMessages(TRUE) for the process being debugged.

The Log call looks like:

public static void Log(
    int level,
    string category,
    string message
);
 

The event is exposed via callback, which looks like this:

HRESULT LogMessage(
    [in] ICorDebugAppDomain *pAppDomain,
    [in] ICorDebugThread *pThread,   <--
ICorDebugThread for the thread that called Debugger.Log.
    [in] LONG lLevel,  <-- 'level' parameter to Log
    [in] WCHAR *pLogSwitchName,  <-- 'category' parameter to Log
    [in] WCHAR *pMessage  <-- 'message' parameter to log.
); 

VS handles this debug event by printing the log message to the Output Window.  A fancier debugger could use this as a pipeline for the debuggee to issue commands to the debugger.

You can kind of think of this as a cross-process TraceListener. If MDbg was fancier, we could have connected this LogMessage to a TraceListener .