はじめに
ログが必要というのはわかっていてもログの内容を記載したり、ログを追加する際に使用したコードをリリースでは消したいときにifdefで囲ったり、nameofで関数名を取得したりやっていることが多いと思います(してました)。その手間を省くためにオリジナルのデバッグログを紹介します。
環境
VS2022 C# .Netframework4.8
使用する属性について
・ConditionalAttribute
「ConditionalAttribute」は指定されているdefine宣言されているばあい関数の呼び出しを行い、宣言されていない場合は呼び出し自体の呼び出しをなくします。
下記例の場合「DEBUG」がdefine宣言されている場合処理を実行します。VSでの開発環境ではデバッグにすると勝手につくので便利です。
[Conditional("DEBUG")]
public static void DebugLog(string message)
{
//ログ処理
}
注意事項として引数に使用した関数も呼ばれないということには意識しなければいけません。
確実にやらなければいけない処理を内部でコールするのはNG行為となります。
private static _log="INI";
public static void main()
{
DebugLog(CreateLolog);
Console.WriteLine(_log);
//DEBUG宣言あり:LOG
//DEBUG宣言なし:INI
}
public static string CreateLolog()
{
_log="LOG";
return _log;
}
・CallerMemberNameAttribute
呼び出し元の名前を取得する。
・CallerFilePathAttribute
呼び出し元のファイルパスを取得する。
・CallerLineNumberAttribute
呼び出し元の行数を取得する。
これらの属性は関数の引数に宣言できる属性となります。また、コンパイル時に入るため処理時間もきにすることもあまりありません。
public static void MethodStartLog([CallerMemberName]string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber]int sourceLineNumber = 0)
{
Console.WriteLine(memberName);//呼び出し元メソッド名
Console.WriteLine(sourceFilePath);//呼び出し元ファイルパス
Console.WriteLine(sourceLineNumber.ToString());//呼び出し元行数
}
コード
内容
「Conditional」属性の性質であるコンパイル時に判断される点があるため性的「static」にすることによってライブラリにしてもそれを使うプロジェクトのdefine宣言に影響します。
今回は使いまわせるようライブラリに落とし、よく使うメソッドの開始、終了を独立した関数にすることによって、呼び出しを楽にしています。
また、開始時には引数をobjectの配列にして中の値をすべてログに出せる機能作成しています。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
namespace LoggerLibrary
{
public static class UserDebug
{
internal enum LogTypes
{
None = 0,
Start,
End,
Error
}
private static int s_methodMaxLength=0;
private static int s_fileMaxLength=0;
public static string DateTimeFormat = "yyyy/MM/dd HH:mm:ss.fff";
/// <summary>
/// デバッグ用メソッド開始ログ
/// </summary>
/// <param name="message">メッセージ</param>
/// <param name="memberName">メソッド名(入力不要)</param>
/// <param name="sourceFilePath">ファイルパス(入力不要)</param>
/// <param name="sourceLineNumber">ソースデータ行(入力不要)</param>
/// <remarks>DEBUG時のみ実行</remarks>
[Conditional("DEBUG")]
public static void MethodStartLog(string message="",
[CallerMemberName]string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber]int sourceLineNumber = 0)
{
_DebugLog(DateTime.Now, LogTypes.Start,
(string.IsNullOrWhiteSpace(message) ? "" : $"[Message:{message}]"),
memberName, Path.GetFileName(sourceFilePath), sourceLineNumber);
//DebugLog(DateTime.Now,$"[{memberName} : Start],{(string.IsNullOrWhiteSpace(message) ? "" : $"[Message:{message}]")},[File:{Path.GetFileName(sourceFilePath)}],[Line:{sourceLineNumber}]");
}
/// <summary>
/// デバッグ用メソッド開始ログ
/// </summary>
/// <param name="message">メッセージ</param>
/// <param name="memberName">メソッド名(入力不要)</param>
/// <param name="sourceFilePath">ファイルパス(入力不要)</param>
/// <param name="sourceLineNumber">ソースデータ行(入力不要)</param>
/// <remarks>DEBUG時のみ実行</remarks>
[Conditional("DEBUG")]
public static void MethodStartLog(string message,
object[] args ,
[CallerMemberName]string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber]int sourceLineNumber = 0)
{
var hashs = new List<int>();
string argText = ToStringModule.ListToString(args,ref hashs);
_DebugLog(DateTime.Now, LogTypes.Start,
$"{(string.IsNullOrWhiteSpace(message) ? "" : $"[Message:{message}]")}," +
$"{(string.IsNullOrWhiteSpace(argText) ? "" : $"[Args:{argText}]")}",
memberName,
Path.GetFileName(sourceFilePath), sourceLineNumber);
}
/// <summary>
/// デバッグ用メソッド終了ログ
/// </summary>
/// <param name="message">メッセージ</param>
/// <param name="memberName">メソッド名(入力不要)</param>
/// <param name="sourceFilePath">ファイルパス(入力不要)</param>
/// <param name="sourceLineNumber">ソースデータ行(入力不要)</param>
/// <remarks>DEBUG時のみ実行</remarks>
[Conditional("DEBUG")]
public static void MethodEndLog(string message = "",
[CallerMemberName]string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber]int sourceLineNumber = 0)
{
_DebugLog(DateTime.Now, LogTypes.End,
(string.IsNullOrWhiteSpace(message) ? "" : $"[Message:{message}]"),
memberName, Path.GetFileName(sourceFilePath), sourceLineNumber);
}
/// <summary>
/// デバッグ用エラーログ
/// </summary>
/// <param name="ex">エラー情報</param>
/// <param name="memberName">メソッド名(入力不要)</param>
/// <param name="sourceFilePath">ファイルパス(入力不要)</param>
/// <param name="sourceLineNumber">ソースデータ行(入力不要)</param>
/// <remarks>DEBUG時のみ実行</remarks>
[Conditional("DEBUG")]
public static void ErrorLog(Exception ex,
[CallerMemberName]string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber]int sourceLineNumber = 0)
{
_DebugLog(DateTime.Now, LogTypes.None,
($"Type[{ex.GetType().Name}][Message:{ex.Message}]"),
memberName, Path.GetFileName(sourceFilePath), sourceLineNumber);
}
/// <summary>
/// デバッグ用ログ
/// </summary>
/// <param name="message">メッセージ</param>
/// <param name="memberName">メソッド名(入力不要)</param>
/// <param name="sourceFilePath">ファイルパス(入力不要)</param>
/// <param name="sourceLineNumber">ソースデータ行(入力不要)</param>
/// <remarks>DEBUG時のみ実行</remarks>
[Conditional("DEBUG")]
public static void DebugLog(string message,
[CallerMemberName]string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber]int sourceLineNumber = 0)
{
_DebugLog(DateTime.Now, LogTypes.None,
(string.IsNullOrWhiteSpace(message) ? "" : $"[Message:{message}]"),
memberName, Path.GetFileName(sourceFilePath), sourceLineNumber);
}
/// <summary>
/// デバッグ用ログ
/// </summary>
/// <param name="message">メッセージ</param>
/// <remarks>DEBUG時のみ実行</remarks>
[Conditional("DEBUG")]
private static void _DebugLog(DateTime dateTime,LogTypes type,string message, string memberName,
string sourceFileName ,
int sourceLineNumber)
{
if(memberName.Length>s_methodMaxLength)
s_methodMaxLength=memberName.Length;
var member=memberName.PadLeft(s_methodMaxLength, ' ');
if (sourceFileName.Length>s_fileMaxLength)
s_fileMaxLength=sourceFileName.Length;
var file=sourceFileName.PadLeft(s_fileMaxLength, ' ');
var text="";
switch (type)
{
case LogTypes.None:
text=$"[{dateTime.ToString(DateTimeFormat)}]," +
$"[{member}],"+
$"[File:{file}]," +
$"[Line:{sourceLineNumber.ToString().PadLeft(5,' ')}]," +
$"[DEBUG]," +
$"{message}";
break;
case LogTypes.Start:
text=$"[{dateTime.ToString(DateTimeFormat)}]," +
$"[{member}],"+
$"[File:{(file)}]," +
$"[Line:{sourceLineNumber.ToString().PadLeft(5, ' ')}]," +
$"[START]," +
$"{message}";
break;
case LogTypes.End:
text=$"[{dateTime.ToString(DateTimeFormat)}]," +
$"[{member}],"+
$"[File:{(file)}]," +
$"[Line:{sourceLineNumber.ToString().PadLeft(5, ' ')}]," +
$"[END ]," +
$"{message}";
break;
case LogTypes.Error:
text=$"[{dateTime.ToString(DateTimeFormat)}]," +
$"[{member}],"+
$"[File:{(file)}]," +
$"[Line:{sourceLineNumber.ToString().PadLeft(5, ' ')}]," +
$"[ERROR]," +
$"{message}";
break;
}
Console.WriteLine(text);
}
}
}
以上