yesodweb / yesodweb/persistent
[RFC] Refactor logging to RIO-style
Nobody has claimed this yet.
- Dominant language
- Haskell
- Stars
- 486
- Forks
- 306
- PR merge metrics
- No merged PRs in 30d
Description
Right now, we use monad-logger for logging. This has a type class:
class (Monad m) => MonadLogger m where
monadLoggerLog :: ToLogStr msg => Loc -> Src -> LogLevel -> msg -> m ()
logInfoN :: MonadLogger m => Text -> m ()
Meanwhile, rio instead defines:
newtype LogFunc = LogFunc
{ unLogFunc :: Loc -> Src -> LogLevel -> LogStr -> IO ()
}
and logging functions look like:
logInfoN
:: (MonadReader env m, HasLogFunc env, MonadIO m)
=> Text -> m ()
as the type of logging actions.
The SqlBackend carries a LogFunc around (though it's not the same as the LogFunc in rio, it's a locally defined type synonym with the same idea). It's used to do logging internal to the library. But end users can't use it! If they want to perform logging in SqlPersistT, they need to incur an extra MonadLogger m constraint and delegate logging to the underlying monad.
type SqlPersistT = ReaderT SqlBackend
foo :: (MonadLogger m) => SqlPersistT m ()
foo = logInfoN "Hello"
In practice, this means concretizing to something like SqlPersistT (LoggingT IO) (). This gives you two logging channels - one for user logs and one for SqlBackend logs.
With rio's logging strategy, we wouldn't need to incur that extra constraint. We could define HasLogFunc SqlBackend and then logInfoN :: (MonadReader r m, HasLogFunc r, MonadIO m) => Text -> m () concretizes to logInfoN :: MonadIO m => Text -> SqlPersistT m ().
Well, that's nice, but why bother?
When we create the SqlBackend, we have a MonadLogger constraint. Then we use askLogFunc :: m LogFunc to put that in the SqlBackend. This allows the log function to "escape" the scope of the runLoggingT call that creates it! And, it's not immediately obvious where those [Debug#SQL] logging calls are coming from - it's not the MonadLogger constraint in your SQL functions, it's the MonadLogger constraint when you create the SqlBackend. Weird!
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by comparing the monad-logger and rio logging types described in the issue, then trace how SqlBackend stores and uses its LogFunc and how askLogFunc is used when creating it. The desired outcome is a RIO-style logging strategy that lets SqlPersistT logging use the backend's log function without an extra MonadLogger constraint or an escaping logging scope.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell
- Domain
- backend
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100