任意のクラス(ただし基底クラスはContextBoundObject)に対してメソッドの呼び出しをロギング出来るカスタム属性です。ファイル名を指定するとログをファイルに書き込みます。指定しなければ標準出力に書き出します。アスペクト指向プログラミングっぽいかな?
using System; using System.IO; using System.Collections; using System.Runtime.Remoting; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Proxies; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Services; // メソッドロギングプロキシ class MethodCallLoggingProxy : RealProxy { readonly MarshalByRefObject target; readonly string logfile; public MethodCallLoggingProxy(MarshalByRefObject target, Type t, string logfile) : base(t) { this.target = target; this.logfile = logfile; } public override IMessage Invoke(IMessage msg) { IMethodCallMessage call = (IMethodCallMessage)msg; IMethodReturnMessage res; IConstructionCallMessage ctor = call as IConstructionCallMessage; if (ctor != null) { RealProxy rp = RemotingServices.GetRealProxy(target); rp.InitializeServerObject(ctor); MarshalByRefObject tp = this.GetTransparentProxy() as MarshalByRefObject; res = EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp); } else { StreamWriter sw; // ファイル名が指定されていなければ標準出力に書き出す if (logfile != null) sw = new StreamWriter(logfile, true); else sw = new StreamWriter(Console.OpenStandardOutput(1024)); ArrayList input = new ArrayList(); ArrayList output = new ArrayList(); input.Add("\t<in>"); for (int i = 0; i < call.InArgCount; ++i) input.Add(String.Format("<{0}>{1}</{0}>", call.GetInArgName(i), call.GetInArg(i))); input.Add("</in>\n"); // メソッドの実行及び、実行時間の測定 DateTime t1 = DateTime.Now; res = RemotingServices.ExecuteMessage(target, call); DateTime t2 = DateTime.Now; if (res.Exception == null) { output.Add("\t<out>"); for (int i = 0; i <res.OutArgCount; ++i) output.Add(String.Format("<{0}>{1}</{0}>", res.GetOutArgName(i), res.GetOutArg(i))); output.Add("</out>\n"); output.Add(String.Format("\t<return>{0}</return>\n", res.ReturnValue)); } else { output.Add(String.Format("\t<exception type=\"{0}\">{1}</exception>\n", res.Exception.GetType(), res.Exception.Message)); } sw.WriteLine("<{0} start=\"{1}\" time=\"{2}\">", call.MethodName, t1, t2 - t1); for (int i = 0; i < input.Count; ++i) sw.Write(input[i]); for (int i = 0; i < output.Count; ++i) sw.Write(output[i]); sw.WriteLine(String.Format("</{0}>", call.MethodName)); sw.Close(); } return res; } } // メソッドロギング用のカスタム属性 [AttributeUsage(AttributeTargets.Class)] public class MethodCallLoggingAttribute : ProxyAttribute { readonly string logfile; public MethodCallLoggingAttribute(string logfile) { this.logfile = logfile; } public override MarshalByRefObject CreateInstance(Type serverType) { MarshalByRefObject target = base.CreateInstance (serverType); RealProxy rp = new MethodCallLoggingProxy(target, serverType, logfile); return rp.GetTransparentProxy() as MarshalByRefObject; } }
using System; using System.Threading; // ロギング属性をつける // メソッド呼び出しのログが自動生成される [MethodCallLogging(null)] class MyClass : ContextBoundObject { Random rnd = new Random((int)DateTime.Now.Ticks); public MyClass() {} public double Add(double x, double y) { Thread.Sleep(rnd.Next(1000)); // waitを入れる return x + y; } public void Multiply(double x, double y, out double z) { Thread.Sleep(rnd.Next(1000)); // waitを入れる z = x * y; } public double Divide(double x, double y) { Thread.Sleep(rnd.Next(1000)); // waitを入れる if (x < double.Epsilon) { throw new DivideByZeroException("Exception message"); } return x / y; } } class LoggingTest { [STAThread] static void Main(string[] args) { MyClass my = new MyClass(); double z; // inパラメータと戻り値の例 my.Add(1.23, 4.56); // in,outパラメータの例 my.Multiply(9.87, 6.54, out z); try { // 例外発生の例 my.Divide(0, 0); } catch (Exception) { // do nothing } } } /* 実行結果 <Add start="2004/03/29 1:21:46" time="00:00:00.7031250"> <in><x>1.23</x><y>4.56</y></in> <out></out> <return>5.79</return> </Add> <Multiply start="2004/03/29 1:21:46" time="00:00:00.9062500"> <in><x>9.87</x><y>6.54</y></in> <out><z>64.5498</z></out> <return>System.Void</return> </Multiply> <Divide start="2004/03/29 1:21:47" time="00:00:00.1250000"> <in><x>0</x><y>0</y></in> <exception type="System.DivideByZeroException">Exception message</exception> </Divide> */