diff --git a/src/ZLogger.MessagePack/MessagePackEncodedText.cs b/src/ZLogger.MessagePack/MessagePackEncodedText.cs new file mode 100644 index 00000000..58189eb5 --- /dev/null +++ b/src/ZLogger.MessagePack/MessagePackEncodedText.cs @@ -0,0 +1,71 @@ +using System; +using System.Text; +using MessagePack; + +namespace ZLogger.MessagePack; + +public readonly struct MessagePackEncodedText : IEquatable +{ + static readonly Encoding StringEncoding = new UTF8Encoding(false); + + public string Value { get; private init; } + public ReadOnlySpan Utf8EncodedValue => utf8EncodedValue; + + readonly byte[] utf8EncodedValue; + + MessagePackEncodedText(string value, byte[] utf8EncodedValue) + { + Value = value; + this.utf8EncodedValue = utf8EncodedValue; + } + + public static MessagePackEncodedText Encode(string stringValue, MessagePackSerializerOptions? options = null) + { + var oldSpec = options?.OldSpec ?? false; + var characterLength = stringValue.Length; + var bufferSize = StringEncoding.GetMaxByteCount(characterLength) + 5; + + var useOffset = characterLength switch + { + <= MessagePackRange.MaxFixStringLength => 1, + <= byte.MaxValue when !oldSpec => 2, + <= ushort.MaxValue => 3, + _ => 5 + }; + + Span buffer = stackalloc byte[bufferSize]; + var byteCount = StringEncoding.GetBytes(stringValue, buffer[useOffset..]); + // move body and write prefix + if (byteCount <= MessagePackRange.MaxFixStringLength) + { + buffer[0] = (byte)(MessagePackCode.MinFixStr | byteCount); + } + else if (byteCount <= byte.MaxValue && !oldSpec) + { + buffer[0] = MessagePackCode.Str8; + buffer[1] = unchecked((byte)byteCount); + } + else if (byteCount <= ushort.MaxValue) + { + buffer[0] = MessagePackCode.Str16; + // Write big-endian + buffer[1] = (byte)(byteCount >> 8); + buffer[2] = (byte)byteCount; + } + else + { + buffer[0] = MessagePackCode.Str32; + buffer[1] = (byte)(byteCount >> 24); + buffer[2] = (byte)(byteCount >> 16); + buffer[3] = (byte)(byteCount >> 8); + buffer[4] = (byte)byteCount; + } + + var encodedValue = buffer[..(byteCount + useOffset)].ToArray(); + return new MessagePackEncodedText(stringValue, encodedValue); + } + + public bool Equals(MessagePackEncodedText other) => Value == other.Value; + public override bool Equals(object? obj) => obj is MessagePackEncodedText other && Equals(other); + public override int GetHashCode() => Value.GetHashCode(); +} \ No newline at end of file diff --git a/src/ZLogger.MessagePack/MessagePackZLoggerFormatter.cs b/src/ZLogger.MessagePack/MessagePackZLoggerFormatter.cs index a2d4534e..451fb425 100644 --- a/src/ZLogger.MessagePack/MessagePackZLoggerFormatter.cs +++ b/src/ZLogger.MessagePack/MessagePackZLoggerFormatter.cs @@ -1,259 +1,282 @@ using System; using System.Buffers; +using System.Collections.Generic; using System.Numerics; using MessagePack; using Microsoft.Extensions.Logging; -using ZLogger.MessagePack; -namespace ZLogger +namespace ZLogger.MessagePack; + +public record MessagePackPropertyNames( + MessagePackEncodedText Category, + MessagePackEncodedText Timestamp, + MessagePackEncodedText LogLevel, + MessagePackEncodedText EventId, + MessagePackEncodedText EventIdName, + MessagePackEncodedText Exception, + MessagePackEncodedText Message, + + MessagePackEncodedText ExceptionName, + MessagePackEncodedText ExceptionMessage, + MessagePackEncodedText ExceptionStackTrace, + MessagePackEncodedText ExceptionInnerException, + + MessagePackEncodedText LogLevelTrace, + MessagePackEncodedText LogLevelDebug, + MessagePackEncodedText LogLevelInformation, + MessagePackEncodedText LogLevelWarning, + MessagePackEncodedText LogLevelError, + MessagePackEncodedText LogLevelCritical, + MessagePackEncodedText LogLevelNone, + + MessagePackEncodedText? ParameterKeyValues = null, + MessagePackEncodedText? ScopeKeyValues = null) { - public static class ZLoggerOptionsMessagePackExtensions + public static readonly MessagePackPropertyNames Default = new( + Category: MessagePackEncodedText.Encode("Category"), + Timestamp: MessagePackEncodedText.Encode("Timestamp"), + LogLevel: MessagePackEncodedText.Encode("LogLevel"), + EventId: MessagePackEncodedText.Encode("EventId"), + EventIdName: MessagePackEncodedText.Encode("EventIdName"), + Exception: MessagePackEncodedText.Encode("Exception"), + Message: MessagePackEncodedText.Encode("Message"), + + ExceptionName: MessagePackEncodedText.Encode("Name"), + ExceptionMessage: MessagePackEncodedText.Encode("Message"), + ExceptionStackTrace: MessagePackEncodedText.Encode("StackTrace"), + ExceptionInnerException: MessagePackEncodedText.Encode("InnerException"), + + LogLevelTrace: MessagePackEncodedText.Encode(nameof(Microsoft.Extensions.Logging.LogLevel.Trace)), + LogLevelDebug: MessagePackEncodedText.Encode(nameof(Microsoft.Extensions.Logging.LogLevel.Debug)), + LogLevelInformation: MessagePackEncodedText.Encode(nameof(Microsoft.Extensions.Logging.LogLevel.Information)), + LogLevelWarning: MessagePackEncodedText.Encode(nameof(Microsoft.Extensions.Logging.LogLevel.Warning)), + LogLevelError: MessagePackEncodedText.Encode(nameof(Microsoft.Extensions.Logging.LogLevel.Error)), + LogLevelCritical: MessagePackEncodedText.Encode(nameof(Microsoft.Extensions.Logging.LogLevel.Critical)), + LogLevelNone: MessagePackEncodedText.Encode(nameof(Microsoft.Extensions.Logging.LogLevel.None))); + + public MessagePackEncodedText GetEncodedLogLevel(LogLevel logLevel) => logLevel switch { - public static ZLoggerOptions UseMessagePackFormatter(this ZLoggerOptions options, Action? configure = null) - { - return options.UseFormatter(() => - { - var formatter = new MessagePackZLoggerFormatter(); - configure?.Invoke(formatter); - return formatter; - }); - } - } + Microsoft.Extensions.Logging.LogLevel.Trace => LogLevelTrace, + Microsoft.Extensions.Logging.LogLevel.Debug => LogLevelDebug, + Microsoft.Extensions.Logging.LogLevel.Information => LogLevelInformation, + Microsoft.Extensions.Logging.LogLevel.Warning => LogLevelWarning, + Microsoft.Extensions.Logging.LogLevel.Error => LogLevelError, + Microsoft.Extensions.Logging.LogLevel.Critical => LogLevelCritical, + Microsoft.Extensions.Logging.LogLevel.None => LogLevelNone, + _ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null) + }; } -namespace ZLogger.MessagePack +public class MessagePackZLoggerFormatter : IZLoggerFormatter { - public class MessagePackZLoggerFormatter : IZLoggerFormatter + [ThreadStatic] + static ArrayBufferWriter? threadStaticBufferWriter; + + bool IZLoggerFormatter.WithLineBreak => false; + + public MessagePackSerializerOptions MessagePackSerializerOptions { get; set; } = MessagePackSerializer.DefaultOptions; + public IncludeProperties IncludeProperties { get; set; } = IncludeProperties.Default; + public MessagePackPropertyNames PropertyNames { get; set; } = MessagePackPropertyNames.Default; + public IKeyNameMutator? KeyNameMutator { get; set; } + + public void FormatLogEntry(IBufferWriter writer, TEntry entry) where TEntry : IZLoggerEntry { - // "CategoryName" - static readonly byte[] CategoryNameKey = { 0b10100000 | 12, 67, 97, 116, 101, 103, 111, 114, 121, 78, 97, 109, 101 }; - // "Timestamp" - static readonly byte[] TimestampKey = { 0b10100000 | 9, 84, 105, 109, 101, 115, 116, 97, 109, 112 }; - // "LogLevel" - static readonly byte[] LogLevelKey = { 0b10100000 | 8, 76, 111, 103, 76, 101, 118, 101, 108 }; - // "EventId" - static readonly byte[] EventIdKey = { 0b10100000 | 7, 69, 118, 101, 110, 116, 73, 100 }; - // "EventIdName" - static readonly byte[] EventIdNameKey = { 0b10100000 | 11, 69, 118, 101, 110, 116, 73, 100, 78, 97, 109, 101 }; - // "Exception" - static readonly byte[] ExceptionKey = { 0b10100000 | 9, 69, 120, 99, 101, 112, 116, 105, 111, 110 }; - - // "Name" - static readonly byte[] NameKey = { 0b10100000 | 4, 78, 97, 109, 101 }; - // "Message" - static readonly byte[] MessageKey = { 0b10100000 | 7, 77, 101, 115, 115, 97, 103, 101 }; - // "StackTrace" - static readonly byte[] StackTraceKey = { 0b10100000 | 10, 83, 116, 97, 99, 107, 84, 114, 97, 99, 101 }; - // "InnerException" - static readonly byte[] InnerExceptionKey = { 0b10100000 | 14, 73, 110, 110, 101, 114, 69, 120, 99, 101, 112, 116, 105, 111, 110 }; - - // "Trace" - static readonly byte[] Trace = { 0b10100000 | 5, 84, 114, 97, 99, 101 }; - // "Debug" - static readonly byte[] Debug = { 0b10100000 | 5, 68, 101, 98, 117, 103 }; - // "Information" - static readonly byte[] Information = { 0b10100000 | 11, 73, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110 }; - // "Warning" - static readonly byte[] Warning = { 0b10100000 | 7, 87, 97, 114, 110, 105, 110, 103 }; - // "Error" - static readonly byte[] Error = { 0b10100000 | 5, 69, 114, 114, 111, 114 }; - // "Critical" - static readonly byte[] Critical = { 0b10100000 | 8, 67, 114, 105, 116, 105, 99, 97, 108 }; - // "None" - static readonly byte[] None = { 0b10100000 | 4, 78, 111, 110, 101 }; - - [ThreadStatic] - static ArrayBufferWriter? threadStaticBufferWriter; - - bool IZLoggerFormatter.WithLineBreak => false; - - public MessagePackSerializerOptions MessagePackSerializerOptions { get; set; } = MessagePackSerializer.DefaultOptions; - public string MessagePropertyName { get; set; } = "Message"; - public IncludeProperties IncludeProperties { get; set; } = IncludeProperties.Default; - public IKeyNameMutator? KeyNameMutator { get; set; } - - public void FormatLogEntry(IBufferWriter writer, TEntry entry) where TEntry : IZLoggerEntry - { - var messagePackWriter = new MessagePackWriter(writer); - var propCount = BitOperations.PopCount((uint)IncludeProperties); + var messagePackWriter = new MessagePackWriter(writer); + var propCount = BitOperations.PopCount((uint)IncludeProperties); + var scopePropCount = 0; - if ((IncludeProperties & IncludeProperties.ParameterKeyValues) != 0) - { - propCount--; - propCount += entry.ParameterCount; - } - if (entry.LogInfo.Exception == null && ((IncludeProperties & IncludeProperties.Exception) != 0)) + if ((IncludeProperties & IncludeProperties.ParameterKeyValues) != 0 && + // flatten + PropertyNames.ParameterKeyValues == null) + { + propCount--; + propCount += entry.ParameterCount; + } + if (entry.LogInfo.Exception == null && ((IncludeProperties & IncludeProperties.Exception) != 0)) + { + propCount--; + } + if ((IncludeProperties & IncludeProperties.ScopeKeyValues) != 0) + { + if (entry.LogInfo.ScopeState == null) { propCount--; } - if ((IncludeProperties & IncludeProperties.ScopeKeyValues) != 0) + else { - propCount--; - if (entry.LogInfo.ScopeState != null) + var scopeProperties = entry.LogInfo.ScopeState.Properties; + foreach (var t in scopeProperties) { - var scopeProperties = entry.LogInfo.ScopeState.Properties; - for (var i = 0; i < scopeProperties.Length; i++) - { - if (scopeProperties[i].Key != "{OriginalFormat}") - { - propCount++; - } - } + if (t.Key != "{OriginalFormat}") scopePropCount++; } - } - - messagePackWriter.WriteMapHeader(propCount); - // LogInfo - var flag = IncludeProperties; - if ((flag & IncludeProperties.CategoryName) != 0) - { - messagePackWriter.WriteRaw(CategoryNameKey); - messagePackWriter.WriteString(entry.LogInfo.Category.Utf8Span); - } - if ((flag & IncludeProperties.LogLevel) != 0) - { - messagePackWriter.WriteRaw(LogLevelKey); - messagePackWriter.WriteRaw(EncodedLogLevel(entry.LogInfo.LogLevel)); - } - if ((flag & IncludeProperties.EventIdValue) != 0) - { - messagePackWriter.WriteRaw(EventIdKey); - messagePackWriter.WriteInt32(entry.LogInfo.EventId.Id); - } - if ((flag & IncludeProperties.EventIdName) != 0) - { - messagePackWriter.WriteRaw(EventIdNameKey); - messagePackWriter.Write(entry.LogInfo.EventId.Name); - } - if ((flag & IncludeProperties.Timestamp) != 0) - { - messagePackWriter.WriteRaw(TimestampKey); - MessagePackSerializerOptions.Resolver.GetFormatterWithVerify() - .Serialize(ref messagePackWriter, entry.LogInfo.Timestamp.Utc.DateTime, MessagePackSerializerOptions); - } - if ((flag & IncludeProperties.Exception) != 0) - { - if (entry.LogInfo.Exception is { } ex) + // flatten + if (PropertyNames.ScopeKeyValues == null) { - messagePackWriter.WriteRaw(ExceptionKey); - WriteException(ref messagePackWriter, ex); + propCount--; + propCount += scopePropCount; } } + } + + messagePackWriter.WriteMapHeader(propCount); - // Message - if ((flag & IncludeProperties.Message) != 0) + // LogInfo + var flag = IncludeProperties; + if ((flag & IncludeProperties.CategoryName) != 0) + { + messagePackWriter.WriteRaw(PropertyNames.Category.Utf8EncodedValue); + messagePackWriter.WriteString(entry.LogInfo.Category.Utf8Span); + } + if ((flag & IncludeProperties.LogLevel) != 0) + { + messagePackWriter.WriteRaw(PropertyNames.LogLevel.Utf8EncodedValue); + messagePackWriter.WriteRaw(PropertyNames.GetEncodedLogLevel(entry.LogInfo.LogLevel).Utf8EncodedValue); + } + if ((flag & IncludeProperties.EventIdValue) != 0) + { + messagePackWriter.WriteRaw(PropertyNames.EventId.Utf8EncodedValue); + messagePackWriter.WriteInt32(entry.LogInfo.EventId.Id); + } + if ((flag & IncludeProperties.EventIdName) != 0) + { + messagePackWriter.WriteRaw(PropertyNames.EventIdName.Utf8EncodedValue); + messagePackWriter.Write(entry.LogInfo.EventId.Name); + } + if ((flag & IncludeProperties.Timestamp) != 0) + { + messagePackWriter.WriteRaw(PropertyNames.Timestamp.Utf8EncodedValue); + MessagePackSerializerOptions.Resolver.GetFormatterWithVerify() + .Serialize(ref messagePackWriter, entry.LogInfo.Timestamp.Utc.DateTime, MessagePackSerializerOptions); + } + if ((flag & IncludeProperties.Exception) != 0) + { + if (entry.LogInfo.Exception is { } ex) { - messagePackWriter.Write(MessagePropertyName); - var buffer = GetThreadStaticBufferWriter(); - entry.ToString(buffer); - messagePackWriter.WriteString(buffer.WrittenSpan); + messagePackWriter.WriteRaw(PropertyNames.Exception.Utf8EncodedValue); + WriteException(ref messagePackWriter, ex); } + } + + // Message + if ((flag & IncludeProperties.Message) != 0) + { + messagePackWriter.WriteRaw(PropertyNames.Message.Utf8EncodedValue); + var buffer = GetThreadStaticBufferWriter(); + entry.ToString(buffer); + messagePackWriter.WriteString(buffer.WrittenSpan); + } - // Scope - if ((flag & IncludeProperties.ScopeKeyValues) != 0) + // Scope + if ((flag & IncludeProperties.ScopeKeyValues) != 0) + { + if (entry.LogInfo.ScopeState is { Properties: var scopeProperties }) { - if (entry.LogInfo.ScopeState is { Properties: var scopeProperties }) + // nested + if (PropertyNames.ScopeKeyValues != null) { - foreach (var t in scopeProperties) - { - var (key, value) = t; - // If `BeginScope(format, arg1, arg2)` style is used, the first argument `format` string is passed with this name - if (key == "{OriginalFormat}") continue; - - WriteKeyName(ref messagePackWriter, key); - if (value == null) - { - messagePackWriter.WriteNil(); - } - else - { - MessagePackSerializer.Serialize(value.GetType(), ref messagePackWriter, value, MessagePackSerializerOptions); - } - } + messagePackWriter.WriteRaw(PropertyNames.ScopeKeyValues.Value.Utf8EncodedValue); + messagePackWriter.WriteMapHeader(scopePropCount); } - } - - // Params - if ((flag & IncludeProperties.ParameterKeyValues) != 0) - { - for (var i = 0; i < entry.ParameterCount; i++) + + foreach (var t in scopeProperties) { - if (entry.IsSupportUtf8ParameterKey) + var (key, value) = t; + // If `BeginScope(format, arg1, arg2)` style is used, the first argument `format` string is passed with this name + if (key == "{OriginalFormat}") continue; + + WriteKeyName(ref messagePackWriter, key); + if (value == null) { - var key = entry.GetParameterKey(i); - messagePackWriter.Write(key); + messagePackWriter.WriteNil(); } else { - WriteKeyName(ref messagePackWriter, entry, i); + MessagePackSerializer.Serialize(value.GetType(), ref messagePackWriter, value, MessagePackSerializerOptions); } - - WriteParameterValue(ref messagePackWriter, entry, entry.GetParameterType(i), i); } } - - - messagePackWriter.Flush(); } - void WriteKeyName(ref MessagePackWriter messagePackWriter, TEntry entry, int parameterIndex) - where TEntry : IZLoggerEntry + // Params + if ((flag & IncludeProperties.ParameterKeyValues) != 0) { - if (entry.IsSupportUtf8ParameterKey) + // nested + if (PropertyNames.ParameterKeyValues != null) { - var key = entry.GetParameterKey(parameterIndex); - messagePackWriter.Write(key); + messagePackWriter.WriteRaw(PropertyNames.ParameterKeyValues.Value.Utf8EncodedValue); + messagePackWriter.WriteMapHeader(entry.ParameterCount); } - else + for (var i = 0; i < entry.ParameterCount; i++) { - var key = entry.GetParameterKeyAsString(parameterIndex); - WriteKeyName(ref messagePackWriter, key); + WriteKeyName(ref messagePackWriter, entry, i); + WriteParameterValue(ref messagePackWriter, entry, entry.GetParameterType(i), i); } } - void WriteKeyName(ref MessagePackWriter messagePackWriter, ReadOnlySpan keyName) + + messagePackWriter.Flush(); + } + + void WriteKeyName(ref MessagePackWriter messagePackWriter, TEntry entry, int parameterIndex) + where TEntry : IZLoggerEntry + { + if (entry.IsSupportUtf8ParameterKey) + { + var key = entry.GetParameterKey(parameterIndex); + messagePackWriter.WriteString(key); + } + else + { + var key = entry.GetParameterKeyAsString(parameterIndex); + WriteKeyName(ref messagePackWriter, key); + } + } + + void WriteKeyName(ref MessagePackWriter messagePackWriter, ReadOnlySpan keyName) + { + if (KeyNameMutator is { } mutator) { - if (KeyNameMutator is { } mutator) + if (mutator.IsSupportSlice) { - if (mutator.IsSupportSlice) - { - messagePackWriter.Write(mutator.Slice(keyName)); - } - else - { - var bufferSize = keyName.Length * 2; - while (!TryMutate(ref messagePackWriter, keyName, bufferSize)) - { - bufferSize *= 2; - } - } + messagePackWriter.Write(mutator.Slice(keyName)); } else { - messagePackWriter.Write(keyName); + var bufferSize = keyName.Length * 2; + while (!TryMutate(ref messagePackWriter, keyName, bufferSize)) + { + bufferSize *= 2; + } } + } + else + { + messagePackWriter.Write(keyName); + } - bool TryMutate(ref MessagePackWriter messagePackWriter, ReadOnlySpan source, int bufferSize) + bool TryMutate(ref MessagePackWriter messagePackWriter, ReadOnlySpan source, int bufferSize) + { + var buffer = ArrayPool.Shared.Rent(bufferSize); + try { - var buffer = ArrayPool.Shared.Rent(bufferSize); - try + if (mutator.TryMutate(source, buffer, out var written)) { - if (mutator.TryMutate(source, buffer, out var written)) - { - messagePackWriter.Write(buffer.AsSpan(0, written)); - return true; - } - } - finally - { - ArrayPool.Shared.Return(buffer); + messagePackWriter.Write(buffer.AsSpan(0, written)); + return true; } - return false; } + finally + { + ArrayPool.Shared.Return(buffer); + } + return false; } + } - static void WriteException(ref MessagePackWriter messagePackWriter, Exception? ex) + void WriteException(ref MessagePackWriter messagePackWriter, Exception? ex) + { + while (true) { if (ex == null) { @@ -263,85 +286,86 @@ static void WriteException(ref MessagePackWriter messagePackWriter, Exception? e messagePackWriter.WriteMapHeader(4); - messagePackWriter.WriteRaw(NameKey); + messagePackWriter.WriteRaw(PropertyNames.ExceptionName.Utf8EncodedValue); messagePackWriter.Write(ex.GetType().FullName); - messagePackWriter.WriteRaw(MessageKey); + messagePackWriter.WriteRaw(PropertyNames.ExceptionMessage.Utf8EncodedValue); messagePackWriter.Write(ex.Message); - messagePackWriter.WriteRaw(StackTraceKey); + messagePackWriter.WriteRaw(PropertyNames.ExceptionStackTrace.Utf8EncodedValue); messagePackWriter.Write(ex.StackTrace); - messagePackWriter.WriteRaw(InnerExceptionKey); - WriteException(ref messagePackWriter, ex.InnerException); + messagePackWriter.WriteRaw(PropertyNames.ExceptionInnerException.Utf8EncodedValue); + ex = ex.InnerException; + } + } + + void WriteParameterValue(ref MessagePackWriter messagePackWriter, TEntry entry, Type type, int index) + where TEntry : IZLoggerEntry + { + var code = Type.GetTypeCode(type); + switch (code) + { + case TypeCode.Boolean: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.Char: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.SByte: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.Byte: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.Int16: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.UInt16: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.Int32: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.UInt32: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.Int64: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.UInt64: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.Single: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.Double: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; + case TypeCode.DateTime: + return; + case TypeCode.String: + messagePackWriter.Write(entry.GetParameterValue(index)); + return; } - void WriteParameterValue(ref MessagePackWriter messagePackWriter, TEntry entry, Type type, int index) - where TEntry : IZLoggerEntry + if (type.IsValueType) { - var code = Type.GetTypeCode(type); - switch (code) + if (type == typeof(DateTime)) { - case TypeCode.Boolean: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.Char: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.SByte: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.Byte: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.Int16: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.UInt16: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.Int32: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.UInt32: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.Int64: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.UInt64: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.Single: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.Double: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; - case TypeCode.DateTime: - return; - case TypeCode.String: - messagePackWriter.Write(entry.GetParameterValue(index)); - return; + var value = entry.GetParameterValue(index); + MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); } - - if (type.IsValueType) + else if (type == typeof(DateTimeOffset)) { - if (type == typeof(DateTime)) - { - var value = entry.GetParameterValue(index); - MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); - } - else if (type == typeof(DateTimeOffset)) - { - var value = entry.GetParameterValue(index); - MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); - } - else if (type == typeof(TimeSpan)) - { - var value = entry.GetParameterValue(index); - MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); - } + var value = entry.GetParameterValue(index); + MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); + } + else if (type == typeof(TimeSpan)) + { + var value = entry.GetParameterValue(index); + MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); + } #if NET6_OR_GRATER else if (type == typeof(TimeOnly)) { @@ -354,180 +378,156 @@ void WriteParameterValue(ref MessagePackWriter messagePackWriter, TEntry MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); } #endif - else if (type == typeof(Guid)) - { - var value = entry.GetParameterValue(index); - MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); - } - else if (Nullable.GetUnderlyingType(type) is { } underlyingType) + else if (type == typeof(Guid)) + { + var value = entry.GetParameterValue(index); + MessagePackSerializer.Serialize(type, ref messagePackWriter, value, MessagePackSerializerOptions); + } + else if (Nullable.GetUnderlyingType(type) is { } underlyingType) + { + code = Type.GetTypeCode(underlyingType); + switch (code) { - code = Type.GetTypeCode(underlyingType); - switch (code) + case TypeCode.Boolean: { - case TypeCode.Boolean: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.Char: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.SByte: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.Byte: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.Int16: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.UInt16: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.Int32: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.UInt32: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.Int64: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.UInt64: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.Single: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.Double: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } - case TypeCode.DateTime: - { - var nullableValue = entry.GetParameterValue(index); - if (nullableValue.HasValue) - messagePackWriter.Write(nullableValue.Value); - else - messagePackWriter.WriteNil(); - return; - } + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.Char: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.SByte: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.Byte: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.Int16: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.UInt16: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.Int32: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.UInt32: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.Int64: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.UInt64: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.Single: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.Double: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; + } + case TypeCode.DateTime: + { + var nullableValue = entry.GetParameterValue(index); + if (nullableValue.HasValue) + messagePackWriter.Write(nullableValue.Value); + else + messagePackWriter.WriteNil(); + return; } } } - - var boxedValue = entry.GetParameterValue(index); - if (boxedValue == null) - { - messagePackWriter.WriteNil(); - } - else - { - MessagePackSerializer.Serialize(type, ref messagePackWriter, boxedValue, MessagePackSerializerOptions); - } } - static byte[] EncodedLogLevel(LogLevel logLevel) + var boxedValue = entry.GetParameterValue(index); + if (boxedValue == null) { - switch (logLevel) - { - case LogLevel.Trace: - return Trace; - case LogLevel.Debug: - return Debug; - case LogLevel.Information: - return Information; - case LogLevel.Warning: - return Warning; - case LogLevel.Error: - return Error; - case LogLevel.Critical: - return Critical; - case LogLevel.None: - return None; - default: - throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null); - } + messagePackWriter.WriteNil(); } - - static ArrayBufferWriter GetThreadStaticBufferWriter() + else { - threadStaticBufferWriter ??= new ArrayBufferWriter(); + MessagePackSerializer.Serialize(type, ref messagePackWriter, boxedValue, MessagePackSerializerOptions); + } + } + + static ArrayBufferWriter GetThreadStaticBufferWriter() + { + threadStaticBufferWriter ??= new ArrayBufferWriter(); #if NET8_0_OR_GREATER threadStaticBufferWriter.ResetWrittenCount(); #else - threadStaticBufferWriter.Clear(); + threadStaticBufferWriter.Clear(); #endif - return threadStaticBufferWriter; - } + return threadStaticBufferWriter; } } diff --git a/src/ZLogger.MessagePack/ZLogger.MessagePack.csproj b/src/ZLogger.MessagePack/ZLogger.MessagePack.csproj index f0880b39..da53b50b 100644 --- a/src/ZLogger.MessagePack/ZLogger.MessagePack.csproj +++ b/src/ZLogger.MessagePack/ZLogger.MessagePack.csproj @@ -11,6 +11,7 @@ + diff --git a/src/ZLogger.MessagePack/ZLoggerOptionsMessagePackExtensions.cs b/src/ZLogger.MessagePack/ZLoggerOptionsMessagePackExtensions.cs new file mode 100644 index 00000000..5b0c9a7d --- /dev/null +++ b/src/ZLogger.MessagePack/ZLoggerOptionsMessagePackExtensions.cs @@ -0,0 +1,17 @@ +using System; +using ZLogger.MessagePack; + +namespace ZLogger; + +public static class ZLoggerOptionsMessagePackExtensions +{ + public static ZLoggerOptions UseMessagePackFormatter(this ZLoggerOptions options, Action? configure = null) + { + return options.UseFormatter(() => + { + var formatter = new MessagePackZLoggerFormatter(); + configure?.Invoke(formatter); + return formatter; + }); + } +} \ No newline at end of file diff --git a/src/ZLogger/Formatters/SystemTextJsonZLoggerFormatter.cs b/src/ZLogger/Formatters/SystemTextJsonZLoggerFormatter.cs index d384a8f5..bc0bb839 100644 --- a/src/ZLogger/Formatters/SystemTextJsonZLoggerFormatter.cs +++ b/src/ZLogger/Formatters/SystemTextJsonZLoggerFormatter.cs @@ -66,7 +66,7 @@ public record JsonPropertyNames( JsonEncodedText LogLevelNone ) { - public static readonly JsonPropertyNames Default = new JsonPropertyNames( + public static readonly JsonPropertyNames Default = new( Category: JsonEncodedText.Encode(nameof(LogInfo.Category)), Timestamp: JsonEncodedText.Encode(nameof(LogInfo.Timestamp)), LogLevel: JsonEncodedText.Encode(nameof(LogInfo.LogLevel)), diff --git a/tests/ZLogger.MessagePack.Tests/FormatterTest.cs b/tests/ZLogger.MessagePack.Tests/FormatterTest.cs index f1350965..cb52694e 100644 --- a/tests/ZLogger.MessagePack.Tests/FormatterTest.cs +++ b/tests/ZLogger.MessagePack.Tests/FormatterTest.cs @@ -1,199 +1,345 @@ using System; using FluentAssertions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Time.Testing; using Xunit; -namespace ZLogger.MessagePack.Tests +namespace ZLogger.MessagePack.Tests; + +public class FormatterTest { - public class FormatterTest - { - TestProcessor processor; - ILogger logger; + TestProcessor processor; + ILogger logger; + readonly FakeTimeProvider timeProvider = new(); + readonly DateTimeOffset now = DateTimeOffset.UtcNow; - public FormatterTest() + public FormatterTest() + { + timeProvider.SetUtcNow(now); + + processor = new TestProcessor(new MessagePackZLoggerFormatter { - var options = new ZLoggerOptions(); - - options.UseMessagePackFormatter(); - - processor = new TestProcessor(options); + IncludeProperties = IncludeProperties.Default + }); - var loggerFactory = LoggerFactory.Create(x => + var loggerFactory = LoggerFactory.Create(x => + { + x.SetMinimumLevel(LogLevel.Debug); + x.AddZLoggerLogProcessor(options => { - x.SetMinimumLevel(LogLevel.Debug); - x.AddZLoggerLogProcessor(processor); + options.TimeProvider = timeProvider; + return processor; }); - logger = loggerFactory.CreateLogger("test"); + }); + logger = loggerFactory.CreateLogger("test"); + } + + [Fact] + public void PlainMessage() + { + logger.ZLogInformation(new EventId(1, "HOGE"), $"AAA {111} BBB {"Hello"}"); + + var msgpack = processor.Dequeue(); + ((string)msgpack["Category"]).Should().Be("test"); + ((string)msgpack["LogLevel"]).Should().Be("Information"); + ((string)msgpack["Message"]).Should().Be("AAA 111 BBB Hello"); + ((DateTimeOffset)msgpack["Timestamp"]).Should().BeOnOrAfter(now); + ((bool)msgpack.ContainsKey("Exception")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("Payload")).Should().BeFalse(); + } + + [Fact] + public void WithException() + { + try + { + throw new TestException("DAME"); + } + catch (Exception ex) + { + logger.ZLogError(new EventId(1, "NG"), ex, $"DAMEDA 111"); } + + var msgpack = processor.Dequeue(); + ((string)msgpack["Category"]).Should().Be("test"); + ((string)msgpack["LogLevel"]).Should().Be("Error"); + ((string)msgpack["Message"]).Should().Be("DAMEDA 111"); + ((DateTimeOffset)msgpack["Timestamp"]).Should().BeOnOrAfter(now); + ((string)msgpack["Exception"]["Name"]).Should().Be("ZLogger.MessagePack.Tests.TestException"); + ((string)msgpack["Exception"]["Message"]).Should().Be("DAME"); + ((string)msgpack["Exception"]["StackTrace"]).Should().NotBeEmpty(); + ((string)msgpack["Exception"]["InnerException"]).Should().BeNull(); + } - [Fact] - public void PlainMessage() + [Fact] + public void WithExceptionWithInnerException() + { + try { - var now = DateTime.UtcNow; - logger.ZLogInformation(new EventId(1, "HOGE"), $"AAA {111} BBB {"Hello"}"); - - var msgpack = processor.Dequeue(); - ((string)msgpack["CategoryName"]).Should().Be("test"); - ((string)msgpack["LogLevel"]).Should().Be("Information"); - ((string)msgpack["Message"]).Should().Be("AAA 111 BBB Hello"); - ((DateTime)msgpack["Timestamp"]).Should().BeOnOrAfter(now); - ((bool)msgpack.ContainsKey("Exception")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("Payload")).Should().BeFalse(); + throw new TestException("DAME!", new TestException("INNER!")); + } + catch (Exception ex) + { + logger.ZLogError(new EventId(1, "NG"), ex, $"DAMEDA 111"); } + + var msgpack = processor.Dequeue(); + ((string)msgpack["Category"]).Should().Be("test"); + ((string)msgpack["LogLevel"]).Should().Be("Error"); + ((string)msgpack["Message"]).Should().Be("DAMEDA 111"); + ((DateTimeOffset)msgpack["Timestamp"]).Should().BeOnOrAfter(now); + ((string)msgpack["Exception"]["Name"]).Should().Be("ZLogger.MessagePack.Tests.TestException"); + ((string)msgpack["Exception"]["Message"]).Should().Be("DAME!"); + ((string)msgpack["Exception"]["StackTrace"]).Should().NotBeEmpty(); + ((string)msgpack["Exception"]["InnerException"]["Name"]).Should().Be("ZLogger.MessagePack.Tests.TestException"); + ((string)msgpack["Exception"]["InnerException"]["Message"]).Should().Be("INNER!"); + } + + [Fact] + public void WithParameters() + { + var payload = new TestPayload { X = 999 }; + var x = 100; + int? y = null; + logger.ZLogInformation(new EventId(1, "HOGE"), $"UMU {payload} {x} {y}"); + + var msgpack = processor.Dequeue(); + ((string)msgpack["Category"]).Should().Be("test"); + ((string)msgpack["LogLevel"]).Should().Be("Information"); + ((DateTimeOffset)msgpack["Timestamp"]).Should().BeOnOrAfter(now); + ((int?)msgpack["x"]).Should().Be(100); + ((int?)msgpack["y"]).Should().Be(null); + ((int)msgpack["payload"]["x"]).Should().Be(999); + ((bool)msgpack.ContainsKey("Exception")).Should().BeFalse(); + } - [Fact] - public void WithException() + [Fact] + public void LowercaseMutator() + { + processor = new TestProcessor(new MessagePackZLoggerFormatter { - var now = DateTime.UtcNow; - try + KeyNameMutator = KeyNameMutator.LowerFirstCharacter + }); + + var loggerFactory = LoggerFactory.Create(x => + { + x.SetMinimumLevel(LogLevel.Debug); + x.AddZLoggerLogProcessor(options => { - throw new TestException("DAME"); - } - catch (Exception ex) + options.TimeProvider = timeProvider; + return processor; + }); + }); + logger = loggerFactory.CreateLogger("test"); + + var XyzAbc = 100; + var fOo = 200; + logger.ZLogInformation($"AAA {XyzAbc} {fOo}"); + + var msgpack = processor.Dequeue(); + ((string)msgpack["Category"]).Should().Be("test"); + ((string)msgpack["LogLevel"]).Should().Be("Information"); + ((string)msgpack["Message"]).Should().Be("AAA 100 200"); + ((int)msgpack["xyzAbc"]).Should().Be(100); + ((int)msgpack["fOo"]).Should().Be(200); + } + + [Fact] + public void ExcludeLogInfoProperties() + { + processor = new TestProcessor(new MessagePackZLoggerFormatter + { + IncludeProperties = IncludeProperties.LogLevel | + IncludeProperties.Timestamp | + IncludeProperties.EventIdValue + }); + + var loggerFactory = LoggerFactory.Create(x => + { + x.SetMinimumLevel(LogLevel.Debug); + x.AddZLoggerLogProcessor(options => { - logger.ZLogError(new EventId(1, "NG"), ex, $"DAMEDA 111"); - } + options.TimeProvider = timeProvider; + return processor; + }); + }); + logger = loggerFactory.CreateLogger("test"); + + logger.ZLogInformation(new EventId(1, "TEST"), $"HELLO!"); - var msgpack = processor.Dequeue(); - ((string)msgpack["CategoryName"]).Should().Be("test"); - ((string)msgpack["LogLevel"]).Should().Be("Error"); - ((string)msgpack["Message"]).Should().Be("DAMEDA 111"); - ((DateTime)msgpack["Timestamp"]).Should().BeOnOrAfter(now); - ((string)msgpack["Exception"]["Name"]).Should().Be("ZLogger.MessagePack.Tests.TestException"); - ((string)msgpack["Exception"]["Message"]).Should().Be("DAME"); - ((string)msgpack["Exception"]["StackTrace"]).Should().NotBeEmpty(); - ((string)msgpack["Exception"]["InnerException"]).Should().BeNull(); - } + var msgpack = processor.Dequeue(); + ((string)msgpack["LogLevel"]).Should().Be("Information"); + ((int)msgpack["EventId"]).Should().Be(1); + ((DateTimeOffset)msgpack["Timestamp"]).Should().BeOnOrAfter(now); + ((bool)msgpack.ContainsKey("Exception")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("CategoryName")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("EventIdName")).Should().BeFalse(); + } - [Fact] - public void WithExceptionWithInnerException() + [Fact] + public void ExcludeAllLogInfo() + { + processor = new TestProcessor(new MessagePackZLoggerFormatter { - var now = DateTime.UtcNow; - try - { - throw new TestException("DAME!", new TestException("INNER!")); - } - catch (Exception ex) + IncludeProperties = IncludeProperties.None + }); + + var loggerFactory = LoggerFactory.Create(x => x + .SetMinimumLevel(LogLevel.Debug) + .AddZLoggerLogProcessor(options => { - logger.ZLogError(new EventId(1, "NG"), ex, $"DAMEDA 111"); - } + options.TimeProvider = timeProvider; + return processor; + })); + logger = loggerFactory.CreateLogger("test"); + + logger.ZLogInformation(new EventId(1, "TEST"), $"HELLO!"); - var msgpack = processor.Dequeue(); - ((string)msgpack["CategoryName"]).Should().Be("test"); - ((string)msgpack["LogLevel"]).Should().Be("Error"); - ((string)msgpack["Message"]).Should().Be("DAMEDA 111"); - ((DateTime)msgpack["Timestamp"]).Should().BeOnOrAfter(now); - ((string)msgpack["Exception"]["Name"]).Should().Be("ZLogger.MessagePack.Tests.TestException"); - ((string)msgpack["Exception"]["Message"]).Should().Be("DAME!"); - ((string)msgpack["Exception"]["StackTrace"]).Should().NotBeEmpty(); - ((string)msgpack["Exception"]["InnerException"]["Name"]).Should().Be("ZLogger.MessagePack.Tests.TestException"); - ((string)msgpack["Exception"]["InnerException"]["Message"]).Should().Be("INNER!"); - } + var msgpack = processor.Dequeue(); + ((bool)msgpack.ContainsKey("LogLevel")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("Timestamp")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("EventId")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("EventIdName")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("Exception")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("CategoryName")).Should().BeFalse(); + ((bool)msgpack.ContainsKey("EventIdName")).Should().BeFalse(); + } - [Fact] - public void WithParameters() - { - var now = DateTime.UtcNow; - var payload = new TestPayload { X = 999 }; - var x = 100; - int? y = null; - logger.ZLogInformation(new EventId(1, "HOGE"), $"UMU {payload} {x} {y}"); - - var msgpack = processor.Dequeue(); - ((string)msgpack["CategoryName"]).Should().Be("test"); - ((string)msgpack["LogLevel"]).Should().Be("Information"); - ((DateTime)msgpack["Timestamp"]).Should().BeOnOrAfter(now); - ((int?)msgpack["x"]).Should().Be(100); - ((int?)msgpack["y"]).Should().Be(null); - ((int)msgpack["payload"]["x"]).Should().Be(999); - ((bool)msgpack.ContainsKey("Exception")).Should().BeFalse(); - } + [Fact] + public void CustomPropertyNames() + { + var formatter = new MessagePackZLoggerFormatter + { + PropertyNames = MessagePackPropertyNames.Default with + { + Timestamp = MessagePackEncodedText.Encode("time"), + LogLevel = MessagePackEncodedText.Encode("level"), + LogLevelInformation = MessagePackEncodedText.Encode("INFO"), + } + }; - [Fact] - public void LowercaseMutator() + processor = new TestProcessor(formatter); + + var loggerFactory = LoggerFactory.Create(x => { - var options = new ZLoggerOptions().UseMessagePackFormatter(formatter => + x.SetMinimumLevel(LogLevel.Debug); + x.AddZLoggerLogProcessor(options => { - formatter.KeyNameMutator = KeyNameMutator.LowerFirstCharacter; + options.TimeProvider = timeProvider; + return processor; }); - - processor = new TestProcessor(options); - - var loggerFactory = LoggerFactory.Create(x => + }); + logger = loggerFactory.CreateLogger("test"); + + logger.ZLogInformation($"HELLO!"); + + var msg = processor.Dequeue(); + ((string)msg["level"]).Should().Be("INFO"); + } + + [Fact] + public void NestedParameterKeyValues() + { + var formatter = new MessagePackZLoggerFormatter + { + PropertyNames = MessagePackPropertyNames.Default with { - x.SetMinimumLevel(LogLevel.Debug); - x.AddZLoggerLogProcessor(processor); - }); - logger = loggerFactory.CreateLogger("test"); - - var XyzAbc = 100; - var fOo = 200; - logger.ZLogInformation($"AAA {XyzAbc} {fOo}"); - - var msgpack = processor.Dequeue(); - ((string)msgpack["CategoryName"]).Should().Be("test"); - ((string)msgpack["LogLevel"]).Should().Be("Information"); - ((string)msgpack["Message"]).Should().Be("AAA 100 200"); - ((int)msgpack["xyzAbc"]).Should().Be(100); - ((int)msgpack["fOo"]).Should().Be(200); - } + ParameterKeyValues = MessagePackEncodedText.Encode("attributes"), + ScopeKeyValues = MessagePackEncodedText.Encode("scope") + } + }; + + processor = new TestProcessor(formatter); - [Fact] - public void ExcludeLogInfoProperties() + var loggerFactory = LoggerFactory.Create(x => { - var options = new ZLoggerOptions().UseMessagePackFormatter(formatter => + x.SetMinimumLevel(LogLevel.Debug); + x.AddZLoggerLogProcessor(options => { - formatter.IncludeProperties = IncludeProperties.LogLevel | - IncludeProperties.Timestamp | - IncludeProperties.EventIdValue; + options.TimeProvider = timeProvider; + return processor; }); + }); + logger = loggerFactory.CreateLogger("test"); - processor = new TestProcessor(options); - var loggerFactory = LoggerFactory.Create(x => + logger.ZLogInformation($"{456:@y}"); + + var msg = processor.Dequeue(); + ((string)msg["Message"]).Should().Be("456"); + ((int)msg["attributes"]["y"]).Should().Be(456); + } + + [Fact] + public void NestedScopeKeyValues() + { + var formatter = new MessagePackZLoggerFormatter + { + PropertyNames = MessagePackPropertyNames.Default with { - x.SetMinimumLevel(LogLevel.Debug); - x.AddZLoggerLogProcessor(processor); - }); - logger = loggerFactory.CreateLogger("test"); - - var now = DateTime.UtcNow; - logger.ZLogInformation(new EventId(1, "TEST"), $"HELLO!"); - - var msgpack = processor.Dequeue(); - ((string)msgpack["LogLevel"]).Should().Be("Information"); - ((int)msgpack["EventId"]).Should().Be(1); - ((DateTime)msgpack["Timestamp"]).Should().BeOnOrAfter(now); - ((bool)msgpack.ContainsKey("Exception")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("CategoryName")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("EventIdName")).Should().BeFalse(); - } + ParameterKeyValues = MessagePackEncodedText.Encode("attributes"), + ScopeKeyValues = MessagePackEncodedText.Encode("scope") + } + }; - [Fact] - public void ExcludeAllLogInfo() + processor = new TestProcessor(formatter); + + var loggerFactory = LoggerFactory.Create(x => { - var options = new ZLoggerOptions().UseMessagePackFormatter(formatter => + x.SetMinimumLevel(LogLevel.Debug); + x.AddZLoggerLogProcessor(options => { - formatter.IncludeProperties = IncludeProperties.None; + options.IncludeScopes = true; + options.TimeProvider = timeProvider; + return processor; }); + }); + logger = loggerFactory.CreateLogger("test"); + + using (logger.BeginScope("{X}", 123)) + { + logger.ZLogInformation($"{456:@y}"); + } + + var msg = processor.Dequeue(); + ((string)msg["Message"]).Should().Be("456"); + ((int)msg["scope"]["X"]).Should().Be(123); + ((int)msg["attributes"]["y"]).Should().Be(456); + } + + [Fact] + public void WithSourceGenerator() + { + int IntValue = 123; + int? IntNullableValue = 123; + int? IntNull = null; - processor = new TestProcessor(options); + float FloatValue = 123.456f; + float? FloatNullableValue = 123.456f; + float? FloatNull = null; - var loggerFactory = LoggerFactory.Create(x => x - .SetMinimumLevel(LogLevel.Debug) - .AddZLoggerLogProcessor(processor)); - logger = loggerFactory.CreateLogger("test"); - - logger.ZLogInformation(new EventId(1, "TEST"), $"HELLO!"); + var StringValue = "Hello Hello Hello"; - var msgpack = processor.Dequeue(); - ((bool)msgpack.ContainsKey("LogLevel")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("Timestamp")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("EventId")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("EventIdName")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("Exception")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("CategoryName")).Should().BeFalse(); - ((bool)msgpack.ContainsKey("EventIdName")).Should().BeFalse(); - } - } + logger.ZLogInformation($"{IntValue} {IntNullableValue} {IntNull} {FloatValue} {FloatNullableValue} {FloatNull} {StringValue}"); + var msg1 = processor.DequeueAsRaw(); + + logger.Log1(IntValue, IntNullableValue, IntNull, FloatValue, FloatNullableValue, FloatNull, StringValue); + var msg2 = processor.DequeueAsRaw(); + + msg1.AsSpan().SequenceEqual(msg2).Should().BeTrue(); + } } + +static partial class GeneratedLog +{ + [ZLoggerMessage(LogLevel.Information, "{IntValue} {IntNullableValue} {IntNull} {FloatValue} {FloatNullableValue} {FloatNull} {StringValue}")] + public static partial void Log1( + this ILogger logger, + int intValue, + int? intNullableValue, + int? intNull, + float floatValue, + float? floatNullableValue, + float? floatNull, + string stringValue); +} diff --git a/tests/ZLogger.MessagePack.Tests/MessagePackEncodedTextTest.cs b/tests/ZLogger.MessagePack.Tests/MessagePackEncodedTextTest.cs new file mode 100644 index 00000000..05a667d5 --- /dev/null +++ b/tests/ZLogger.MessagePack.Tests/MessagePackEncodedTextTest.cs @@ -0,0 +1,27 @@ +using FluentAssertions; +using MessagePack; +using Xunit; + +namespace ZLogger.MessagePack.Tests; + +public class MessagePackEncodedTextTest +{ + [Fact] + public void Encode() + { + var fixStrEncoded = MessagePackSerializer.Serialize("CategoryName"); + MessagePackEncodedText.Encode("CategoryName").Utf8EncodedValue.ToArray().Should().BeEquivalentTo(fixStrEncoded); + + var str8 = new string('a', 512); + var str8Encoded = MessagePackSerializer.Serialize(str8); + MessagePackEncodedText.Encode(str8).Utf8EncodedValue.ToArray().Should().BeEquivalentTo(str8Encoded); + + var str16 = new string('a', 65535); + var str16Encoded = MessagePackSerializer.Serialize(str16); + MessagePackEncodedText.Encode(str16).Utf8EncodedValue.ToArray().Should().BeEquivalentTo(str16Encoded); + + var str32 = new string('a', 65536); + var str32Encoded = MessagePackSerializer.Serialize(str32); + MessagePackEncodedText.Encode(str32).Utf8EncodedValue.ToArray().Should().BeEquivalentTo(str32Encoded); + } +} \ No newline at end of file diff --git a/tests/ZLogger.MessagePack.Tests/ScopeTest.cs b/tests/ZLogger.MessagePack.Tests/ScopeTest.cs index 9194633d..a4fece27 100644 --- a/tests/ZLogger.MessagePack.Tests/ScopeTest.cs +++ b/tests/ZLogger.MessagePack.Tests/ScopeTest.cs @@ -3,104 +3,97 @@ using FluentAssertions; using Xunit; -namespace ZLogger.MessagePack.Tests +namespace ZLogger.MessagePack.Tests; + +public class ScopeTest { - public class ScopeTest + TestProcessor processor; + ILogger logger; + + public ScopeTest() { - TestProcessor processor; - ILogger logger; + processor = new TestProcessor(new MessagePackZLoggerFormatter()); - public ScopeTest() + var loggerFactory = LoggerFactory.Create(x => { - var options = new ZLoggerOptions - { - IncludeScopes = true - }; - options.UseMessagePackFormatter(); - - processor = new TestProcessor(options); - - var loggerFactory = LoggerFactory.Create(x => + x.SetMinimumLevel(LogLevel.Debug); + x.AddZLoggerLogProcessor(options => { - x.SetMinimumLevel(LogLevel.Debug); - x.AddZLoggerLogProcessor(options => - { - options.IncludeScopes = true; - return processor; - }); + options.IncludeScopes = true; + return processor; }); - logger = loggerFactory.CreateLogger("test"); - } + }); + logger = loggerFactory.CreateLogger("test"); + } - [Fact] - public void BeginScope_FormattedLogValuesToMessagePack() + [Fact] + public void BeginScope_FormattedLogValuesToMessagePack() + { + using (logger.BeginScope("({X}, {Y})", 111, null)) { - using (logger.BeginScope("({X}, {Y})", 111, null)) - { - var a = 333; - var b = 444; - logger.ZLogInformation($"FooBar{a} NanoNano{b}"); - } + var a = 333; + var b = 444; + logger.ZLogInformation($"FooBar{a} NanoNano{b}"); + } - var msgpack = processor.Dequeue(); - ((string)msgpack["Message"]).Should().Be("FooBar333 NanoNano444"); - ((int)msgpack["X"]).Should().Be(111); - ((string?)msgpack["Y"]).Should().Be(null); + var msgpack = processor.Dequeue(); + ((string)msgpack["Message"]).Should().Be("FooBar333 NanoNano444"); + ((int)msgpack["X"]).Should().Be(111); + ((string?)msgpack["Y"]).Should().Be(null); - } + } - [Fact] - public void BeginScope_KeyValuePairToJson() + [Fact] + public void BeginScope_KeyValuePair() + { + using (logger.BeginScope(new KeyValuePair("Hoge", "AAA"))) { - using (logger.BeginScope(new KeyValuePair("Hoge", "AAA"))) - { - var a = 100; - var b = 200; - logger.ZLogInformation($"FooBar{a} NanoNano{b}"); - } - - var msgpack = processor.Dequeue(); - ((string)msgpack["Message"]).Should().Be("FooBar100 NanoNano200"); - ((string)msgpack["Hoge"]).Should().Be("AAA"); + var a = 100; + var b = 200; + logger.ZLogInformation($"FooBar{a} NanoNano{b}"); } - [Fact] - public void BeginScope_AnyScopeValueToJson() - { - using (logger.BeginScope(new TestPayload { X = 999 })) - { - var a = 100; - var b = 200; - logger.ZLogInformation($"FooBar{a} NanoNano{b}"); - } + var msgpack = processor.Dequeue(); + ((string)msgpack["Message"]).Should().Be("FooBar100 NanoNano200"); + ((string)msgpack["Hoge"]).Should().Be("AAA"); + } - var msgpack = processor.Dequeue(); - ((string)msgpack["Message"]).Should().Be("FooBar100 NanoNano200"); - ((int)msgpack["Scope"]["x"]).Should().Be(999); + [Fact] + public void BeginScope_AnyScopeValue() + { + using (logger.BeginScope(new TestPayload { X = 999 })) + { + var a = 100; + var b = 200; + logger.ZLogInformation($"FooBar{a} NanoNano{b}"); } - [Fact] - public void BeginScope_NestedToJson() + var msgpack = processor.Dequeue(); + ((string)msgpack["Message"]).Should().Be("FooBar100 NanoNano200"); + ((int)msgpack["Scope"]["x"]).Should().Be(999); + } + + [Fact] + public void BeginScope_Nested() + { + using (logger.BeginScope("A={A}", 100)) { - using (logger.BeginScope("A={A}", 100)) - { - logger.ZLogInformation($"Message 1"); + logger.ZLogInformation($"Message 1"); - using (logger.BeginScope("B={B}", 200)) - { - logger.ZLogInformation($"Message 2"); - } + using (logger.BeginScope("B={B}", 200)) + { + logger.ZLogInformation($"Message 2"); } + } - var msgpack1 = processor.Dequeue(); - var msgpack2 = processor.Dequeue(); - ((string)msgpack1["Message"]).Should().Be("Message 1"); - ((int)msgpack1["A"]).Should().Be(100); - ((bool)msgpack1.ContainsKey("B")).Should().BeFalse(); + var msgpack1 = processor.Dequeue(); + var msgpack2 = processor.Dequeue(); + ((string)msgpack1["Message"]).Should().Be("Message 1"); + ((int)msgpack1["A"]).Should().Be(100); + ((bool)msgpack1.ContainsKey("B")).Should().BeFalse(); - ((string)msgpack2["Message"]).Should().Be("Message 2"); - ((int)msgpack2["A"]).Should().Be(100); - ((int)msgpack2["B"]).Should().Be(200); - } + ((string)msgpack2["Message"]).Should().Be("Message 2"); + ((int)msgpack2["A"]).Should().Be(100); + ((int)msgpack2["B"]).Should().Be(200); } -} +} \ No newline at end of file diff --git a/tests/ZLogger.MessagePack.Tests/TestHelpers.cs b/tests/ZLogger.MessagePack.Tests/TestHelpers.cs index da0c2948..d1e93981 100644 --- a/tests/ZLogger.MessagePack.Tests/TestHelpers.cs +++ b/tests/ZLogger.MessagePack.Tests/TestHelpers.cs @@ -25,19 +25,17 @@ public TestException(string message, Exception innerException) : base(message, i } } - class TestProcessor : IAsyncLogProcessor + class TestProcessor(IZLoggerFormatter formatter) : IAsyncLogProcessor { - public Queue EntryMessages = new(); - readonly ZLoggerOptions options; - readonly IZLoggerFormatter formatter; + public readonly Queue EntryMessages = new(); readonly ArrayBufferWriter bufferWriter = new(); - public dynamic Dequeue() => EntryMessages.Dequeue(); + public byte[] DequeueAsRaw() => EntryMessages.Dequeue(); - public TestProcessor(ZLoggerOptions options) + public dynamic Dequeue() { - this.options = options; - formatter = options.CreateFormatter(); + var data = EntryMessages.Dequeue(); + return MessagePackSerializer.Deserialize(data, ContractlessStandardResolver.Options); } public ValueTask DisposeAsync() @@ -48,9 +46,8 @@ public ValueTask DisposeAsync() public void Post(IZLoggerEntry log) { log.FormatUtf8(bufferWriter, formatter); - var entry = MessagePackSerializer.Deserialize(bufferWriter.WrittenMemory, ContractlessStandardResolver.Options); + EntryMessages.Enqueue(bufferWriter.WrittenMemory.ToArray()); bufferWriter.Clear(); - EntryMessages.Enqueue(entry); } } } diff --git a/tests/ZLogger.MessagePack.Tests/ZLogger.MessagePack.Tests.csproj b/tests/ZLogger.MessagePack.Tests/ZLogger.MessagePack.Tests.csproj index 7b658136..68bf1d80 100644 --- a/tests/ZLogger.MessagePack.Tests/ZLogger.MessagePack.Tests.csproj +++ b/tests/ZLogger.MessagePack.Tests/ZLogger.MessagePack.Tests.csproj @@ -9,6 +9,7 @@ + @@ -20,5 +21,10 @@ + + + Analyzer + false +