Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast logger migration #274

Merged
merged 65 commits into from
Mar 31, 2023
Merged

Conversation

RiugaBachi
Copy link
Contributor

@RiugaBachi RiugaBachi commented Mar 29, 2023

This PR implements the changes discussed in #257 as well as some concerns discussed privately, namely:

  • Configurable logging to stderr (via rotate-logs config option)
    • This can be useful i.e. when keter is managed via a systemd, which automatically captures and tails stderr output.
  • Implementation of a KeterM cfg a monad to encapsulate a (logging + config) context, which crops up prominently.
    • For contexts that only require the logging capability, we can simply leave cfg polymorphic!
  • Refactoring of relevant modules to use the new KeterM monad to:
    • Allow access to MonadLogger -related capabilities via the internal LoggingT representation.
    • Cut down on redundant passing of the same configs and loggers everywhere.
  • Changing of logging calls to use TH-based logging from monad-logger, giving us access to Loc (module+line number) info at each call site.
  • Reformatting of internal log messages to make use of the new info with format: {keter|}$time|$module$:$line_num|$log_level> $msg
    Where {...} signifies a part strictly only present when logging to stderr.
  • Reformatting of external (process monitor, individual apps) logs to be tagged with their name:
    {process-monitor> }$msg
    and
    {app-$name> }$msg
    respectively. (Same {...} semantics as above)
    • This was a minor design compromise as we would have had to incur a big lift IO / unlift IO mess to propagate LoggingT to the whole Keter.Conduit.Process.Unix module. The extra info here is also not as useful as it is for internal logs. Perhaps in the future log formatting can be streamlined in a separate PR.
  • Removal of the LogMessage ADT and its gigantic Show instance, which was a bit overkill and clunky for logging.
  • Removal of the old in-house logging solution.
  • Removal of the old logEx utility.

As was discussed privately, log rotation naming and directory scheme have had to be changed due to limitations in fast-logger. Namely, that it dictates the naming scheme of rotated logs with no straightforward way of customizing it to preserve backwards compatibility. On top of the public keter API changes due to the KeterM refactor, package version has been bumped to 2.1.0 to indicate a breaking change.

Relevant parts of the test suite have also been rewritten to comply with the API changes, but overall semantics are still retained.


TODO:

  • Resolve individual todo comments (@jappeace could use your input on some assumptions 🙏) ✔️
  • Version bounds on monad-logger / fast-logger / unliftio-core? ✔️
  • Refactoring ✔️
    • Possibly some things we can move to LogFile module ✔️
      -> Generalized logger creation in Keter.Main and Keter.App to Keter.Logger.createLoggerViaConfig
    • Rename/change dir of LogFile module? ✔️
      -> Moved to Keter.Logger
    • Investigate if we really still need a TVar for RotatingLog since fast-logger was implemented with the assumption of multithreaded contexts.
    • Formatting
  • Propagate a logging context around ✔️
  • Change individual log calls to source line log splices based on the show instance for the log message ADT. ✔️
  • Then remove said ADT. ✔️
  • Fix stubbed out parts of the test suite ✔️
  • Update changelog again ✔️
  • Bump version in cabal file ✔️

Rotated logs will now simply have `.1` `.2` ascending appended to the name of the base logs
rather than be named after the date and time they were rotated at:
`logs/keter/20230413_231415.log` -> `logs/keter/keter.log.1`
Please update anything that depended on the old log naming and directory conventions accordingly.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does this work with several apps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed a typo here actually 🙏 Added some examples for app log changes just now.

@jappeace
Copy link
Collaborator

jappeace commented Mar 29, 2023

I think deleting that log message datatype would be nice

data LogMessage

I've no idea why this was made typesafe, I hardly can imagine a situation you want to parse and re-use log messages.


anyway, like I said you don't need an instance because you can use LoggingT: https://hackage.haskell.org/package/monad-logger-0.3.39/docs/Control-Monad-Logger.html#t:LoggingT

fast logger provides you with LogStr -> IO (), so you just have to tell it how to deal with those other args and Loc -> LogSource -> LogLevel (eg format them into a LogStr for example and concat the given LogStr, or just ignore them).
Then we can use:

x :: IO ()
x = runLoggingT (myFormatting fastlogHandle) $ $logInfo "messages from LogMessage as normal text"

myFormatting :: (LogStr -> IO ()) -> (Loc -> LogSource -> LogLevel -> LogStr -> IO ())

@RiugaBachi
Copy link
Contributor Author

RiugaBachi commented Mar 30, 2023

Oh lol my eyes completely skipped over the unwrapper being exposed. Thanks for the heads up 👍

Though, I was considering my options and still think the best path forward would be to propagate a new KeterM monad context to everything. This would allow for extensibility if at some point we decide to throw in a ReaderT KeterConfig layer etc. for other resources that seem to be passed around to everything along with the logger. I couldn't see a benefit to generalizing everything to MonadLogger/IO/etc. constraints since keter (main) stuff forms a pretty linear call graph and the polymorphism wouldn't be useful in practice.

import Control.Monad.Reader (MonadReader, ReaderT, runReaderT, ask
, withReaderT)

-- | The top-level keter context monad, carrying around the main logger and some locally relevant configuration structure.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also refer to a relevant blogpost if you have one in mind for this design.
for example https://www.fpcomplete.com/blog/2017/06/readert-design-pattern/

this allows newer people to figure out what goes on with all the boilerplate :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I know of one off the top of my head more specialized than that, so if you don't mind I'll steal that link lol. Added

keter.cabal Outdated
@@ -62,6 +62,9 @@ Library
, attoparsec >= 0.10
, http-client >= 0.5.0
, http-conduit >= 2.1
, fast-logger
, monad-logger
, unliftio-core
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we should add upper bounds

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Terminate -> return noWorker

launchWorker :: AppManager
-> AppId
-- | TODO: Propagate `KeterM` up through here?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this appears to be done already

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

-- , show c
-- ]
-- loc <- fmap showLoc TH.qLocation
-- [|(. ExceptionThrown (pack $(TH.lift loc)))|]
Copy link
Collaborator

@jappeace jappeace Mar 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, just delete it. we've git to conjure up past code, which rarely happens.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

e' <-
case e of
FSN.Removed fp _ _ -> do
--log $ WatchedFile "removed" (fromFilePath fp)
Copy link
Collaborator

@jappeace jappeace Mar 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we re-add thes log lines as monad logger ones?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! I used my fuzzy find plugin fearing this but I guess a few escaped my eyes

Copy link
Collaborator

@jappeace jappeace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oki, I'll merge this tonight, you may fixup whatever you want meanwhile. 🎉

@jappeace jappeace merged commit dcdfd80 into snoyberg:master Mar 31, 2023
@jappeace
Copy link
Collaborator

thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants