Files
aibis-dream/Assets/Scripts/Framework/LogKit/RuntimeLogBuffer.cs
T
2026-07-20 14:29:47 +08:00

143 lines
4.0 KiB
C#

using System;
using System.Collections.Generic;
namespace AibisDream.Kit
{
/// <summary>开发模式日志页消费的不可变日志记录。</summary>
internal readonly struct RuntimeLogRecord
{
public readonly long Sequence;
public readonly DateTime TimestampUtc;
public readonly LogLevel Level;
public readonly LogCategory Category;
public readonly string Message;
public readonly string StackTrace;
public readonly string Context;
public readonly string SceneName;
public readonly int FrameCount;
public readonly int ThreadId;
public RuntimeLogRecord(long sequence, LogEntry entry)
{
Sequence = sequence;
TimestampUtc = entry.TimestampUtc;
Level = entry.Level;
Category = entry.Category;
Message = entry.Message;
StackTrace = entry.StackTrace;
Context = entry.Context;
SceneName = entry.SceneName;
FrameCount = entry.FrameCount;
ThreadId = entry.ThreadId;
}
public string ToDisplayString(bool includeStackTrace)
{
var context = string.IsNullOrEmpty(Context) ? string.Empty : $" | {Context}";
var scene = string.IsNullOrEmpty(SceneName) ? string.Empty : $" | {SceneName}";
var line = $"{TimestampUtc.ToLocalTime():HH:mm:ss.fff} [{Level}] [{Category}]{scene}{context} {Message}";
if (includeStackTrace && !string.IsNullOrEmpty(StackTrace))
{
line += $"\n{StackTrace}";
}
return line;
}
}
/// <summary>
/// 有界、线程安全的运行时日志缓冲。Application.logMessageReceivedThreaded 可从任意线程写入,
/// UI 仅通过快照在主线程读取。
/// </summary>
internal sealed class RuntimeLogBuffer
{
public const int DefaultCapacity = 1000;
private readonly object _sync = new();
private readonly RuntimeLogRecord[] _entries;
private int _start;
private int _count;
private long _nextSequence = 1;
private int _version;
public RuntimeLogBuffer(int capacity = DefaultCapacity)
{
if (capacity <= 0)
{
throw new ArgumentOutOfRangeException(nameof(capacity));
}
_entries = new RuntimeLogRecord[capacity];
}
public int Capacity => _entries.Length;
public int Count
{
get
{
lock (_sync)
{
return _count;
}
}
}
public int Version
{
get
{
lock (_sync)
{
return _version;
}
}
}
public void Add(LogEntry entry)
{
lock (_sync)
{
var record = new RuntimeLogRecord(_nextSequence++, entry);
if (_count < _entries.Length)
{
_entries[(_start + _count) % _entries.Length] = record;
_count++;
}
else
{
_entries[_start] = record;
_start = (_start + 1) % _entries.Length;
}
_version++;
}
}
public RuntimeLogRecord[] Snapshot()
{
lock (_sync)
{
var result = new RuntimeLogRecord[_count];
for (var i = 0; i < _count; i++)
{
result[i] = _entries[(_start + i) % _entries.Length];
}
return result;
}
}
public void Clear()
{
lock (_sync)
{
Array.Clear(_entries, 0, _entries.Length);
_start = 0;
_count = 0;
_version++;
}
}
}
}