如何使用 NLog 以功能方式登录 F#

Mar*_*son 3 logging f# nlog

我一直在研究日志记录选项并选择 NLog 并记录到数据库

我对函数式编程相当陌生,并且有 C# OOP 背景。

如何在 F# 中以函数式方式实现日志记录?
我是吗

  • 在顶层创建记录器,然后将其传递给每个函数
  • 根据需要通过静态方法访问记录器(显然每次实例化记录器都会有一些开销 - 但也许这不是什么大问题)
  • 还有别的事吗?

我想避免仅仅因为我的项目很小而使用商业日志记录选项。

谢谢你的时间。

Gen*_*hak 5

通过静态方法访问记录器

这种方法对于实验应用程序来说很好,但一般来说它是服务定位器的实例,通常被认为是反模式。

更可靠的替代方案是依赖注入(DI)。由于 F# 是混合语言,因此您可以使用 OOP 概念并以 OOP 的常见方式应用 DI 模式。但是,如果您想使用函数式风格,F# 中的 DI 没有单一的通用函数式模式。

在顶层创建记录器并将其传递给每个函数

这只是在函数代码中使用 DI 最直接的方法。这种方法可能适用于小型应用程序,但是如果除了日志记录之外还有其他横切问题(例如配置),则最终可能会出现参数爆炸,从而使代码变得混乱。有不同的方法来解决这个问题。我已经解决了这里描述的方法。

您可以通过以下方式应用此方法。

您的业​​务逻辑函数可能如下所示:

let doSomething env = 
    let logger = getLogger env
    logger.Debug("Do something")
    // Do something
Run Code Online (Sandbox Code Playgroud)

这意味着logger引用是通过env参数提供并通过函数访问的getLogger

此代码应引用通用日志记录模块,该模块可能如下所示:

[<Interface>]
type ILoggerProvider =
    abstract Logger: ILogger

let getLogger (env: #ILoggerProvider) = env.Logger
Run Code Online (Sandbox Code Playgroud)

env请注意, 的参数类型doSomething会自动推断为#ILoggerProvider(即继承ILoggerProvider)。

想象一下,稍后在相同的业务逻辑中,我们需要访问配置(以及日志记录)。在这种情况下,我们不需要新参数,而是env以类似的方式从同一参数访问配置:let configuration = getConfiguration env,前提是还创建了用于配置的公共模块:

[<Interface>]
type IConfigurationProvider =
    abstract Configuration: IConfiguration

let getConfiguration (env: #IConfigurationProvider) = env.Configuration
Run Code Online (Sandbox Code Playgroud)

env在这种情况下,的参数类型doSomething会自动重新推断, 'a (requires 'a :> ILoggerProvider and 'a :> IConfigurationProvider) 这意味着它应该继承ILoggerProviderIConfigurationProvider

在应用程序级别,您需要创建环境实例并将其传递给业务逻辑,以便提供对日志记录和配置(以及可能的其他服务)的访问。您可以通过以下方式进行操作:

[<Interface>] 
type IEnvironment = 
    inherit ILoggerProvider 
    inherit IConfigurationProvider
    
let createEnvironment (logger, configuration) = 
    { new IEnvironment with
        member self.Logger = logger 
        member self.Configuration = configuration }

let createLogger () = // create logger...
    
let createConfiguration () = // create configuration...

let logger = createLogger ()
let configuration = createConfiguration ()
let env = createEnvironment (logger, configuration)

// Call business logic
doSomething env
Run Code Online (Sandbox Code Playgroud)

请注意,很容易使用新的横切关注点来扩展此代码,因为每个业务逻辑功能仅了解所使用的服务(例如日志记录、配置),并且对IEnvironment接口及其所有内容一无所知。

您可以在这里找到我的示例的完整版本。

您可以在此处找到有关 F# 中 DI 方法的更全面概述。