关于以AOP方式记录方法的每次调用
About logging every calling of a method in AOP way
我想为我的项目创建一个日志系统。我希望它以 AOP 方式进行。需要用参数和返回值记录每个执行的方法。我的理解是,它可以通过拦截器来实现。我为它创建了一个宠物项目。它几乎可以工作。我遇到的问题是如何从日志中了解在一个请求期间执行了什么以及在另一个请求期间执行了什么?
F.e。我有 asp.net 申请。用户来到应用程序。我记录一切。但是我不明白某个具体用户做了什么。我只是在日志中看到执行了一个方法,然后执行了另一个方法,依此类推。但是如何定义属于具体请求的所有执行的方法?
更新:
我知道 ASP.Net 中的过滤器。我将它用于控制器。但是我也想记录服务和数据访问层。
另一个更新:
nvoigt 的回答有效。非常感谢 nvoigt!
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var builder = new ContainerBuilder();
var config = GlobalConfiguration.Configuration;
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<ItemService>().As<IItemService>().EnableInterfaceInterceptors().InterceptedBy(typeof(CallLogger)).InstancePerRequest();
builder.RegisterType<ItemRepository>().As<IItemRepository>().EnableInterfaceInterceptors().InterceptedBy(typeof(CallLogger)).InstancePerRequest();
builder.RegisterType<CallLogger>().AsSelf().InstancePerRequest();
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
如您所见,CallLoger 具有每个请求的生命周期。我添加了 Guid scopeId 属性 到 CallLogger
public class CallLogger : IInterceptor
{
private readonly string _path = @"D:.txt";
private readonly Guid _scopeId;
public CallLogger()
{
_scopeId = Guid.NewGuid();
}
public void Intercept(IInvocation invocation)
{
string message = String.Format("{0}: {1} - Calling method {2} with parameters {3}... ",
_scopeId,
invocation.TargetType.FullName, invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => a)));
string[] m1 = { message };
File.AppendAllLines(_path, m1);
invocation.Proceed();
List<string> m2 = new List<string>();
if (invocation.ReturnValue is IEnumerable)
{
m2.Add(String.Format($"{_scopeId}: Done {invocation.TargetType.FullName} - {invocation.Method.Name}: result was"));
foreach (var rv in (IEnumerable)invocation.ReturnValue)
{
m2.Add(rv.ToString());
}
}
else
{
m2.Add(String.Format($"{_scopeId}: Done {invocation.TargetType.FullName} - {invocation.Method.Name}: result was {invocation.ReturnValue}"));
}
File.AppendAllLines(_path, m2);
}
}
现在我的 scopeId 日志:
fd35e0f3-a162-48f9-9d69-b39c601513a2: Core.ItemService - Calling method Get with parameters 1...
fd35e0f3-a162-48f9-9d69-b39c601513a2: Core.Repositories.ItemRepository - Calling method Get with parameters 1...
fd35e0f3-a162-48f9-9d69-b39c601513a2: Done Core.Repositories.ItemRepository - Get: result was Id is 1, SomeProperty is Property1, AnotherProperty is Another1
fd35e0f3-a162-48f9-9d69-b39c601513a2: Done Core.ItemService - Get: result was Id is 1, SomeProperty is Property1, AnotherProperty is Another1
1ed6bfd7-f786-4397-905e-6ba7027bdd55: Core.ItemService - Calling method Get with parameters 5...
1ed6bfd7-f786-4397-905e-6ba7027bdd55: Core.Repositories.ItemRepository - Calling method Get with parameters 5...
1ed6bfd7-f786-4397-905e-6ba7027bdd55: Done Core.Repositories.ItemRepository - Get: result was Id is 5, SomeProperty is Property5, AnotherProperty is Another5
1ed6bfd7-f786-4397-905e-6ba7027bdd55: Done Core.ItemService - Get: result was Id is 5, SomeProperty is Property5, AnotherProperty is Another5
你对你实际所做的事情非常模糊所以这个答案对你需要做的事情也有点模糊:
每个日志条目都必须包含一个标识符,这样您就可以按标识符对日志条目进行分组,基本上每次调用控制器都会得到一组日志(控制器日志、服务层日志、存储库都分组了通过他们的初始条目一起)。
您已经有一个实例来保存每次调用的数据,它是您的依赖项注入容器,每次调用都有一个 scope。你需要有一个 class 来保存你的范围特定数据(例如每个范围只有一个 Guid.NewGuid()
)并且你的记录器必须通过你的依赖访问那个 class注入框架。然后您的记录器可以将该标识符附加到您的日志中,您稍后可以按它分组。
我想为我的项目创建一个日志系统。我希望它以 AOP 方式进行。需要用参数和返回值记录每个执行的方法。我的理解是,它可以通过拦截器来实现。我为它创建了一个宠物项目。它几乎可以工作。我遇到的问题是如何从日志中了解在一个请求期间执行了什么以及在另一个请求期间执行了什么? F.e。我有 asp.net 申请。用户来到应用程序。我记录一切。但是我不明白某个具体用户做了什么。我只是在日志中看到执行了一个方法,然后执行了另一个方法,依此类推。但是如何定义属于具体请求的所有执行的方法?
更新: 我知道 ASP.Net 中的过滤器。我将它用于控制器。但是我也想记录服务和数据访问层。
另一个更新: nvoigt 的回答有效。非常感谢 nvoigt!
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var builder = new ContainerBuilder();
var config = GlobalConfiguration.Configuration;
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<ItemService>().As<IItemService>().EnableInterfaceInterceptors().InterceptedBy(typeof(CallLogger)).InstancePerRequest();
builder.RegisterType<ItemRepository>().As<IItemRepository>().EnableInterfaceInterceptors().InterceptedBy(typeof(CallLogger)).InstancePerRequest();
builder.RegisterType<CallLogger>().AsSelf().InstancePerRequest();
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
如您所见,CallLoger 具有每个请求的生命周期。我添加了 Guid scopeId 属性 到 CallLogger
public class CallLogger : IInterceptor
{
private readonly string _path = @"D:.txt";
private readonly Guid _scopeId;
public CallLogger()
{
_scopeId = Guid.NewGuid();
}
public void Intercept(IInvocation invocation)
{
string message = String.Format("{0}: {1} - Calling method {2} with parameters {3}... ",
_scopeId,
invocation.TargetType.FullName, invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => a)));
string[] m1 = { message };
File.AppendAllLines(_path, m1);
invocation.Proceed();
List<string> m2 = new List<string>();
if (invocation.ReturnValue is IEnumerable)
{
m2.Add(String.Format($"{_scopeId}: Done {invocation.TargetType.FullName} - {invocation.Method.Name}: result was"));
foreach (var rv in (IEnumerable)invocation.ReturnValue)
{
m2.Add(rv.ToString());
}
}
else
{
m2.Add(String.Format($"{_scopeId}: Done {invocation.TargetType.FullName} - {invocation.Method.Name}: result was {invocation.ReturnValue}"));
}
File.AppendAllLines(_path, m2);
}
}
现在我的 scopeId 日志:
fd35e0f3-a162-48f9-9d69-b39c601513a2: Core.ItemService - Calling method Get with parameters 1...
fd35e0f3-a162-48f9-9d69-b39c601513a2: Core.Repositories.ItemRepository - Calling method Get with parameters 1...
fd35e0f3-a162-48f9-9d69-b39c601513a2: Done Core.Repositories.ItemRepository - Get: result was Id is 1, SomeProperty is Property1, AnotherProperty is Another1
fd35e0f3-a162-48f9-9d69-b39c601513a2: Done Core.ItemService - Get: result was Id is 1, SomeProperty is Property1, AnotherProperty is Another1
1ed6bfd7-f786-4397-905e-6ba7027bdd55: Core.ItemService - Calling method Get with parameters 5...
1ed6bfd7-f786-4397-905e-6ba7027bdd55: Core.Repositories.ItemRepository - Calling method Get with parameters 5...
1ed6bfd7-f786-4397-905e-6ba7027bdd55: Done Core.Repositories.ItemRepository - Get: result was Id is 5, SomeProperty is Property5, AnotherProperty is Another5
1ed6bfd7-f786-4397-905e-6ba7027bdd55: Done Core.ItemService - Get: result was Id is 5, SomeProperty is Property5, AnotherProperty is Another5
你对你实际所做的事情非常模糊所以这个答案对你需要做的事情也有点模糊:
每个日志条目都必须包含一个标识符,这样您就可以按标识符对日志条目进行分组,基本上每次调用控制器都会得到一组日志(控制器日志、服务层日志、存储库都分组了通过他们的初始条目一起)。
您已经有一个实例来保存每次调用的数据,它是您的依赖项注入容器,每次调用都有一个 scope。你需要有一个 class 来保存你的范围特定数据(例如每个范围只有一个 Guid.NewGuid()
)并且你的记录器必须通过你的依赖访问那个 class注入框架。然后您的记录器可以将该标识符附加到您的日志中,您稍后可以按它分组。