[ VB.NET / C# ] 呼び出し元のメソッド名, ファイル名, 行番号を取得する ( .Net Framework4.5 以降 )

Pocket

プログラム開発において障害が発生したときに必要な情報としては、障害内容(最重要)、障害発生箇所(重要) だと思います。障害内容はケースバイケースになると思いますが、障害箇所については .Net Framework4.5 以降 System.Runtime.CompilerServices 名前空間に呼び出し元情報属性が追加され、容易にログなどの出力箇所を一箇所に集約することができるようになりました。

スポンサーリンク

呼び出し元情報の取得 ( メソッド名・ファイル名・行番号 )

呼び出し元情報を自動的に取得するサンプルコードになります。System.Runtime.CompilerServices 名前空間の CallerMemberName ( 呼び出し元メソッド名・プロパティ名 )、CallerFilePath ( 呼び出し元ファイルパス )、CallerLineNumber ( 呼び出し元ファイル行番号 ) を省略可能引数に設定して取得します。 詳細はコメントを参照ください。

VB.NET

Public Class Form1

    Public Sub New()
        MyLog("コンストラクタ")
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Try
            ' 例外を発生させる
            Throw New Exception("MyMessage")

        Catch ex As Exception

            ' ログ出力
            MyLog(ex.Message)

        End Try

    End Sub

    ' ログ出力
    Public Sub MyLog(ByVal msg As String,
                    <System.Runtime.CompilerServices.CallerMemberName> Optional callerMemberName As String = Nothing,
                    <System.Runtime.CompilerServices.CallerFilePath> Optional callerFilePath As String = Nothing,
                    <System.Runtime.CompilerServices.CallerLineNumber> Optional callerLineNumber As Integer = 0)

        Console.WriteLine("メッセージ: " & msg)                                  ' メッセージ
        Console.WriteLine("呼び出し元メソッド・プロパティ: " & callerMemberName) ' 呼び出し元メソッドまたはプロパティ名
        Console.WriteLine("呼び出し元ファイルパス: " & callerFilePath)           ' 呼び出し元ファイルパス
        Console.WriteLine("呼び出し元ファイル行番号: " & callerLineNumber)       ' 呼び出し元行番号

        If (callerMemberName = ".ctor" OrElse        ' コンストラクタから呼ばれた場合
               callerMemberName = ".cctor" OrElse    ' 静的コンストラクタから呼ばれた場合
               callerMemberName = "Finalize") Then   ' デストラクタから呼ばれた場合
            Throw New Exception("コンストラクタで呼ぶなって言ってんだろ。このバカチンが!")
        End If

    End Sub

End Class

C#

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        MyLog("コンストラクタ");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        try
        {
            // 例外発生
            throw new Exception("MyMessage");
        }
        catch (Exception ex)
        {
            // ログ出力
            MyLog(ex.Message);
        }
    }

    // ログ出力
    public void MyLog(string msg,
                      [System.Runtime.CompilerServices.CallerMemberName] string callerMemberName = "",
                      [System.Runtime.CompilerServices.CallerFilePath]   string callerFilePath = "",
                      [System.Runtime.CompilerServices.CallerLineNumber] int callerLineNumber = 0)
    {
        Console.WriteLine("メッセージ: " + msg);                                  // メッセージ
        Console.WriteLine("呼び出し元メソッド・プロパティ: " + callerMemberName); // 呼び出し元メソッドまたはプロパティ名
        Console.WriteLine("呼び出し元ファイルパス: " + callerFilePath);           // 呼び出し元ファイルパス
        Console.WriteLine("呼び出し元ファイル行番号: " + callerLineNumber);       // 呼び出し元行番号

        if(callerMemberName == ".ctor" ||  // コンストラクタから呼ばれた場合
           callerMemberName == ".cctor" || // 静的コンストラクタから呼ばれた場合
           callerMemberName == "Finalize") // デストラクタから呼ばれた場合
        {
            throw new Exception("コンストラクタで呼ぶなって言ってんだろ。このバカチンが!");
        }
    }
}

出力結果

ログの出力結果は次のようになります。C# での出力結果です。

メッセージ: コンストラクタ
呼び出し元メソッド・プロパティ: .ctor または button1_click
呼び出し元ファイルパス: c:\project\\WindowsFormsApplication\WindowsFormsApplication\Form1.cs
呼び出し元ファイル行番号: 19

ログに関するおっさんの小言

レベルに応じてファイルを分割

エラーログだとか、インフォログなどかレベルごとに出力するファイルを分けたログ管理することを否定するつもりは全くありませんが、前後の情報が必要なのであれば同じファイルに時系列で出力したほうが良いでしょう。システムの特性に応じてしっかりと検討することをおススメします。

エラーコード

多言語化などで威力を発揮するエラーコードですが、ログにエラーコードのみ出力している現場に遭遇したことがあります。エラーコードを元にエクセルで検索します。頻繁に見つからないことがありました。運用できないならやめておきましょう。理想は結果が伴わなければ、ただの幻想です。

ログを見ればソースコードが見える

ログに出力する内容は非常に重要です。過不足なくエレガントなログを目指しましょう。整理された情報が出力されたログは、大抵ソースコードも綺麗になります。綺麗なソースコードでない限り綺麗なログは出力できないでしょう。もちろん、そうでない場合もあります。そのようなソースはカロリー消費量がハンパないソースではないかと想像します。

参考
スポンサーリンク


Pocket

Leave a Comment

Your email address will not be published. Required fields are marked *