尝试使用 DynamicProxy 为 StructureMap 制作日志拦截器
我正在尝试记录从 UI(DNN 模块)到它使用的一些各种服务的调用,以分析人们如何与网站交互。我正在使用 StructureMap 2.5.3.0 和 Log4Net
我在各个类/实例对上运行良好,但我必须配置如下内容:
ObjectFactory.Configure(ce =>
ce.ForRequestedType<IRegService>()
.TheDefaultIsConcreteType<RegService>()
.EnrichWith(LoggingEnrichment.InterfaceLogger<IRegService>));
两次使用 IRegService
感觉有点混乱,但我可以与它共存。
日志记录是这样实现的:
public class LoggingEnrichment
{
public static object InterfaceLogger<TInterface>(object concrete)
{
return InterfaceLogger(typeof(TInterface), concrete);
}
public static object InterfaceLogger(Type iinterface, object concrete)
{
var dynamicProxy = new ProxyGenerator();
return dynamicProxy.CreateInterfaceProxyWithTarget(iinterface, concrete, new LogInterceptor());
}
}
public class LogInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var watch = new Stopwatch();
watch.Start();
invocation.Proceed();
watch.Stop();
ILog logger = LogManager.GetLogger(typeof(LogInterceptor));
var sb = new StringBuilder();
sb.AppendFormat("Calling: {0}.{1}\n", invocation.InvocationTarget.GetType(), invocation.MethodInvocationTarget.Name);
var param = invocation.Method.GetParameters();
if (param.Length > 0) sb.Append("With:\n");
for (int i = 0; i < param.Length; i++)
{
sb.AppendFormat("\t{0}\n\t\t{1}", param[i].Name, invocation.GetArgumentValue(i));
}
if(invocation.Method.ReturnType != typeof(void))
{
sb.AppendFormat("Returning: {0}\n", invocation.ReturnValue ?? "null");
}
sb.AppendFormat("In: {0}ms\n", watch.ElapsedMilliseconds);
logger.Debug(sb.ToString());
}
}
这可行,但有几个问题:
- 我必须手动配置每个服务<->接口对
- 我只想在从 UI 调用服务时连接日志记录
我试图通过为 StructureMap 实现 TypeInterceptor 来解决这个问题:
public class ApplicationRegistry : Registry
{
public ApplicationRegistry()
{
RegisterInterceptor(new LoggingInterceptor());
Scan(scanner =>
{
scanner.TheCallingAssembly();
var codeBase = System.Reflection.Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", String.Empty);
codeBase = codeBase.Substring(0, codeBase.LastIndexOf("/"));
scanner.AssembliesFromPath(codeBase);
scanner.WithDefaultConventions();
scanner.LookForRegistries();
});
}
}
public class LoggingInterceptor :TypeInterceptor
{
public object Process(object target, IContext context)
{
var newTarget = target;
if (context.BuildStack.Current != null && context.BuildStack.Current.RequestedType != null)
{
newTarget = LoggingEnrichment.InterfaceLogger(context.BuildStack.Current.RequestedType, target);
}
return newTarget;
}
public bool MatchesType(Type type)
{
return type.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase);
}
}
但是我遇到了一个问题,其中调用 Process 给了我一个没有实现构建上下文定义的接口的类。这导致必须将
InterfaceLogger
的实现更改为
public static object InterfaceLogger(Type iinterface, object concrete)
{
if(!iinterface.IsAssignableFrom(concrete.GetType())) return concrete;
var dynamicProxy = new ProxyGenerator();
var interfaceProxy = dynamicProxy.CreateInterfaceProxyWithTarget(iinterface, concrete, new LogInterceptor());
return interfaceProxy;
}
return interfaceProxy;
上的断点永远不会到达,这表明 context.BuildStack.Current。 RequestedType
未返回正确的接口。奇怪的是,我的所有课程似乎都被正确注入。
另外,即使这有效,我仍然会遇到只想拦截来自 UI 层的调用的问题。
我正在寻找解决前两个问题的方法,以及我在 TypeInterceptor
上做错了什么
I'm trying to log calls from the UI (DNN module) to some of various services it uses, in a effort to profile how people are interacting with the site. I'm using StructureMap 2.5.3.0 and Log4Net
I got things working well on individual class/instance pairs, but I have to configure things like this:
ObjectFactory.Configure(ce =>
ce.ForRequestedType<IRegService>()
.TheDefaultIsConcreteType<RegService>()
.EnrichWith(LoggingEnrichment.InterfaceLogger<IRegService>));
Having the IRegService
twice felt a bit messy, but I can live with it.
The logging is implemented like this:
public class LoggingEnrichment
{
public static object InterfaceLogger<TInterface>(object concrete)
{
return InterfaceLogger(typeof(TInterface), concrete);
}
public static object InterfaceLogger(Type iinterface, object concrete)
{
var dynamicProxy = new ProxyGenerator();
return dynamicProxy.CreateInterfaceProxyWithTarget(iinterface, concrete, new LogInterceptor());
}
}
public class LogInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var watch = new Stopwatch();
watch.Start();
invocation.Proceed();
watch.Stop();
ILog logger = LogManager.GetLogger(typeof(LogInterceptor));
var sb = new StringBuilder();
sb.AppendFormat("Calling: {0}.{1}\n", invocation.InvocationTarget.GetType(), invocation.MethodInvocationTarget.Name);
var param = invocation.Method.GetParameters();
if (param.Length > 0) sb.Append("With:\n");
for (int i = 0; i < param.Length; i++)
{
sb.AppendFormat("\t{0}\n\t\t{1}", param[i].Name, invocation.GetArgumentValue(i));
}
if(invocation.Method.ReturnType != typeof(void))
{
sb.AppendFormat("Returning: {0}\n", invocation.ReturnValue ?? "null");
}
sb.AppendFormat("In: {0}ms\n", watch.ElapsedMilliseconds);
logger.Debug(sb.ToString());
}
}
This works, but has a couple issues:
- I have to manually configure each service <-> interface pair
- I only want to wire up the logging when the service is called from the UI
I tried to get around this by implementing a TypeInterceptor for StructureMap:
public class ApplicationRegistry : Registry
{
public ApplicationRegistry()
{
RegisterInterceptor(new LoggingInterceptor());
Scan(scanner =>
{
scanner.TheCallingAssembly();
var codeBase = System.Reflection.Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", String.Empty);
codeBase = codeBase.Substring(0, codeBase.LastIndexOf("/"));
scanner.AssembliesFromPath(codeBase);
scanner.WithDefaultConventions();
scanner.LookForRegistries();
});
}
}
public class LoggingInterceptor :TypeInterceptor
{
public object Process(object target, IContext context)
{
var newTarget = target;
if (context.BuildStack.Current != null && context.BuildStack.Current.RequestedType != null)
{
newTarget = LoggingEnrichment.InterfaceLogger(context.BuildStack.Current.RequestedType, target);
}
return newTarget;
}
public bool MatchesType(Type type)
{
return type.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase);
}
}
But I'm getting a problem with that where the call to Process
is giving me a class that doesn't implement the interface defined by the build context. This has resulted in having to change the implementation of the InterfaceLogger
to
public static object InterfaceLogger(Type iinterface, object concrete)
{
if(!iinterface.IsAssignableFrom(concrete.GetType())) return concrete;
var dynamicProxy = new ProxyGenerator();
var interfaceProxy = dynamicProxy.CreateInterfaceProxyWithTarget(iinterface, concrete, new LogInterceptor());
return interfaceProxy;
}
A breakpoint on the return interfaceProxy;
is never reached, this indicates that context.BuildStack.Current.RequestedType
isn't returning the right interface. The odd thing is that all my classes seem to be injected correctly.
Also, even if this was working I'd still have the issue of only wanting to intercept the calls from the UI layer.
I'm looking for a way my first 2 issues, and also what I'm doing wrong with the TypeInterceptor
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我通过使用约定来解决这个问题。以下是我为实现此目标所做的步骤。
首先,我对指定的程序集进行了扫描,我将在其中附加装饰器。
然后我创建了一个 Convention 类。我实际上是从这个线程 Decorating a generic interface with Structuremap 得到的,并做了一些根据您的实施进行修订。
最后是会议课程。
我使用与您相同的 LoggingEnrichment 类。
希望这能解决您提到的问题。
I got around with this by using Convention. Below are the steps I did to achieve this.
First I did a scan on my specified assembly where I would attach my decorator.
Then I created a Convention class. I actually got it from this thread Decorating a generic interface with Structuremap and did some revision based on your implementation.
And lastly this is the Convention class.
I use the same LoggingEnrichment class that you have.
Hope this solves your mentioned issues.