143 lines
4.0 KiB
C#
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++;
|
|
}
|
|
}
|
|
}
|
|
}
|