在 .NET Core 中,我们可以使用 Diagnostics(Diagnostic Source)来自定义记录跟踪信息。其主要原理是,在关键时刻发送一个事件,将事件传递给监听器,从而实现跟踪记录。整个流程可以分为三个步骤:
- 定义属性事件源
Diagnostics 中的每个事件源都需要定义一个类,在这个类中,我们可以定义多个属性来描述该事件。假设我们要在示例中记录控制器的请求信息,事件源类可能如下所示:
public class ControllerSource : DiagnosticSource
{
public static ControllerSource Log = new ControllerSource();
private ControllerSource() : base("Demo-Controller") { }
public void RequestStart(HttpContext context)
{
if (this.IsEnabled("RequestStart"))
{
this.Write("RequestStart", context);
}
}
public void RequestStop(HttpContext context)
{
if (this.IsEnabled("RequestStop"))
{
this.Write("RequestStop", context);
}
}
}
在上面的代码中,我们定义了一个名为ControllerSource
的属性事件源,它继承了DiagnosticSource
类。在类的构造函数中,我们传入了该事件源的名称,它将用作日志记录器的名称。然后,我们定义了两个RequestStart
和RequestStop
方法,它们将在日志记录器事件中被使用。IsEnabled
方法用于检测事件是否可用,Write
方法则用于写入日志。
- 确认监听器
在记录跟踪信息之前,我们需要在我们的应用程序中定义一个监听器。我们可以使用以下代码定义一个基于控制台输出的监听器:
public class ConsoleDiagnosticListener : IObserver<DiagnosticListener>
{
private IDisposable subscription;
public void OnCompleted() { }
public void OnError(Exception error) { }
public void OnNext(DiagnosticListener diagnosticListener)
{
if (diagnosticListener.Name == "Demo-Controller")
{
this.subscription = diagnosticListener.Subscribe(new ConsoleDiagnosticObserver());
}
}
public void Dispose() => this.subscription.Dispose();
}
public class ConsoleDiagnosticObserver : IObserver<KeyValuePair<string, object>>
{
public void OnCompleted() { }
public void OnError(Exception error) { }
public void OnNext(KeyValuePair<string, object> value)
{
Console.WriteLine($"[ {value.Key} ] {value.Value}");
}
}
上述代码中,我们首先定义了一个名为ConsoleDiagnosticListener
的类,它实现了接口IObserver<DiagnosticListener>
,它将跟踪日志记录器的所有事件。在方法OnNext
中,我们筛选了名称为Demo-Controller
的日志记录器,当它被使用时,我们定了一个ConsoleDiagnosticObserver
的实例,这个实例将负责把日志记录到控制台中来。
- 触发事件
现在,在程序运行时,我们需要触发RequestStart
和RequestStop
事件,这将为ControllerSource
创建一个新的跟踪片段,并将其发送给监听器。示例代码可能如下所示:
public class HomeController : Controller
{
private readonly ControllerSource controllerSource;
public HomeController()
{
this.controllerSource = ControllerSource.Log;
}
public IActionResult Index()
{
var result = this.controllerSource.RequestStart(this.HttpContext);
... //do something
this.controllerSource.RequestStop(this.HttpContext);
return View();
}
}
在上面的代码中,我们首先注入了ControllerSource
,它将用于记录跟踪的信息。然后,在请求开始时,我们调用RequestStart
方法,在请求结束时调用RequestStop
方法。
示例:记录 EF Core 数据库查询
另一个示例是记录 EF Core 中的数据库查询。在这个例子中,我们还将添加一个名为ActivitySource
的事件源,用于处理活动跟踪 ID。
- 定义属性事件源
定义一个EntityFrameworkSource
类,用于记录 EF Core 中的关键事件。
public class EntityFrameworkSource : DiagnosticSource
{
private EntityFrameworkSource() : base("Demo-EFCore") { }
public static readonly EntityFrameworkSource Instance = new EntityFrameworkSource();
public Activity MapToActivity(QueryTrackingBehavior queryTrackingBehavior, Guid relatedActivityId, string commandText, IEnumerable<KeyValuePair<string, object>> parameters)
{
var activity = new Activity("EF-Query");
activity.SetParentId(relatedActivityId);
activity.SetTag("CommandText", commandText);
activity.SetTag("Parameters", JsonConvert.SerializeObject(parameters));
activity.Start();
activity.AddTag("TrackingBehavior", queryTrackingBehavior.ToString());
activity.AddEvent(new ActivityEvent("ExecuteCommand"));
return activity;
}
}
在上述代码中,我们定义了一个名为EntityFrameworkSource
的事件源,并为其添加了MapToActivity
方法。这个方法将用于将 EF Core 查询映射到活动跟踪 ID 上。
- 确认监听器
定义一个基于日志的监听器,也可以使用控制台监听器。示例中我们使用了基于日志的监听器。
private class EntityFrameworkDiagnosticObserver : IObserver<KeyValuePair<string, object>>
{
private readonly ILogger<EntityFrameworkDiagnosticObserver> logger;
public EntityFrameworkDiagnosticObserver(ILogger<EntityFrameworkDiagnosticObserver> logger) => this.logger = logger;
public void OnCompleted() { }
public void OnError(Exception error) => this.logger.LogError(error, "Error on entity framework diagnostic observer");
public void OnNext(KeyValuePair<string, object> value)
{
if (value.Key == "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted")
{
Guid.TryParse(value.Value.GetType().GetProperty("ActivityId") ?.GetValue(value.Value).ToString(), out Guid activityId);
var command = value.Value.GetType().GetProperty("CommandText") ?.GetValue(value.Value).ToString();
QueryTrackingBehavior.TryParse(value.Value.GetType().GetProperty("QueryTrackingBehavior") ?.GetValue(value.Value).ToString(), out QueryTrackingBehavior queryTrackingBehavior);
var parameters = new List<KeyValuePair<string, object>>();
var dbParameterChoiceType = typeof(Microsoft.EntityFrameworkCore.Storage.RelationalParameter);
var dbParameterDictionary = (IDictionary)(value.Value.GetType().GetProperty("Parameters") ?.GetValue(value.Value));
foreach (var key in dbParameterDictionary.Keys)
{
var param = dbParameterDictionary[key];
parameters.Add(new KeyValuePair<string, object>(
param.GetType().GetProperty("InvariantName") ?.GetValue(param).ToString(),
param.GetType().GetProperty("Value") ?.GetValue(param)));
}
EntityFrameworkSource.Instance.MapToActivity(queryTrackingBehavior, activityId, command, parameters) ?.Dispose();
}
}
}
private class EntityFrameworkDiagnosticListener : IObserver<DiagnosticListener>
{
private readonly ILogger<EntityFrameworkDiagnosticListener> logger;
private IDisposable subscription;
public EntityFrameworkDiagnosticListener(ILogger<EntityFrameworkDiagnosticListener> logger) => this.logger = logger;
public void OnCompleted() { }
public void OnError(Exception error) => this.logger.LogError(error, "Error on entity framework diagnostic listener");
public void OnNext(DiagnosticListener listener)
{
if (listener.Name == "Microsoft.EntityFrameworkCore")
{
this.subscription = listener.Subscribe(new EntityFrameworkDiagnosticObserver(logger));
}
}
}
在上述代码中,我们定义了两个类:EntityFrameworkDiagnosticObserver
和EntityFrameworkDiagnosticListener
,用于监听 EF Core 中的数据库命令执行事件。当事件发生时,我们可以将它们转换为一个新的Activity
,并将其记录到日志中。
- 触发事件
在 EF Core 执行数据库命令时将会触发数据库命令执行事件。
using (var scope = this.dbContext.ContextLogger.BeginScope("DB"))
{
this.dbContext.Organization.Add(entity);
this.dbContext.SaveChanges();
}
在上述代码中,我们调用了ContextLogger.BeginScope
方法。这个方法将创建一个新的作用域,并将在作用域中执行所有的 EF Core 查询。我们可以在监听器中查看这些事件,并将它们映射到一个新的Activity
中。
至此,我们就可以通过 Diagnostics 在 .NET Core中记录跟踪信息了。但为了在实践中更好的运用这些工具,我们还需要更多的学习和实践。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:在 .NET Core 中使用 Diagnostics (Diagnostic Source) 记录跟踪信息 - Python技术站