我如何做Haskell日志?

我正在尝试使用HSlogger获取有关我的程序的一些信息。 所以我添加下面的行到我的function

import Data.Word import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L import Data.Bits import Data.Int import Data.ByteString.Parser import System.Log.Logger import System.Log.Handler.Syslog importFile :: FilePath -> IO (Either String (PESFile )) importFile n = do warningM "MyApp.Component2" "Something Bad is about to happen." ... 

这工作正常,因为function是在IO内。 但是,当我添加一个类似的行到以下function:

 ... parsePES :: Parser PESFile parsePES = do header <- string "#PES" warningM "parsing header" ... return (PESFile ...) 

我得到一个types错误:

  Couldn't match expected type `Parser a0' with actual type `String -> IO ()' In the return type of a call of `warningM' In a stmt of a 'do' expression: warningM "parsing header" In the expression: do { header <- string "#PES"; warningM "parsing header"; ... 

我完全理解为什么 – parsePES在parsing器monad中,而不是IO monad。 我不明白的是该怎么做。 我需要一个monad变压器,所以我可以将parsing器monad和IO monad堆叠在一起吗? 我如何去做呢?

首先,快速免责声明:“日志logging”在通常的Haskell代码中通常是没有意义的,因为它假定某种顺序执行可能有意义,也可能没有意义。 确保区分logging程序执行的方式logging计算出的值 。 在严格的命令式语言中,这些大部分是相同的,但在Haskell中却不是。

也就是说,这听起来像是你想根据计算的值进行login,在已经是顺序和有状态的计算的环境中,这与大多数其他语言的login几乎一样。 但是,您确实需要monad来支持这样做的一些手段。 它看起来像你使用的parsing器是从HCodecs包 ,这似乎是相对有限的,不允许IO ,并没有被定义为单子变换器。

老实说,我的build议是考虑使用不同的parsing库。 Parsec往往是默认的select,我认为attoparsec是特定目的(这可能包括你在做什么)stream行。 要么你可以更容易地添加日志logging:Parsec是一个monad变换器,所以你可以把它放在IO之上,然后根据需要使用liftIO ,而attoparsec是围绕增量处理devise的,所以你可以将input分块并logging处理(尽pipe在实际parsing器内部logging可能会更尴尬)。 还有其他的select,但我不知道足够的细节来提出build议。 大多数基于parsing器的基于组合器的库往往具有相当相似的devise,所以我期望移植你的代码将是直截了当的。

最后的select是,如果你真的想坚持你所拥有的东西,那么现在就来看看你正在使用的parsing库的实现,然后推出你自己的面向IO的版本。 但是这可能不理想。


另外,作为一个附录,如果你实际上并不是真正的日志logging,而是追踪程序的执行过程作为开发的一部分,你可能会发现GHCi中内置的debugging器更加有用,或者是老式的通过Debug.Trace模块进行 printfdebugging。


编辑 :好吧,听起来像你有合理的理由考虑滚动自己的变化。 你粗略地想要的是一个ParserT monad变压器。 这里是Parser的当前定义:

 newtype Parser a = Parser { unParser :: S -> Either String (a, S) } 

Stypes是parsing器状态。 请注意,这大致是StateT S (Either String) a的硬编码版本StateT S (Either String) a

 newtype StateT sma = StateT { runStateT :: s -> m (a,s) } 

…其中Either String被视为一个错误单子。 ErrorT monad变压器也是这样做的:

 newtype ErrorT ema = ErrorT { runErrorT :: m (Either ea) } 

所以当前types相当于StateT S (ErrorT String Identity) ,你想要的是StateT S (ErrorT String IO)

它看起来像模块中的大部分函数都不会破坏Parser monad的内部,所以你应该能够简单地replacetypes定义,提供适当的types实例,编写你自己的runParser函数,并做好去。

免责声明:我是Logger haskell框架的作者。

尽pipe麦肯的回答非常详细,但并没有告诉人们,在提问的时候,Haskell缺乏一个通用的日志框架。 HSLogger现在是一个标准,但它提供了非常基本的日志loggingfunction,同时速度慢,不可扩展。 要清楚的是,这里是HSLogger的一些缺陷:

  1. 这很慢。 我的意思是说,每次你logging一条消息时,它都会parsing(以一种非常简单的方式)一个描述日志起源的string,并在引擎下使用一些存在的数据types,这必须在运行时引入一些性能开销。
  2. 它不允许在IO之外的其他monad中login,所以你必须使用WriterT或其他解决scheme来避免WriterT你的代码。
  3. 它不可扩展 – 你不能创build自己的优先级,定义自定义行为(如线程间日志logging)或编译时间日志过滤。
  4. 它不提供一些信息,例如放置日志的行号或文件名。 当然,要支持这些信息是很难的。

这就是说我很想介绍Logger的haskell框架 。 它允许高效和可扩展的日志logging,包括:

  1. login顺序纯代码(执行以及使用WriterT monad)
  2. 高级消息过滤(包括编译时过滤)
  3. 线程间的日志logging能力
  4. 提供了TemplateHaskell接口,允许logging其他细节,如文件编号或模块名称
  5. 是非常容易扩展的 – 所有的function都创build为一个简单的BaseLogger扩展,它不能做任何事情明智的。 要清楚 – 过滤function创build在less于20行作为logging器 – 变压器,您可以定义自己的变压器。 如何做到这一点在文档中描述。
  6. 在所有平台上默认提供彩色输出。

但是这个库很新,所以它可能缺less一些必要的function。 好的信息是,您可以轻松地自行创build此function,或通过在GitHub上报告请求来帮助我们改进它。

logging器是由我正在工作的公司( luna-lang.org )内部开发的,在我们正在创build的编译器中使用。