using System;
using System.Threading;
namespace Aivfo.OperationLog
{
///
/// 进程内 traceId / parentId / operator / 设备上下文 透传(AsyncLocal,跨 async/线程切换)。
/// 对称 Java 的 Trace(TransmittableThreadLocal)。
///
public static class OperationLogContext
{
private static readonly AsyncLocal _traceId = new AsyncLocal();
private static readonly AsyncLocal _parentId = new AsyncLocal();
private static readonly AsyncLocal _operator = new AsyncLocal();
private static readonly AsyncLocal _houseSn = new AsyncLocal();
private static readonly AsyncLocal _wellSn = new AsyncLocal();
/// 当前 traceId(无则返回 null)。
public static string TraceId
{
get => _traceId.Value;
set => _traceId.Value = value;
}
/// 当前 parentId。
public static string ParentId
{
get => _parentId.Value;
set => _parentId.Value = value;
}
/// 当前操作者(登录用户)。可在登录后设置一次。
public static string Operator
{
get => _operator.Value;
set => _operator.Value = value;
}
/// 当前舱号。
public static int? HouseSn
{
get => _houseSn.Value;
set => _houseSn.Value = value;
}
/// 当前 well 号。
public static int? WellSn
{
get => _wellSn.Value;
set => _wellSn.Value = value;
}
/// 生成一个新的 traceId(去横线的 Guid,紧凑唯一)。
public static string NewTraceId()
{
return Guid.NewGuid().ToString("N");
}
///
/// 开启一次操作 scope:设置/继承 traceId,把当前 traceId 作为新 parentId。
/// 释放时恢复原值。用 using 包裹一次完整操作。
///
public static IDisposable BeginScope(string traceId = null)
{
var prevTrace = _traceId.Value;
var prevParent = _parentId.Value;
// 新 scope 的 parentId = 外层的 traceId(构成父子链);最外层为 null。
_parentId.Value = prevTrace;
_traceId.Value = string.IsNullOrEmpty(traceId)
? (string.IsNullOrEmpty(prevTrace) ? NewTraceId() : prevTrace)
: traceId;
return new ScopeRestore(prevTrace, prevParent);
}
private sealed class ScopeRestore : IDisposable
{
private readonly string _prevTrace;
private readonly string _prevParent;
private bool _done;
public ScopeRestore(string prevTrace, string prevParent)
{
_prevTrace = prevTrace;
_prevParent = prevParent;
}
public void Dispose()
{
if (_done) return;
_done = true;
_traceId.Value = _prevTrace;
_parentId.Value = _prevParent;
}
}
}
}