web-dev-qa-db-ja.com

C#のすべてのメソッド呼び出しにTrace()を追加するにはどうすればよいですか?

ロックの問題を追跡するのに苦労しているので、すべてのメソッド呼び出しの開始と終了をログに記録したいと思います。私は以前、すべてのメソッドにコードを追加することなく、C++でこれを実行しました。これはC#で可能ですか?

26
Jon Tackabury

おそらく最善の策は、AOP(アスペクト指向プログラミング)フレームワークを使用して、メソッドの実行の前後にトレースコードを自動的に呼び出すことです。 AOPと.NETの一般的な選択肢は PostSharp です。

19
Dennis G.

あなたの主な目標が関数の入口/出口ポイントとその間の時折の情報をログに記録することである場合、Disposableロギングオブジェクトで良い結果が得られました。ここでコンストラクターは関数エントリをトレースし、Dispose()トレース出口。これにより、呼び出しコードは、各メソッドのコードを単一のusingステートメント内に単純にラップできます。間にある任意のログのメソッドも提供されます。これは、完全なC#ETWイベントトレースクラスと関数の開始/終了ラッパーです。

using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace MyExample
{
    // This class traces function entry/exit
    // Constructor is used to automatically log function entry.
    // Dispose is used to automatically log function exit.
    // use "using(FnTraceWrap x = new FnTraceWrap()){ function code }" pattern for function entry/exit tracing
    public class FnTraceWrap : IDisposable
    {
        string methodName;
        string className;

        private bool _disposed = false;

        public FnTraceWrap()
        {
            StackFrame frame;
            MethodBase method;

            frame = new StackFrame(1);
            method = frame.GetMethod();
            this.methodName = method.Name;
            this.className = method.DeclaringType.Name;

            MyEventSourceClass.Log.TraceEnter(this.className, this.methodName);
        }

        public void TraceMessage(string format, params object[] args)
        {
            string message = String.Format(format, args);
            MyEventSourceClass.Log.TraceMessage(message);
        }

        public void Dispose()
        {
            if (!this._disposed)
            {
                this._disposed = true;
                MyEventSourceClass.Log.TraceExit(this.className, this.methodName);
            }
        }
    }

    [EventSource(Name = "MyEventSource")]
    sealed class MyEventSourceClass : EventSource
    {
        // Global singleton instance
        public static MyEventSourceClass Log = new MyEventSourceClass();

        private MyEventSourceClass()
        {
        }

        [Event(1, Opcode = EventOpcode.Info, Level = EventLevel.Informational)]
        public void TraceMessage(string message)
        {
            WriteEvent(1, message);
        }

        [Event(2, Message = "{0}({1}) - {2}: {3}", Opcode = EventOpcode.Info, Level = EventLevel.Informational)]
        public void TraceCodeLine([CallerFilePath] string filePath = "",
                                  [CallerLineNumber] int line = 0,
                                  [CallerMemberName] string memberName = "", string message = "")
        {
            WriteEvent(2, filePath, line, memberName, message);
        }

        // Function-level entry and exit tracing
        [Event(3, Message = "Entering {0}.{1}", Opcode = EventOpcode.Start, Level = EventLevel.Informational)]
        public void TraceEnter(string className, string methodName)
        {
            WriteEvent(3, className, methodName);
        }

        [Event(4, Message = "Exiting {0}.{1}", Opcode = EventOpcode.Stop, Level = EventLevel.Informational)]
        public void TraceExit(string className, string methodName)
        {
            WriteEvent(4, className, methodName);
        }
    }
}

これを使用するコードは次のようになります。

public void DoWork(string foo)
{
    using (FnTraceWrap fnTrace = new FnTraceWrap())
    {
        fnTrace.TraceMessage("Doing work on {0}.", foo);
        /*
        code ...
        */
    }
}
3

プロファイラーは、開発中に実行中のコードを確認するのに最適ですが、本番環境でカスタムトレースを実行する機能を探している場合は、Denis G.が述べたように、 PostSharp が最適なツールです。すべてのコードを変更する必要はなく、簡単にオン/オフを切り替えることができます。

また、数分でセットアップするのも簡単です。PostSharpの作成者であるGaëlFraiteurには、既存のアプリにトレースを追加するのがいかに簡単かを示すビデオもあります。
例とチュートリアルは ドキュメントセクション にあります。

3
Renaud Bompuis

使用 ANTS Profiler RedGateからが最善の策です。それができない場合は、 Castle Windsorinterceptors を調べてください。ただし、IoCを介してタイプをロードしていることを前提としています。

リフレクションは別の方法です。 System.Reflection.Emit メソッドを使用して、コードをメモリに「書き込む」ことができます。そのコードは、メソッドのコードを置き換えて実行できますが、適切なログが記録されます。ただし、幸運を祈ります... Aspect# のようなアスペクト指向プログラミングフレームワークを使用する方が簡単です。

2
Neil Barnwell

ロックの問題が定着するのを待って、メモリダンプを実行し、さまざまなスレッドでコールスタックを分析している可能性があります。 DebugDiag または Debugging Tools for Windows に付属のadplusスクリプト(この場合はハングモード)を使用できます。

Tess Ferrandez .NETメモリダンプを使用してさまざまな問題をデバッグする方法を学ぶための 優れたラボシリーズ もあります。私はそれを強くお勧めします。

1
Richard Szalay

デッドロックの問題がある場合は、チェックアウトしてください http://www.codeproject.com/KB/dotnet/Deadlock_Detection.aspx

0
user186748

それが起こっていることをどうやって知っていますか?これがマルチスレッドアプリケーションの場合は、状態をテストし、実行時にSystem.Diagnostics.Debugger.Break()を呼び出して検出することをお勧めします。次に、[スレッド]ウィンドウを開き、関連する各スレッドの呼び出しスタックをステップ実行します。

0
Robert Venables