To be Burst compatible Unity provides logging via Unity.Logging namespace (package: com.unity.logging). In my setup I create a LoggerHandle for every struct implementing ISystem. Each logger handle writes into a separate file with a custom formatter using its own scope like this:
public static Unity.Logging.LoggerHandle CreateLogger(string name)
{
var logger = new Unity.Logging.LoggerConfig()
.MinimumLevel.Debug()
.WriteTo.File(@$"C:\temp\MyGameLogs\{name}.clef",
formatter: LogFormatterClef.Formatter, maxFileSizeBytes: 1024 * 1024 * 100)
#if UNITY_EDITOR
.WriteTo.UnityEditorConsole()
#endif
.CreateLogger();
// The scope lasts forever
Unity.Logging.Log.To(logger.Handle).Decorate("SourceContext", name);
return logger.Handle;
}
The logger handle will be created in the OnCreate() of the system and supplied to jobs like this:
using Unity.Logging;
// ...
[BurstCompile]
public partial struct ServerConnectionSystem : ISystem
{
/// <inheritdoc />
public void OnCreate(ref SystemState state)
{
this._logger = LogFactory.CreateLogger(nameof(ServerConnectionSystem));
// ...
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
if (!this._initializeConnectionQuery.IsEmpty)
{
Log.To(this._logger).Info("[{SystemTime:00000}] New connection incoming", SystemAPI.Time.ElapsedTime);
var initializeConnectionJob = new InitializeConnectionJob()
{
CommandBuffer = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged),
JobLogger = this._logger,
};
initializeConnectionJob.Schedule(this._initializeConnectionQuery);
}
}
[BurstCompile]
private partial struct InitializeConnectionJob : IJobEntity
{
public EntityCommandBuffer CommandBuffer;
public LoggerHandle JobLogger;
[BurstCompile]
public void Execute(Entity connectionEntity, in NetworkId networkId)
{
Log.To(this.JobLogger).Info("Client with network ID {NetworkId} is connected", networkId.Value);
this.CommandBuffer.AddComponent<InitializedConnection>(connectionEntity);
this.CommandBuffer.AddComponent<ConnectionState>(connectionEntity);
}
}
}
It currently works this way. But you know, it may work a thousand times and then one-time log entries gone missing cause of race conditions. So is it thread-safe to use the same LoggerHandle of systems also in scheduled jobs? Didn’t find anything on the sparse Logging documentation.