File: LogBuffering\SerializedLogRecordFactory.cs
Web Access
Project: src\src\Shared\Shared.csproj (Shared)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Shared.Pools;
 
namespace Microsoft.Extensions.Diagnostics.Buffering;
 
internal static class SerializedLogRecordFactory
{
    private static readonly ObjectPool<List<KeyValuePair<string, object?>>> _attributesPool =
        PoolFactory.CreateListPool<KeyValuePair<string, object?>>();
 
    private static readonly int _serializedLogRecordSize = Unsafe.SizeOf<SerializedLogRecord>();
 
    public static SerializedLogRecord Create(
        LogLevel logLevel,
        EventId eventId,
        DateTimeOffset timestamp,
        IReadOnlyList<KeyValuePair<string, object?>> attributes,
        Exception? exception,
        string formattedMessage)
    {
        int sizeInBytes = _serializedLogRecordSize;
        List<KeyValuePair<string, object?>> serializedAttributes = _attributesPool.Get();
        for (int i = 0; i < attributes.Count; i++)
        {
            string key = attributes[i].Key;
            string value = attributes[i].Value?.ToString() ?? string.Empty;
 
            // deliberately not counting the size of the key,
            // as it is constant strings in the vast majority of cases
 
            sizeInBytes += CalculateStringSize(value);
 
            serializedAttributes.Add(new KeyValuePair<string, object?>(key, value));
        }
 
        string exceptionMessage = string.Empty;
        if (exception is not null)
        {
            exceptionMessage = exception.Message;
            sizeInBytes += CalculateStringSize(exceptionMessage);
        }
 
        sizeInBytes += CalculateStringSize(formattedMessage);
 
        return new SerializedLogRecord(
            logLevel,
            eventId,
            timestamp,
            serializedAttributes,
            exceptionMessage,
            formattedMessage,
            sizeInBytes);
    }
 
    public static void Return(SerializedLogRecord bufferedRecord)
    {
        _attributesPool.Return(bufferedRecord.Attributes);
    }
 
    private static int CalculateStringSize(string str)
    {
        if (string.IsNullOrEmpty(str))
        {
            return 0;
        }
 
        // Base size: object overhead (16 bytes) + other stuff.
        const int BaseSize = 26;
 
        // Strings are aligned to 8-byte boundaries
        const int Alignment = 7;
 
        int charSize = str.Length * sizeof(char);
        return (BaseSize + charSize + Alignment) & ~Alignment;
    }
}