Search code examples
haskellloggingamazon-cloudwatchamazon-cloudwatchlogsservant

Correct format for AWS CloudWatch


I use Servant and colog in a Docker container which runs on AWS ECS.

The HTTP requests are perfectly logged into AWS CloudWatch but not my manual logging.

I have initiated my WAI server that way:

main :: IO ()
main = do
  withStdoutLogger $ \aplogger -> do
    let settings = setPort 8080 $ setLogger aplogger defaultSettings
    initializeApplication >>= runSettings settings

I also have tried to imitate the Apache Logging style with:

logStringStdoutCloudWatch :: MonadIO m => LogAction m String
logStringStdoutCloudWatch = LogAction $ \x -> liftIO $ do
  let format = "%d/%b/%Y:%T %z"
  time <- getCurrentTime
  putStrLn $ "- - - [" ++ formatTime defaultTimeLocale format time ++ "] \"log\" - - \"\" \"" ++ x ++ "\""

It gives me the following logs:

- - - [12/Nov/2020:16:36:16 +0000] "log" - - "" "A log message"
127.0.0.1 - - [12/Nov/2020:17:36:16 +0100] "POST /event HTTP/1.1" 400 - "" "Client"

But only the last line is logged in CloudWatch.

What is wrong with my formatting? Alternatively: is there dedicated lib/function to have the correct format?


Solution

  • Actually I have cheated changing the log format to json, first at Servant level:

    import Network.Wai.Middleware
    import Network.Wai.Middleware.RequestLogger
    import Network.Wai.Middleware.RequestLogger.JSON(formatAsJSONWithHeaders)
    
    initializeApplication :: IO Application
    initializeApplication = do
      logger <- 
      mkRequestLogger def {outputFormat = CustomOutputFormatWithDetailsAndHeaders formatAsJSONWithHeaders}
      return
        $ logger
        $ serve (Proxy @API)
        $ hoistServer (Proxy @API) (toHandler env) server
    
    main :: IO ()
    main = do
      let settings = setPort 8080 defaultSettings
      initializeApplication >>= runSettings settings
    

    and the logger:

    logStringStdoutCloudWatch :: MonadIO m => LogAction m String
    logStringStdoutCloudWatch = LogAction $ \x -> liftIO $ do
      let format = "%d/%b/%Y:%T %z"
      time <- getCurrentTime
      putStrLn $ "{\"time\":\"" ++ formatTime defaultTimeLocale format time ++ "\",\"log\":\"" ++ x ++ "\"}"