4

I have an application built on top of Servant, and now I want to add logging to the application. I skimmed through Haskell log packages, and I assume this one provides what I need: https://github.com/serokell/log-warper/blob/master/log-warper/examples/HowTo.md

One of the 'design patterns' often used for building apps with Servant is to use Reader monad, so I use this approach for the app: I have AppEnv which contains AppConfig. Usually, I could add something like a 'logger' entity to the AppEnv and so use it in handlers. Although, log-warper doesn't provide 'a logger', but it uses different approach instead (seems to be another monad, I assume; please, see the example on the link above). And so I can't figure out how to use this logger with Servant.

Here is my Servant-based app (using recent version of Servant, basing on examples from the doc: http://haskell-servant.readthedocs.io/en/stable/tutorial/Server.html#welcome-hoistserver):

data AppEnv = AppEnv { config :: Config }
type MyHandler = ReaderT AppEnv (ExceptT ServantErr IO)

startApp :: AppEnv -> IO ()
startApp env = do
  run 16384 (app env)

app :: AppEnv -> Application
app env = serve readerAPI (readerServer env)

readerAPI :: Proxy ReaderAPI
readerAPI = Proxy

readerToHandler :: AppEnv -> Reader AppEnv a -> Handler a
readerToHandler env r = return (runReader r env)

readerServer :: AppEnv -> Server ReaderAPI
readerServer env = hoistServer readerAPI (readerToHandler env) readerServerT

b :: Reader AppEnv Bool
b = do
  c <- config <$> ask
  let
    s = getServerConfig c
    p = getServerPort s
  return (p == 1)

getServerConfig :: Config -> ServerConfig
getServerConfig (Config s _) = s

getServerPort :: ServerConfig -> Int
getServerPort (ServerConfig _ p) = p

readerServerT :: ServerT ReaderAPI (Reader AppEnv)
readerServerT = a :<|> b where
    a :: Reader AppEnv Int
    a = return 1797

And here is the main function:

main :: IO ()
main = do
  config <- loadYamlSettings ["etc/config.yaml"] [] useEnv
  print (config :: Config)
  let
    env = AppEnv config
  startApp env

Now, how can I add log-warper to the application so I could initialize the logger (with launchFromFile I assume), and then use logging (logInfo, logError, etc.) in the app (in particular, in handlers, but possibly in other functions as well)?

Thanks

Shersh
  • 9,019
  • 3
  • 33
  • 61
fycth
  • 3,410
  • 25
  • 37
  • 2
    Hello! I'm the author of latest `log-warper` version on Hackage. And I really like the fact that you're using the library! But that version is no longer maintained by the organization. Tutorial works for the latest version, but `master` branch contains complete rewriting of `log-warper`. And I'm not aware of the future plans for this library. I could help you to resolve your issue, but probably it's not a good idea to use `log-warper` at first place. – Shersh Jul 10 '18 at 04:10
  • @Shersh many thanks for the details! So, if the project is not being supported anymore, may be you could advise some decent logging lib for haskell? (I mean, you seem to be good in the topic) – fycth Jul 10 '18 at 06:46
  • 1
    I haven't written decent logging library yet ;) But jokes aside. At our work we are using `katip` and `monad-logger` in different projects and we are still thinking which is one is better. I think you can take either `katip` or `monad-logger`, which one you like more. One significant difference between these libraries: `monad-logger` runs logging in separate thread. This might increase performance of logging but also results in weird bugs sometimes. – Shersh Jul 10 '18 at 11:56
  • 4
    Btw, if you're still interested, I finished library I was working on. You can even read the blog post: https://kowainik.github.io/posts/2018-09-25-co-log Please, feel free to share any feedback and ask any questions :) – Shersh Sep 25 '18 at 15:05

1 Answers1

1

General logging

If you want a generic logging tool with some sophisticated options katip looks like a good choice. There is even a small discussion about how to use it with servant. You just need to add a couple of parameters for katip to your Config type, initialize them, then you can log in your handlers.

Request logging

servant-server is built on top of wai and warp so you can reuse a lot of there tools. If you are just interested in logging data about requests to servant, you can use wai-logger without changing any of your types.

startApp would look something like this.

startApp :: AppEnv -> IO ()
startApp env = do
  withStdoutLogger $ \logger -> 
    runSettings (setPort 16384 $ setLogger logger $ defaultSettings) $ app env 
MCH
  • 2,124
  • 1
  • 19
  • 34