我一直在研究日志记录选项并选择了 NLog 并记录到数据库
我是函数式编程的新手,有 C# OOP 背景。
我如何在 F# 中以功能方式实现日志记录?
我会吗
- 在顶层创建记录器,然后将它传递给每个函数
- 根据需要通过静态方法访问记录器(显然每次实例化记录器都会有一些开销 - 但也许这不是什么大问题)
- 还有别的吗?
我想避免使用商业日志记录选项,因为我的项目很小。
感谢您的宝贵时间。
最佳答案
Access the logger through a static method
这种方法适用于实验性应用,但一般来说它是 Service Locator 的实例这通常被认为是反模式。
更可靠的替代方案是依赖注入(inject) (DI)。由于 F# 是混合语言,您可以使用 OOP 概念并以 OOP 的通用方式应用 DI 模式。但是,如果您想使用函数式风格,F# 中的 DI 没有单一的通用函数式模式。
Create the logger at the top level and just pass it in to every function
这只是在功能代码中使用 DI 的最直接方式。这种方法可能适用于小型应用程序,但是如果除了日志记录之外还有其他横切关注点(例如配置),您可能会以参数爆炸而告终,这会使代码变得困惑。 有不同的方法来解决这个问题。我已经解决了描述的方法 here .
您可以通过以下方式应用此方法。
您的业务逻辑函数可能如下所示:
let doSomething env =
let logger = getLogger env
logger.Debug("Do something")
// Do something
这意味着logger
引用通过env
参数提供,并通过函数getLogger
访问。
此代码应引用公共(public)日志记录模块,它可能如下所示:
[<Interface>]
type ILoggerProvider =
abstract Logger: ILogger
let getLogger (env: #ILoggerProvider) = env.Logger
注意 doSomething
的 env
参数类型被自动推断为 #ILoggerProvider
(即继承 ILoggerProvider
).
设想稍后在相同的业务逻辑中我们需要访问配置(以及日志记录)。在这种情况下,我们不需要新参数,而是以类似的方式从相同的 env
参数访问配置:let configuration = getConfiguration env
,前提是用于配置的通用模块是也创建了:
[<Interface>]
type IConfigurationProvider =
abstract Configuration: IConfiguration
let getConfiguration (env: #IConfigurationProvider) = env.Configuration
在这种情况下,doSomething
的 env
参数类型会自动重新推断为
'a(需要 'a :> ILoggerProvider 和 'a :> IConfigurationProvider)
这意味着它应该同时继承 ILoggerProvider
和 IConfigurationProvider
。
在应用程序级别,您需要创建环境实例并将其传递给业务逻辑,以便提供对日志记录和配置(以及可能的其他服务)的访问。您可以通过以下方式进行:
[<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
请注意,使用新的横切关注点很容易扩展此代码,因为每个业务逻辑功能只知道使用过的服务(例如日志记录、配置),而对 IEnvironment
界面及其所有内容。
您可以找到我的示例的完整版本 here .
您可能会发现 F# 中 DI 方法的更全面概述 here .
关于logging - 如何使用 NLog 以功能方式登录 F#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70040464/