using System; using System.Collections.Generic; namespace AibisDream.Kit { /// 开发模式日志页消费的不可变日志记录。 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; } } /// /// 有界、线程安全的运行时日志缓冲。Application.logMessageReceivedThreaded 可从任意线程写入, /// UI 仅通过快照在主线程读取。 /// 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++; } } } }