迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):wdanilo/haskell-logger开源软件地址(OpenSource Url):https://github.com/wdanilo/haskell-logger开源编程语言(OpenSource Language):Haskell 100.0%开源软件介绍(OpenSource Introduction):THIS LIBRARY NEEDS A NEW MAINTAINER. DUE TO SEVERAL TIME CONSTRAINTS IN MY LIFE AND NOT WORKING ACTIVELY IN HASKELL CURRENTLY, I CANNOT PROVIDE RELIABE SUPPORT HERE. IF ANYONE WOULD LIKE TO HELP WITH IT, ID LOVE TO GUIDE ON THE DESIGN AND HELP WITH THE MAINTENANCE.haskell-loggerFast & extensible logging framework for Haskell! OverviewLogger is a fast and extensible Haskell logging framework. Logger allows you to log any kind of messages in both The framework bases on the idea of logger transformer stack defining the way it works. You can build your own stack to highly tailor the behaviour to your needs, starting with such simple things, like logging messages to a list, ending on logging compile-time, priority-filtered messages from different threads and gathering them in other logger thread. DocumentationThe following documentation describes how to use the framework, how it works under the hood and how can you extend it. BasicsThis chapter covers all the basic information about logger transformers shipped with the framework. BaseLoggerLet's start with a very simple example: import System.Log.Simple
test = do
debug "a debug"
warning "a warning"
return "Done"
main = print $ runBaseLogger (Lvl, Msg) test
-- output: "Done" There are few things to note here:
As every logger transformer, main = print =<< runBaseLogger (Lvl, Msg, Time) test There is one very important design decision. All the logger transformers, apart from the base one, pass the newly registered log to underlying transformers. This way we can create a transformer that writes messages to disk and combine it with the one, that registers the logs in a list. There are some examples showing this behavior later in this document. WriterLogger
main = print $ (runBaseLogger (Lvl, Msg) . runWriterLoggerT) test As a result we get tuple, whose first element is the function's return value, while the second is list of all Log messages. For now the log message is not very friendly nested-tuple structure, but it will change in the next versions of the library. To be clear, the single log looks like this at the moment: Log {fromLog = (Data {recBase = Lvl, recData = LevelData 0 "Debug"},(Data {recBase = Msg, recData = "a debug"},()))} WriterLogger should work as fast as just HandlerLogger
import System.Log.Simple
test = do
addHandler $ printHandler Nothing
debug "a debug"
warning "a warning"
main = print =<< (runBaseLoggerT (Lvl, Msg) . runHandlerLoggerT defaultFormatter) test As a result, we get a colored output (on all platforms, including Windows): [Debug] a debug
[Warning] a warning
"Done" Ok, so what's happening here? The function For now only the FormattersIt is possible to define a custom message formatter. To do it, import the module defaultFormatter = colorLvlFormatter ("[" <:> Lvl <:> "] ") <:> Msg You might ask now, what are So what if we would like to output not only the message and its priority level, but also the module name and location of the message in the source file? Such logger is also defined and it's called defaultFormatterTH = colorLvlFormatter ("[" <:> Lvl <:> "] ") <:> Loc <:> ": " <:> Msg Its output is similar to: [Debug] Main.hs:4: a debug
[Warning] Main.hs:5: a warning PriorityLoggerThe test = do
addHandler $ printHandler Nothing
debug "a debug"
setPriority Debug
debug "another debug"
warning "a warning"
print =<< ( runBaseLoggerT (Lvl, Msg)
. runHandlerLoggerT defaultFormatter
. runPriorityLoggerT Warning
) test As the output we get: [Debug] another debug
[Warning] a warning ThreadedLoggerThe import System.Log.Simple
import qualified System.Log.Logger.Thread as Thread
import Control.Monad.IO.Class (liftIO)
test = do
addHandler $ printHandler Nothing
debug "a debug"
setPriority Debug
debug "another debug"
warning "a warning"
Thread.fork $ do
liftIO $ print "Threaded print"
debug "debug in fork"
liftIO $ print "End of the test!"
print =<< ( runBaseLoggerT (Lvl, Msg)
. runHandlerLoggerT defaultFormatter
. runPriorityLoggerT Warning
. runThreadedLogger
) test As the output we get: "Threaded print"
"End of the test!"
[Debug] another debug
[Warning] a warning
[Debug] debug in fork The output may of course vary, based on the way threads will be scheduled, because we use Exception handlingAll the loggers behave in a proper way, when an exception is raised. The exception will be evaluated after all necessary logging has been done: test = do
addHandler $ printHandler Nothing
debug "debug"
Thread.fork $ do
fail "oh no"
debug "debug in fork"
warning "a warning"
print =<< ( runBaseLoggerT (Lvl, Msg)
. runHandlerLoggerT defaultFormatter
. runThreadedLogger
) test Results in: [Debug] debug
Main.hs: user error (oh no) DropLoggerThe TemplateHaskell interfaceYou can use more advanced interface to be able to log more information, like module name or file number. To use it, import import System.Log.TH
test = do
addHandler $ printHandler Nothing
$(debug "a debug")
setPriority Debug
$(debug "another debug")
$(warning "a warning")
print =<< ( runBaseLoggerT (Lvl, Msg, Loc)
. runHandlerLoggerT defaultFormatterTH
. runPriorityLoggerT Warning
. runThreadedLogger
) test Which results in the following output: [Debug] Main:7: another debug
[Warning] Main:8: a warning Filtering messagesThe framework allows you to filter messages after they have been created. It is slower than using test = do
addHandler $ addFilter (lvlFilter Warning) $ printHandler Nothing
$(debug "a debug")
$(warning "a warning")
print =<< ( runBaseLoggerT (Lvl, Msg, Loc)
. runHandlerLoggerT defaultFormatterTH
) test Which results in: [Warning] Main:5: a warning Extending the loggerIt is possible to extend the logging framework in any way you want. All the functionality you have seen above are just simple logger transformers and you can modify them in a ton of ways or create custom ones. Custom priority levelsDefining a custom priority level is as easy as creating a new datatype that derives the data Level = Debug -- ^ Debug Logs
| Info -- ^ Information
| Notice -- ^ Normal runtime conditions
| Warning -- ^ General Warnings
| Error -- ^ General Errors
| Critical -- ^ Severe situations
| Alert -- ^ Take immediate action
| Panic -- ^ System is unusable
deriving (Eq, Ord, Show, Read, Enum) Custom data providersIt is possible to define custom data providers. Let's look how the data Msg = Msg deriving (Show)
type instance DataOf Msg = String That's it. There is no more code for it. After creating such new datatype you can create a pretty printing instance for it and use it just like all other data even in the formatter builder!
But how the data is being registered? Let's look how the debug = log empty Debug The data Foo = Foo deriving (Show)
type instance DataOf Foo = Int
debugFoo i = log (appData Foo i empty) Debug
instance PPrint Foo where
pprint = text . show
fooFormatter = defaultFormatter <:> " (" <:> Foo <:> ")"
test = do
addHandler $ printHandler Nothing
debugFoo 7 "my custom debug"
print =<< ( runBaseLoggerT (Lvl, Msg, Foo)
. runHandlerLoggerT defaultFormatter
) test Which results in: [Debug] my custom debug (7) A new function In fact, if we look how the log function is defined, we will find some similarities: log rec pri msg = do
[...]
appendRecord $ appData Lvl (mkLevel pri)
$ appData Msg msg
$ rec Monad data providersWhat happens when such data is not provided when constructing the message? Like import Data.Time.Clock (getCurrentTime, UTCTime)
import Data.Time.Format (formatTime, defaultTimeLocale)
data Time = Time deriving (Show)
type instance DataOf Time = UTCTime
instance MonadIO m => DataGetter Time m where
getData = do liftIO $ Data Time <$> getCurrentTime
instance Pretty UTCTime where
pretty = text . formatTime defaultTimeLocale "%c"
defaultTimeFormatter = colorLvlFormatter ("[" <:> Lvl <:> "] ") <:> Time <:> ": " <:> Msg That's it! You can use any function inside - both pure as well as IO. If you use pure function, just return the value. If you will execute Custom logger transformersIt's also straightforward to define custom logger transformers. They have to be instances of some datatypes. To know more about it, look at example transformers inside the ConclusionThis is a new logging library written for purposes of fast logging between threads. It is still under development, so you can expect some api changes. There is still some functionality missing, like file handlers, but as you have seen, it is easy to define such. Any help would be welcome. Happy logging! ![]() |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论