Log a NativeText inside a Bursted Job

How do you log a NativeText input to a Job that is Bursted if ToString() can’t be bursted?

I’m using Unity.Logging which is supposed to be Burst compatible.

So Log.To(xxx).Error(“{0}”, text.ToString()) ← Can’t burst compile

1 Like

https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/csharp-string-support.html#string-interpolation-support

https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/csharp-string-support.html#managed-strings

If I’m understanding your code correctly, then the Error function takes a regular string? If so, that’s not supported.

Edit:
Sorry, I’m not familiar with the Unity.Logging package (missed that originally). Isn’t it possible just to do Error(“{0}”, text)?

If I’m understanding this documentation correctly: Composite formatting and format specifiers | Unity Logging | 1.3.2
Then it should work with just this: Error(text), since NativeText is implementing INativeList & IUTF8Bytes.

1 Like

Yeah. Besides simply passing string-adjacent types as the msg argument, such types can be passed as additional arguments without ToString and will be interpreted correctly.

However, it should be noted that the support for string-like types only extends as far as the logging library supports it. It doesn’t appear that custom string-like types implementing the listed interfaces receive the same treatment as something like UnsafeText or FixedString32Bytes, which are handled by methods in the Builder type. Instead, they’re just handled like any other custom type (outputs the item’s fields).

Hi @Darkgaze

Thanks for the question! The issue in the code-snippet you share is that ToString() is a managed method, and therefore incompatible with Burst. However, just like @Per-Morten and @Spy-Master mention (thanks both of you for helping around the forums!) you should be able to avoid using ToString() all together and just call:

Log.To(logger).Error("{0}", text)

And have it work :slight_smile: hope this helps!

Edit: Saw that you Darkgaze noted this proposed one-line-solution doesn’t work for NativeText, sorry missed that! But saw you got more help below by other kind users and that via that you managed to get your problem solved already which is great, thanks for pitching in all.

2 Likes

Log.To(xxx).Error(“{0}”, text) when text is NativeText can’t be passed directly. It requires a string.

I guess the problem is with the interpolation and having to create a combined text. In that case, it must be a FixedStringXBytes, but Logging package which is what I’m using can only print FixedString32bytes.

By the way, the job is generic and has a generic type “Data”. That’s the text I should print, but I can’t do it through: typeof(Data).toString() either. If there was any way of printing that without having to call toSTring() that would suffice.

1 Like

Here’s a stupid way to print the type name of a generic both in and outside of Burst: Latios-Framework/PsyshockPhysics/Physics/Internal/Queries/Layers/FindPairsInternal.cs at v0.11.0 · Dreaming381/Latios-Framework · GitHub

By the way, it’s absurd to a level I don’t understand…

I managed to pass a FixedString32Bytes to the job, then

FixedString32bytes aText;

var text = FixedString.Format("Very long text to print ..................... {0} and {1}", andInt, aText);
Log.To(Loggers.Engine).Error(text);

The result of FixedSTring.Format is always a 128bytes or 512bytes string.

But Logging.To → Error method can receive a whole collection of 400 variations that are declared in generated code with classes from my project to insane levels, but It only allows to print a FixedString32Bytes. Doesn’t allow 128 or 512 or any other because that code is not generated. O_O

O_O that’s crazy. I’ll keep that solution but I prefer not to do it that way for now.

Can’t is a strong word; that’s not what I’m seeing in Logging 1.3.2.

Functional sample:

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Logging;
using Unity.Logging.Sinks;
using UnityEngine;

public class LogSample : MonoBehaviour
{
    private Unity.Logging.Logger _logger;
    private JobHandle jobHandle;

    private void Awake()
    {
        var v = new LoggerConfig()
            .CaptureStacktrace()
            .OutputTemplate("[{Timestamp}] {Message}{NewLine}{Stacktrace}")
#if UNITY_EDITOR
            .WriteTo.UnityEditorConsole(outputTemplate: "{Message}")
#else
            .WriteTo.UnityDebugLog(outputTemplate: "{Message}")
#endif
            ;
        _logger = v.CreateLogger();
    }

    private void Update()
    {
        jobHandle.Complete();
        NativeText text = new NativeText($"Frame count: {Time.frameCount}", Allocator.TempJob);
        jobHandle = new LogJob { Text = text, LoggerHandle = _logger.Handle }.Schedule();
        jobHandle = text.Dispose(jobHandle);
    }

    private void OnDestroy()
    {
        jobHandle.Complete();
        _logger.Dispose();
    }
}

[BurstCompile]
internal partial struct LogJob : IJob
{
    [ReadOnly] public NativeText Text;
    public LoggerHandle LoggerHandle;

    public void Execute()
    {
        bool managed = false;
        SetIfManaged(ref managed);
        Log.To(LoggerHandle).Error(Text);
        Log.To(LoggerHandle).Error("{text}", Text);
        Log.To(LoggerHandle).Error("Uh-oh: {text} (managed: {managed})", Text, managed);
    }

    [BurstDiscard]
    private static void SetIfManaged(ref bool managed)
    {
        managed = true;
    }
}

Perhaps you’re overly reliant on your IDE telling you an overload doesn’t exist. Your IDE may be configured to run source generators at specific points. Visual Studio’s default behavior was changed to run source generators on save or build, this can be configured to run after any change in Options > Text Editor > C# > Advanced > Source Generators > Source generator execution.

You probably want to change your managed path to typeof(T).Name. It’s commonly known that, as nameof produces a constant string, nameof(T) just produces "T".