5

我有一个日志接口,我使用一些有用的扩展方法对其进行了扩展,以便我可以传递格式和参数列表,以避免每次调用该方法时都必须使用字符串格式。(它也帮助我遵守 FXCops 文化信息规则)

所以我可以打电话:

logger.Debug("Created {0} with id {1}",typeof(MyObject).Name ,myObject.Id);

代替:

logger.Debug(string.Format("Created {0} with id {1}", typeof(MyObject).Name, myObject.Id));

我现在发现自己处于一个有点棘手的境地,因为在日志中获取一些关于日志写入位置的信息(例如文件、方法和行号)将非常有帮助。这可以通过整洁[CallerMemberName][CallerFilePath]、 和[CallerLineNumber]属性来实现。

logger.Debug("Created {0} with id {1}", typeof(MyObject).Name, myObject.Id);

然后会给我一个日志条目,例如:

“MyObjectProvider.cs,Provide,行:50 | 创建了 ID 为 1564 的 MyObject”

这里的问题是方法签名看起来像这样:

public static void Debug(this ILogger logger, string format [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0, params object[] args)

这是不可能的,因为[Caller*]属性使参数成为可选的,并且不适用于 args 参数。

我还尝试使用固定数量的字符串作为参数进行多种实现,如下所示:

public static void Debug(this ILogger logger, string format [CallerMemberName] string callerMemberName = "",string arg, string arg2 , ...etc... , [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)

但随后我收到编译器错误,提示“以下方法或属性之间的调用不明确”

我现在几乎已经放弃了这个问题,但我想,“也许 SO 可以为我找到解决方案”。所以在这里......是否可以同时使用两者params object[] args[CallerFilePath]以任何方式使用,或者是否有另一种方式来获得预期的结果?

4

5 回答 5

2

我遇到了同样的问题,但解决的方式有点不同。这不是最优雅的解决方案,但它可以工作并且相对干净:

public class SrcLoc
{
    public string sourceFile { get; set; }
    public int lineNumber { get; set; }
    public SrcLoc([CallerFilePath] string sourceFile = "",
                  [CallerLineNumber] int lineNumber = 0)
    {
      this.sourceFile = sourceFile;
      this.lineNumber = lineNumber;
    }
}
public class Logger
{
   public void Log(SrcLoc location,
                int level = 1,
                string formatString = "",
                params object[] parameters)
  {
     string message = String.Format(formatString, parameters);
  }
}
public MainTest
{
    public static void Main()
    {
        string file="filename";
        logger.Log(new SrcLoc(), (int)LogLevel.Debug, "My File: {0}", file);
    }
}
于 2014-05-27T18:38:32.620 回答
1

您不能将两者结合在方法签名中。您可以做的只是其中之一,然后传递null到您需要可选参数的地方,这对您有用吗?

Foo(s, null);
public void Foo(string s, params string[] sArray)
{

}

Foo(new string[] {""});
private static void Foo(string[] sArray,  string s = "")
{
}

或者

为什么不使用一个可以处理格式并使其成为可选的类呢?

public class LogArgs
{
  private string _formatString;
  private string[] _args;
  public LogArgs(string formatString, params string[] args)
  {
    _formatString = formatString;
    _args = args;
  }
  public override string ToString()
  {
    return string.Format(_formatString, _args);
  }
}

public void Foo(string mandatory, LogArgs optionalParam = null)
{
  //Do Stuff
}

Foo("", new LogArgs("{0} is formatted", ""));
于 2013-01-24T16:12:33.350 回答
1

我找到了另一种使用 StackTrace 获取所需信息的方法。它在优化代码中有点不安全,而且速度很慢,但出于调试目的,只要可以在发布版本中关闭它,它就可以很好地工作。

StackTrace stackTrace = new StackTrace();
var callerMember = stackTrace.GetFrame(1).GetMethod();
var callerMemberName = callerMember.Name;
var callerType = callerMember.ReflectedType.Name;
于 2013-01-25T09:16:15.087 回答
0

我发现的最优雅的方法(或最不优雅的方法)是创建一个具有所需名称的方法,该方法提取属性信息并返回一个Action委托。然后,您使用您实际要调用的签名设置此委托。

所以,从

public static void Debug(this ILogger logger, string format, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0, params object[] args)

创建委托

public delegate void LogDelegate(string format, params object[] args);

从您的方法调用返回:

public static void Debug(this ILogger logger, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
  return (format, args)
  {
    LogWithCallerSiteInfo(format, args, callerMemberName, callerFilePath, callerLineNumber, logAction);
  }
}

并使用捕获的数据调用辅助方法:

private static void LogWithCallerSiteInfo(string format, object[] args, string callerMemberName, string callerFilePath, int callerLineNumber, Action<string, object[]> logRequest)
    {
        if (args == null)
        {
            args = new object[0];
        }
        var args2 = new object[args.Length + 3];
        args.CopyTo(args2, 0);
        args2[args.Length] = sourceFile;
        args2[args.Length + 1] = memberName;
        args2[args.Length + 2] = lineNumber;

        logRequest(format + " [{callerFilePath:l}.{callerMemberName:l}-{callerLineNumber}]", args2);
    }

并拨打电话,因此:

logger.Debug()("Created {0} with id {1}",typeof(MyObject).Name ,myObject.Id);

因此,在使用方面,您插入了一个额外的 集合(),它捕获呼叫站点信息,并且集合集合在委托上进行呼叫。这就像我设法做到的一样整洁。

我已经重新创建了params添加捕获数据的数组,否则,(至少使用SeriLog,结果是不可预测的。

于 2016-03-13T17:43:40.990 回答
-2

将所有默认参数向右移动。

于 2013-01-24T16:12:25.303 回答