@@ -27,6 +27,7 @@ module PostgREST.AppState
2727 , reReadConfig
2828 , connectionWorker
2929 , runListener
30+ , getObserver
3031 ) where
3132
3233import qualified Data.Aeson as JSON
@@ -43,6 +44,7 @@ import qualified Hasql.Transaction.Sessions as SQL
4344import qualified Network.HTTP.Types.Status as HTTP
4445import qualified Network.Socket as NS
4546import qualified PostgREST.Error as Error
47+ import qualified PostgREST.Logger as Logger
4648import PostgREST.Observation
4749import PostgREST.Version (prettyVersion )
4850import System.TimeIt (timeItT )
@@ -57,7 +59,6 @@ import Data.IORef (IORef, atomicWriteIORef, newIORef,
5759import Data.Time.Clock (UTCTime , getCurrentTime )
5860
5961import PostgREST.Config (AppConfig (.. ),
60- LogLevel (.. ),
6162 addFallbackAppName ,
6263 readAppConfig )
6364import PostgREST.Config.Database (queryDbSettings ,
@@ -109,19 +110,26 @@ data AppState = AppState
109110 , stateSocketREST :: NS. Socket
110111 -- | Network socket for the admin UI
111112 , stateSocketAdmin :: Maybe NS. Socket
113+ -- | Logger state
114+ , stateLogger :: Logger. LoggerState
115+ -- | Observation handler
116+ , stateObserver :: ObservationHandler
112117 }
113118
114119type AppSockets = (NS. Socket , Maybe NS. Socket )
115120
116121init :: AppConfig -> IO AppState
117- init conf = do
122+ init conf@ AppConfig {configLogLevel} = do
123+ loggerState <- Logger. init
124+ let observer = Logger. observationLogger loggerState configLogLevel
118125 pool <- initPool conf
119126 (sock, adminSock) <- initSockets conf
120- state' <- initWithPool (sock, adminSock) pool conf
121- pure state' { stateSocketREST = sock, stateSocketAdmin = adminSock }
127+ state' <- initWithPool (sock, adminSock) pool conf loggerState observer
128+ pure state' { stateSocketREST = sock, stateSocketAdmin = adminSock}
129+
130+ initWithPool :: AppSockets -> SQL. Pool -> AppConfig -> Logger. LoggerState -> ObservationHandler -> IO AppState
131+ initWithPool (sock, adminSock) pool conf loggerState observer = do
122132
123- initWithPool :: AppSockets -> SQL. Pool -> AppConfig -> IO AppState
124- initWithPool (sock, adminSock) pool conf = do
125133 appState <- AppState pool
126134 <$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
127135 <*> newIORef Nothing
@@ -136,6 +144,8 @@ initWithPool (sock, adminSock) pool conf = do
136144 <*> C. newCache Nothing
137145 <*> pure sock
138146 <*> pure adminSock
147+ <*> pure loggerState
148+ <*> pure observer
139149
140150 debWorker <-
141151 let decisecond = 100000 in
@@ -193,17 +203,16 @@ initPool AppConfig{..} =
193203 (toUtf8 $ addFallbackAppName prettyVersion configDbUri)
194204
195205-- | Run an action with a database connection.
196- usePool :: AppState -> AppConfig -> SQL. Session a -> IO (Either SQL. UsageError a )
197- usePool AppState {.. } AppConfig {configLogLevel, configObserver = observer} sess = do
206+ usePool :: AppState -> SQL. Session a -> IO (Either SQL. UsageError a )
207+ usePool AppState {stateObserver = observer, .. } sess = do
198208 res <- SQL. use statePool sess
199209
200- when (configLogLevel > LogCrit ) $ do
201- whenLeft res (\ case
202- SQL. AcquisitionTimeoutUsageError -> observer $ PoolAcqTimeoutObs SQL. AcquisitionTimeoutUsageError
203- error
204- -- TODO We're using the 500 HTTP status for getting all internal db errors but there's no response here. We need a new intermediate type to not rely on the HTTP status.
205- | Error. status (Error. PgError False error ) >= HTTP. status500 -> observer $ QueryErrorCodeHighObs error
206- | otherwise -> pure () )
210+ whenLeft res (\ case
211+ SQL. AcquisitionTimeoutUsageError -> observer $ PoolAcqTimeoutObs SQL. AcquisitionTimeoutUsageError
212+ error
213+ -- TODO We're using the 500 HTTP status for getting all internal db errors but there's no response here. We need a new intermediate type to not rely on the HTTP status.
214+ | Error. status (Error. PgError False error ) >= HTTP. status500 -> observer $ QueryErrorCodeHighObs error
215+ | otherwise -> pure () )
207216
208217 return res
209218
@@ -281,19 +290,22 @@ getSchemaCacheLoaded = readIORef . stateSchemaCacheLoaded
281290putSchemaCacheLoaded :: AppState -> Bool -> IO ()
282291putSchemaCacheLoaded = atomicWriteIORef . stateSchemaCacheLoaded
283292
293+ getObserver :: AppState -> ObservationHandler
294+ getObserver = stateObserver
295+
284296-- | Schema cache status
285297data SCacheStatus
286298 = SCLoaded
287299 | SCOnRetry
288300 | SCFatalFail
289301
290302-- | Load the SchemaCache by using a connection from the pool.
291- loadSchemaCache :: AppState -> AppConfig -> IO SCacheStatus
292- loadSchemaCache appState AppConfig {configObserver = observer} = do
303+ loadSchemaCache :: AppState -> IO SCacheStatus
304+ loadSchemaCache appState@ AppState {stateObserver = observer} = do
293305 conf@ AppConfig {.. } <- getConfig appState
294306 (resultTime, result) <-
295307 let transaction = if configDbPreparedStatements then SQL. transaction else SQL. unpreparedTransaction in
296- timeItT $ usePool appState conf (transaction SQL. ReadCommitted SQL. Read $ querySchemaCache conf)
308+ timeItT $ usePool appState (transaction SQL. ReadCommitted SQL. Read $ querySchemaCache conf)
297309 case result of
298310 Left e -> do
299311 case checkIsFatal e of
@@ -333,12 +345,12 @@ data ConnectionStatus
333345-- program.
334346-- 3. Obtains the sCache. If this fails, it goes back to 1.
335347internalConnectionWorker :: AppState -> IO ()
336- internalConnectionWorker appState = work
348+ internalConnectionWorker appState@ AppState {stateObserver = observer} = work
337349 where
338350 work = do
339- config @ AppConfig {configObserver = observer, .. } <- getConfig appState
351+ AppConfig {.. } <- getConfig appState
340352 observer DBConnectAttemptObs
341- connected <- establishConnection appState config
353+ connected <- establishConnection appState
342354 case connected of
343355 FatalConnectionError reason ->
344356 -- Fatal error when connecting
@@ -356,7 +368,7 @@ internalConnectionWorker appState = work
356368 -- this could be fail because the connection drops, but the loadSchemaCache will pick the error and retry again
357369 -- We cannot retry after it fails immediately, because db-pre-config could have user errors. We just log the error and continue.
358370 when configDbConfig $ reReadConfig False appState
359- scStatus <- loadSchemaCache appState config
371+ scStatus <- loadSchemaCache appState
360372 case scStatus of
361373 SCLoaded ->
362374 -- do nothing and proceed if the load was successful
@@ -378,8 +390,8 @@ internalConnectionWorker appState = work
378390--
379391-- The connection tries are capped, but if the connection times out no error is
380392-- thrown, just 'False' is returned.
381- establishConnection :: AppState -> AppConfig -> IO ConnectionStatus
382- establishConnection appState config @ AppConfig {configObserver = observer} =
393+ establishConnection :: AppState -> IO ConnectionStatus
394+ establishConnection appState@ AppState {stateObserver = observer} =
383395 retrying retrySettings shouldRetry $
384396 const $ flushPool appState >> getConnectionStatus
385397 where
@@ -389,7 +401,7 @@ establishConnection appState config@AppConfig{configObserver=observer} =
389401
390402 getConnectionStatus :: IO ConnectionStatus
391403 getConnectionStatus = do
392- pgVersion <- usePool appState config (queryPgVersion False ) -- No need to prepare the query here, as the connection might not be established
404+ pgVersion <- usePool appState (queryPgVersion False ) -- No need to prepare the query here, as the connection might not be established
393405 case pgVersion of
394406 Left e -> do
395407 observer $ ConnectionPgVersionErrorObs e
@@ -418,12 +430,12 @@ establishConnection appState config@AppConfig{configObserver=observer} =
418430
419431-- | Re-reads the config plus config options from the db
420432reReadConfig :: Bool -> AppState -> IO ()
421- reReadConfig startingUp appState = do
422- config @ AppConfig {configObserver = observer, .. } <- getConfig appState
433+ reReadConfig startingUp appState@ AppState {stateObserver = observer} = do
434+ AppConfig {.. } <- getConfig appState
423435 pgVer <- getPgVersion appState
424436 dbSettings <-
425437 if configDbConfig then do
426- qDbSettings <- usePool appState config (queryDbSettings (dumpQi <$> configDbPreConfig) configDbPreparedStatements)
438+ qDbSettings <- usePool appState (queryDbSettings (dumpQi <$> configDbPreConfig) configDbPreparedStatements)
427439 case qDbSettings of
428440 Left e -> do
429441 observer ConfigReadErrorObs
@@ -439,15 +451,15 @@ reReadConfig startingUp appState = do
439451 pure mempty
440452 (roleSettings, roleIsolationLvl) <-
441453 if configDbConfig then do
442- rSettings <- usePool appState config (queryRoleSettings pgVer configDbPreparedStatements)
454+ rSettings <- usePool appState (queryRoleSettings pgVer configDbPreparedStatements)
443455 case rSettings of
444456 Left e -> do
445457 observer $ QueryRoleSettingsErrorObs e
446458 pure (mempty , mempty )
447459 Right x -> pure x
448460 else
449461 pure mempty
450- readAppConfig dbSettings configFilePath (Just configDbUri) roleSettings roleIsolationLvl observer >>= \ case
462+ readAppConfig dbSettings configFilePath (Just configDbUri) roleSettings roleIsolationLvl >>= \ case
451463 Left err ->
452464 if startingUp then
453465 panic err -- die on invalid config if the program is starting up
@@ -468,7 +480,7 @@ runListener conf@AppConfig{configDbChannelEnabled} appState = do
468480-- NOTIFY <db-channel> - with an empty payload - is done, it refills the schema
469481-- cache. It uses the connectionWorker in case the LISTEN connection dies.
470482listener :: AppState -> AppConfig -> IO ()
471- listener appState conf @ AppConfig {configObserver = observer, .. } = do
483+ listener appState@ AppState {stateObserver = observer} conf @ AppConfig { .. } = do
472484 let dbChannel = toS configDbChannel
473485
474486 -- The listener has to wait for a signal from the connectionWorker.
0 commit comments