public sealed class ErrorManager
{
// 單例
private static readonly Lazy _instance = new Lazy(() => new ErrorManager());
public static ErrorManager Instance => _instance.Value;
// 計時器:定期清理過期錯誤
private Timer _cleanupTimer;
private readonly TimeSpan _defaultInterval = TimeSpan.FromHours(12);
private readonly Dictionary _errors = new Dictionary();
private readonly object _lock = new object();
private ErrorManager() { }
// 啟動清理計時器
public void Start(TimeSpan? interval = null)
{
TimeSpan i = interval ?? _defaultInterval;
_cleanupTimer = new Timer(_ => CleanupExpired(), null, i, i);
}
public void SetTimerInterval(TimeSpan interval)
{
_cleanupTimer?.Change(interval, interval);
}
// 記錄錯誤並回傳唯一 Key
public string AddError(Exception ex = null)
{
ex = ex ?? HttpContext.Current?.Server?.GetLastError();
if (ex == null) return null;
string key = Guid.NewGuid().ToString("N");
var info = new ErrorInfo
{
Key = key,
Time = DateTime.Now,
Exception = ex,
Message = ex.Message,
StackTrace = ex.StackTrace,
RequestUrl = HttpContext.Current?.Request?.RawUrl,
User = HttpContext.Current?.User?.Identity?.Name
};
lock (_lock) _errors[key] = info;
// 記錄至日誌框架
LogError(info);
return key;
}
// 取得錯誤資訊
public ErrorInfo GetError(string key)
{
lock (_lock)
{
if (_errors.TryGetValue(key, out var info)) return info;
return null;
}
}
// 移除錯誤
public void RemoveError(string key)
{
lock (_lock) _errors.Remove(key);
}
// 清理過期項目 (預設保留 24 小時)
private void CleanupExpired(TimeSpan? maxAge = null)
{
TimeSpan age = maxAge ?? TimeSpan.FromHours(24);
var cutoff = DateTime.Now - age;
List toRemove;
lock (_lock)
{
toRemove = _errors.Where(kvp => kvp.Value.Time < cutoff).Select(kvp => kvp.Key).ToList();
foreach (var k in toRemove) _errors.Remove(k);
}
}
// Session 安全存取
public object GetSession(string key)
{
var session = HttpContext.Current?.Session;
return session?[key];
}
public void SetSession(string key, object value)
{
HttpContext.Current?.Session?.Add(key, value);
// 或 session[key] = value;
}
private void LogError(ErrorInfo info)
{
// 整合 log4net/NLog/Serilog
// log.Error($"[{info.Key}] {info.Message}", info.Exception);
}
}
public class ErrorInfo
{
public string Key { get; set; }
public DateTime Time { get; set; }
public Exception Exception { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public string RequestUrl { get; set; }
public string User { get; set; }
}
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
ErrorManager.Instance.Start(); // 啟動定時清理
// 或 ErrorManager.Instance.SetTimerInterval(TimeSpan.FromHours(6));
}
protected void Application_Error(object sender, EventArgs e)
{
string key = ErrorManager.Instance.AddError();
if (!string.IsNullOrEmpty(key))
{
// 導向錯誤頁面,攜帶 Key
Response.Redirect($"~/Error.aspx?key={key}");
}
}
protected void Application_End(object sender, EventArgs e)
{
ErrorManager.Instance._cleanupTimer?.Dispose();
}
}
protected void Page_Load(object sender, EventArgs e)
{
string key = Request.QueryString["key"];
var error = ErrorManager.Instance.GetError(key);
if (error != null)
{
lblTime.Text = error.Time.ToString("yyyy-MM-dd HH:mm:ss");
lblMessage.Text = error.Message;
#if DEBUG
lblStackTrace.Text = error.StackTrace;
pnlDebug.Visible = true;
#endif
}
else
{
lblMessage.Text = "錯誤資訊已過期或不存在。";
}
}
ErrorManager 以單例、字典快取、計時器清理、Session 包裝為核心,提供輕量級錯誤管理基礎設施。生產環境建議替換為成熟日誌框架 + APM (Application Insights、Elastic APM、SkyWalking) 實現全鏈路觀測。