From 9d52ad228bd44d628937dd8219dd5dfccc5cc51a Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Tue, 2 Apr 2024 09:58:45 +0300 Subject: [PATCH] Fix example of overriding Logger (#1944) * Fix example of overriding Logger * Start add guide * Log also anon users * tabs * Complete example * Apply suggestions from code review * Improve code readability * Code review fixes * Apply suggestions from code review --- Guide/logging.markdown | 51 +++++++++++++++++++++++++++++++++++++++ IHP/Controller/Context.hs | 37 +++++++++++++++++++--------- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/Guide/logging.markdown b/Guide/logging.markdown index 883bc2a54..f9c6f8a7b 100644 --- a/Guide/logging.markdown +++ b/Guide/logging.markdown @@ -198,5 +198,56 @@ Would log a timestamp as: > Sunday, 2020-1-31 22:10:21 +### Decorating the Logs with the User ID +You can override the default logger and have it decorated with additional information. A typical use case is adding the current user's ID or name to the log messages. + +```haskell +-- Web/FrontController.hs + +-- Add imports +import IHP.Log.Types as Log +import IHP.Controller.Context + +instance InitControllerContext WebApplication where + initContext = do + initAuthentication @User + -- ... your other initContext code + + putContext userIdLogger + +userIdLogger :: (?context :: ControllerContext) => Logger +userIdLogger = + defaultLogger { Log.formatter = userIdFormatter defaultLogger.formatter } + where + defaultLogger = ?context.frameworkConfig.logger + + +userIdFormatter :: (?context :: ControllerContext) => Log.LogFormatter -> Log.LogFormatter +userIdFormatter existingFormatter time level string = + existingFormatter time level (prependUserId string) + +prependUserId :: (?context :: ControllerContext) => LogStr -> LogStr +prependUserId string = + toLogStr $ userInfo <> show string + where + userInfo = + case currentUserOrNothing of + Just currentUser -> "Authenticated user ID: " <> show currentUser.id <> " " + Nothing -> "Anonymous user: " +``` + +From your controller you can now add a log message + +```haskell + action PostsAction = do + Log.debug ("This log message should have user info" :: Text) + -- Rest of the action code. +``` + +In your log output, you will see the user info prepended to the log message. + +``` +[30-Mar-2024 18:28:29] Authenticated user ID: 5f32a9e3-da09-48d8-9712-34c935a72c7a "This log message should have user info" +``` \ No newline at end of file diff --git a/IHP/Controller/Context.hs b/IHP/Controller/Context.hs index 0ddb744cd..30ce7411e 100644 --- a/IHP/Controller/Context.hs +++ b/IHP/Controller/Context.hs @@ -143,21 +143,36 @@ instance HasField "frameworkConfig" ControllerContext FrameworkConfig where -- -- This can be useful to customize the log formatter for all actions of an app: -- --- > import IHP.Log.Types +-- > -- Web/FrontController.hs +-- > +-- > import IHP.Log.Types as Log -- > import IHP.Controller.Context --- > +-- > -- > instance InitControllerContext WebApplication where -- > initContext = do --- > let defaultLogger :: Logger = ?context.frameworkConfig.logger --- > let withUserIdLogger :: Logger = { Log.formatter = userIdFormatter defaultLogger.formatter } --- > putContext withUserIdLogger --- > --- > userIdFormatter :: (?context :: Context) => Log.LogFormatter -> Log.LogFormatter +-- > -- ... your other initContext code +-- > +-- > putContext userIdLogger +-- > +-- > userIdLogger :: (?context :: ControllerContext) => Logger +-- > userIdLogger = +-- > defaultLogger { Log.formatter = userIdFormatter defaultLogger.formatter } +-- > where +-- > defaultLogger = ?context.frameworkConfig.logger +-- > +-- > +-- > userIdFormatter :: (?context :: ControllerContext) => Log.LogFormatter -> Log.LogFormatter -- > userIdFormatter existingFormatter time level string = --- > existingFormatter time level (prependUserId string) --- > --- > preprendUserId :: (?context :: Context) => Text -> Text --- > preprendUserId string = "userId: " <> show currentUserId <> " " <> string +-- > existingFormatter time level (prependUserId string) +-- > +-- > prependUserId :: (?context :: ControllerContext) => LogStr -> LogStr +-- > prependUserId string = +-- > toLogStr $ userInfo <> show string +-- > where +-- > userInfo = +-- > case currentUserOrNothing of +-- > Just currentUser -> "Authenticated user ID: " <> show currentUser.id <> " " +-- > Nothing -> "Anonymous user: " -- -- This design mistake should be fixed in IHP v2 instance HasField "logger" ControllerContext Logger where