如何将 Serilog.Log.ForContext 与 F# 函数或 C# 方法一起使用

How to use Serilog.Log.ForContext with F# function or C# Method

使用 Serilog 和 F# 如何将 .ForContext 与函数一起使用?它似乎只接受类型 classes:

type A()=
  log = Serilog.Log.ForContext<A>() // ✅
let f _ =
  log = Serilog.Log.ForContext<f>() // compile error
  log = Serilog.Log.ForContext(f.GetType()) // the information is not the same as A where it gives module + type path

添加 F# 函数或 C# 方法上下文的正确方法是什么(我认为这应该是同一个问题)?


到目前为止,我的解决方案一直在使用这个 return SourceContext 的函数,与使用 class (moduleFullName+className):

完全一样
let logFromFn _ =
  Serilog.Log.ForContext
    ("SourceContext",
     StackTrace().GetFrame(1).GetMethod().DeclaringType
     |> fun dt -> dt.FullName + "+" + dt.Name)
  1. 所述,它可能很昂贵,我在整个程序中会调用一次的地方使用它。
  2. 它在模块内部的 F# 函数中运行良好,但在入口点文件中效果不佳,它 returns program+program

方法没有与之关联的 Type,因此您不能使用 ForContext<T>()ForContext(Type type) 重载。由于 F# 模块中的函数被编译为静态 class(模块)内的静态方法,因此这同样适用于它们。作为一种简单的替代方法,您可以自己设置 SourceContext 属性:

let propName = Serilog.Core.Constants.SourceContextPropertyName
Log.ForContext(propName, "App.Class.Method")
Log.ForContext(propName, "App.Module.function")

如果你想稳健地进行重命名操作,你可以这样做:

class Class
{
    public void Method()
    {
        var source = $"{typeof(Class).FullName}.{nameof(Method)}";
        var logger = Log.ForContext(Core.Constants.SourceContextPropertyName, source);
        // ...
    }
}
module Module =
    // It's a bit fiddly to get the module type in F#, but here's one
    // approach (credit 
    type private Marker = interface end
    let private moduleType = typeof<Marker>.DeclaringType

    let loggerFor name =
        let source = sprintf "%s.%s" moduleType.FullName name
        let propName = Serilog.Core.Constants.SourceContextPropertyName
        Log.ForContext(propName, source)
    

    // Requires the `nameof` operator from F# 4.7 preview.
    // The function needs to be recursive since it is referenced when calling `nameof`.
    let rec func () =
        let logger = loggerFor (nameof func)
        // ...

我要补充一点,通常 class/module 名称足以确定消息的来源,因此我建议从源上下文中省略 method/function 名称。这将允许您使用 ForContext<T>()ForContext(Type type) 重载,因此会大大简化事情。

F# 中的关键问题是您不能使用 module 标识符作为类型字面量,即虽然您可以说 Log.ForContext<TypeName>(),但您不能使用 Log.ForContext<ModuleName>().

不幸的是,在语言支持使用模块标识符作为 Type and/or 添加 typeof(ModuleName) 设施之前,最好的解决方法是 to plonk this ugly boilerplate at the top of your module definition:

type private Marker = interface end
let private moduleType = typeof<Marker>.DeclaringType
let log = Log.ForContext(moduleType)

另一个答案显然完整地回答了问题。这个回答了与 IMO 不同的问题:

与 C# 一样,在日志上下文中包含 member/function 名称不适合 structured logging 的惯用用法 - 该消息应该有意义,与您需要知道什么无关function/member 它嵌入在