Using loggers to simplify debugging

One of the most essential parts of any plugin is logging messages. Be it a piece of information, a warning or a more significant error, BepInEx provides functionality to log it all.
With BepInEx, you can use the following logging APIs:

Whichever API you will use, BepInEx will write the logs to the console, Unity's output_log.txt and to BepInEx/LogOutput.log file.

Using Logger APIs

This is the recommended way for logging in plugins.

All plugin instances have a logger property:

using BepInEx;

namespace MyFirstPlugin
{
    [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
    public class Plugin : BaseUnityPlugin
    {
        private void Awake()
        {
            Logger.LogInfo("This is information");
            Logger.LogWarning("This is a warning");
            Logger.LogError("This is an error");
        }
    }
}

This will print the following messages to BepInEx console:

[Info   : Logger Test] This is information
[Warning: Logger Test] This is a warning
[Error  : Logger Test] This is an error

Notice that the log reports the message type and the message source.
When using BepInEx's own logging API, the log source (i.e. the plugin name is automatically logged).

Check out ManualLogSource for all available logging methods.

Advanced: Log sources and log listeners

BepInEx's logging system mimics that of System.Diagnostics.Trace API.
BepInEx allows creating log sources that can generate log events (i.e. the messages) and log listeners that receive and process those log events.
All sources are linked to listeners via Logger class.

In most cases, you don't have to care about how the API works. However, in some cases, you may want to register your own log sources to log messages.
In addition, sometimes, you might need to process the log events to write them somewhere. This is where you use the manual APIs.

Registering log sources

A log source is a class that inherits from ILogSource.
The most basic implementation is ManualLogSource which exposes various convenience logging functions.

To register a log source, add it to Sources collection:

var myLogSource = new ManualLogSource("MyLogSource"); // The source name is shown in BepInEx log

// Register the source
BepInEx.Logging.Logger.Sources.Add(myLogSource);

myLogSource.LogInfo("Test"); // Will print [Info: MyLogSource] Test

// Remove the source to free resources
BepInEx.Logging.Logger.Sources.Remove(myLogSource);

Because ManualLogSource is so useful, you can use CreateLogSource(String) to automatically create and register a ManualLogSource.

That way, the above example becomes

var myLogSource = BepInEx.Logging.Logger.CreateLogSource("MyLogSource");
myLogSource.LogInfo("Test");
BepInEx.Logging.Logger.Sources.Remove(myLogSource);

About log listeners

Log listeners are used to processing messages from log sources. To create a log source, create a class that inherits ILogListener.
After that, register a log listener by adding it to Listeners.

By default, BepInEx itself registers the following listeners:

If you need to write a custom log listener, consider using the above ones as examples.

Advanced: global plugin logger

If you have multiple classes in your plugin but only one plugin, you might want to use the same plugin logger in the other class as well.

This can be done with with a global plugin logger pattern. To apply the pattern, do the following:

Example:

using BepInEx;
using BepInEx.Logging;

namespace MyFirstPlugin
{
    [BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
    public class Plugin : BaseUnityPlugin
    {
        internal static new ManualLogSource Log;

        private void Awake()
        {
            this.Log = base.Logger;
        }
    }
}

// Some other class in the plugin assembly
class SomeOtherAssembly
{
    public void SomeMethod()
    {
        Plugin.Log.LogInfo("Plugin message!");
    }
}

Summary

BepInEx provides simple logging methods for plugins. Additionally, you are free to extend BepInEx logging facilities to suit your needs.

Next: Reading and writing configuration files