bst*_*ack 7 logging f# functional-programming
如果我有一个函数是模块的一部分,并且我想在函数内部进行日志条目,我必须手动打印函数名称空间和名称,例如
namespace MyModuleNamespace
module MyModule =
let AddTwoNums logger x y =
logger.Info("MyModuleNamespace.AddTwoNums - Start")
let res = x+y
logger.Info("MyModuleNamespace.AddTwoNums - End")
res
Run Code Online (Sandbox Code Playgroud)
有什么方法可以自动解决"MyModuleNamespace.AddTwoNums"的问题,因为它非常麻烦/容易出错,尤其是在重构代码时必须重命名函数和模块时
即使无法做到,有什么方法可以自动解决"AddTwoNums"是什么,即功能名称?
有几种方法可以做到这一点,我不确定哪种方法最适合您的方案.我过去使用的一种方法是从堆栈跟踪中获取信息:
let stackTrace = StackTrace()
let topFrame = stackTrace.GetFrame(0)
let currentFunction = topFrame.GetMethod()
printfn "%s.%s" currentFunction.DeclaringType.Name currentFunction.Name
Run Code Online (Sandbox Code Playgroud)
为了不必将这些行放在每个函数中,你可以创建一个inline执行此操作的函数,它将为您提供由于内联而调用函数的名称.
let inline getCurrentFunction () =
let stackTrace = StackTrace()
let topFrame = stackTrace.GetFrame(0)
let currentFunction = topFrame.GetMethod()
sprintf "%s.%s" currentFunction.DeclaringType.Name currentFunction.Name
Run Code Online (Sandbox Code Playgroud)
如果您不需要实际的名称空间/模块名称,或者愿意用文件名替换它,则可以使用一些特殊的属性来指示编译器为函数提供相应的参数:
open System.Runtime.CompilerServices
type Logger =
static member Trace(
msg: string,
[<CallerMemberName>] ?memberName: string,
[<CallerFilePath>] ?path: string,
[<CallerLineNumberAttribute>] ?line: int) =
printfn "%s from %s.%s on line %i" msg path.Value memberName.Value line.Value
module Foo =
let bar() =
Logger.Trace("hello")
// hello from c:\path\to\Logging.fsx.bar on line 32
Foo.bar()
Run Code Online (Sandbox Code Playgroud)
MS Docs对于“ 来电者信息”还有更多话要说。
请注意,当您从fsi内部运行时,行号仅在第一次时才是准确的。