Cysharp / ZLogger

Zero Allocation Text/Structured Logger for .NET with StringInterpolation and Source Generator, built on top of a Microsoft.Extensions.Logging.
MIT License
1.25k stars 88 forks source link

How To Add a custom formatter? #33

Closed carrotstien closed 3 years ago

carrotstien commented 3 years ago

exact example:

In Unity, when you have a vector3, and I try to log that using zlogger, it looks like (0.1, 0.8, 0.1)

even if the true value is (0.112341, 0.8214152, 0.1123415)

I have an extension Vector3.ToNiceString()

public static string ToNiceString(this Vector3 vec) {
        return $"{{{(double)vec.x},{(double)vec.y},{(double)vec.z}}}";
    }

but obviously using this would create garbage. How would I achieve the same result using the zlogger, but without writing it out in long form like

LogInformation(format, "(", vector3.x, "," ,vector3.y, "," ,vector.z, ")")

I have already been able to adjust the float logging to log long form (cast to double first), but don't know how to do something more complicated as above.

in other words

I'd like to be able to do

ElevenLogger.MPMatchLogger?.Log("Received ball toss from opponent: ",v);

instead of

ElevenLogger.MPMatchLogger?.Log("Received ball toss from opponent: (", v.x, ", ",v.y, ", ", v.z,")");

and achieve the same result

carrotstien commented 3 years ago

for anyone curious about this too, i've found this RegisterTryFormat in https://github.com/Cysharp/ZString

and i've just added this somewhere in my project and it worked

static Encoding UTF8NoBom = new UTF8Encoding(false);
    private static void RegisterVector3Writer()
    {
        Cysharp.Text.Utf8ValueStringBuilder.RegisterTryFormat((Vector3 value, Span<byte> destination, out int written, System.Buffers.StandardFormat format) =>
        {
            written = 0;
            int newWritten = 0;

            newWritten = ElevenUtils.GetBytes(UTF8NoBom, "(".AsSpan(), destination.Slice(written));
            written += newWritten;

            System.Buffers.Text.Utf8Formatter.TryFormat((double)value.x, destination.Slice(written), out newWritten, format);
            written += newWritten;

            newWritten = ElevenUtils.GetBytes(UTF8NoBom, ",".AsSpan(), destination.Slice(written));
            written += newWritten;

            System.Buffers.Text.Utf8Formatter.TryFormat((double)value.y, destination.Slice(written), out newWritten, format);
            written += newWritten;

            newWritten = ElevenUtils.GetBytes(UTF8NoBom, ",".AsSpan(), destination.Slice(written));
            written += newWritten;

            System.Buffers.Text.Utf8Formatter.TryFormat((double)value.z, destination.Slice(written), out newWritten, format);
            written += newWritten;

            newWritten = ElevenUtils.GetBytes(UTF8NoBom, ")".AsSpan(), destination.Slice(written));
            written += newWritten;

            return true;
        });
    }

and the GetBytes is just a copy of what was in the library already (but i guess it was out of reach in terms of package separation

    public static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan<char> span, Span<byte> bytes)
    {
        if (span.Length == 0) return 0;
        fixed (char* src = span)
        fixed (byte* dest = bytes)
        {
            return encoding.GetBytes(src, span.Length, dest, bytes.Length);
        }
    }