2022-08-04 06:21:55 +03:00
|
|
|
-- | Helpful functions and types for generating log statements and URIs during
|
|
|
|
-- Options fetching and merging.
|
2022-07-15 11:54:27 +03:00
|
|
|
module Hasura.Server.Init.Logging
|
2022-08-04 06:21:55 +03:00
|
|
|
( -- * URI/QueryParam Manipulation
|
|
|
|
censorQuery,
|
2022-07-15 11:54:27 +03:00
|
|
|
updateQuery,
|
|
|
|
censorURI,
|
2022-08-04 06:21:55 +03:00
|
|
|
|
|
|
|
-- * Log Construction
|
2022-07-15 11:54:27 +03:00
|
|
|
mkGenericLog,
|
|
|
|
mkGenericStrLog,
|
|
|
|
connInfoToLog,
|
|
|
|
serveOptsToLog,
|
2022-08-04 06:21:55 +03:00
|
|
|
StartupTimeInfo (..),
|
2022-07-15 11:54:27 +03:00
|
|
|
)
|
|
|
|
where
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2022-08-04 06:21:55 +03:00
|
|
|
import Data.Aeson (FromJSON, ToJSON, (.=))
|
|
|
|
import Data.Aeson qualified as Aeson
|
2022-07-15 11:54:27 +03:00
|
|
|
import Data.ByteString.Char8 qualified as Char8
|
2022-08-04 06:21:55 +03:00
|
|
|
import Data.HashSet qualified as HashSet
|
|
|
|
import Data.Text qualified as Text
|
|
|
|
import Data.Text.Encoding qualified as Text.Encoding
|
|
|
|
import Database.PG.Query (ConnInfo)
|
|
|
|
import Database.PG.Query qualified as Query
|
2022-07-15 11:54:27 +03:00
|
|
|
import Hasura.GraphQL.Schema.Options qualified as Options
|
2022-08-04 06:21:55 +03:00
|
|
|
import Hasura.Logging (EngineLogType, LogLevel)
|
|
|
|
import Hasura.Logging qualified as Logging
|
2022-07-15 11:54:27 +03:00
|
|
|
import Hasura.Prelude
|
|
|
|
import Hasura.Server.Auth qualified as Auth
|
2022-08-04 06:21:55 +03:00
|
|
|
import Hasura.Server.Init.Config (ServeOptions)
|
2022-07-15 11:54:27 +03:00
|
|
|
import Hasura.Server.Init.Config qualified as Config
|
2022-08-04 06:21:55 +03:00
|
|
|
import Hasura.Server.Logging (StartupLog)
|
|
|
|
import Hasura.Server.Logging qualified as Server.Logging
|
|
|
|
import Network.HTTP.Types.URI (Query, QueryItem)
|
2022-07-15 11:54:27 +03:00
|
|
|
import Network.HTTP.Types.URI qualified as URI
|
2022-08-04 06:21:55 +03:00
|
|
|
import Network.URI (URI)
|
2022-07-15 11:54:27 +03:00
|
|
|
import Network.URI qualified as URI
|
2022-08-04 06:21:55 +03:00
|
|
|
import Network.WebSockets qualified as WebSockets
|
2022-07-15 11:54:27 +03:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2022-08-04 06:21:55 +03:00
|
|
|
censorQueryItem :: Text -> URI.QueryItem -> QueryItem
|
|
|
|
censorQueryItem sensitive (key, Just _) | key == Text.Encoding.encodeUtf8 sensitive = (key, Just "...")
|
2022-07-15 11:54:27 +03:00
|
|
|
censorQueryItem _ qi = qi
|
|
|
|
|
|
|
|
censorQuery :: Text -> URI.Query -> URI.Query
|
|
|
|
censorQuery sensitive = fmap (censorQueryItem sensitive)
|
|
|
|
|
2022-08-04 06:21:55 +03:00
|
|
|
updateQuery :: (URI.Query -> Query) -> URI -> URI.URI
|
2022-07-15 11:54:27 +03:00
|
|
|
updateQuery f uri =
|
|
|
|
let queries = URI.parseQuery $ Char8.pack $ URI.uriQuery uri
|
|
|
|
in uri {URI.uriQuery = Char8.unpack (URI.renderQuery True $ f queries)}
|
|
|
|
|
2022-08-04 06:21:55 +03:00
|
|
|
censorURI :: Text -> URI -> URI
|
2022-07-15 11:54:27 +03:00
|
|
|
censorURI sensitive uri = updateQuery (censorQuery sensitive) uri
|
|
|
|
|
2022-08-04 06:21:55 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- | Generate a 'Logging.StartupLog' from the Postgres 'Q.ConnInfo'.
|
|
|
|
connInfoToLog :: ConnInfo -> StartupLog
|
2022-07-15 11:54:27 +03:00
|
|
|
connInfoToLog connInfo =
|
2022-08-04 06:21:55 +03:00
|
|
|
Server.Logging.StartupLog Logging.LevelInfo "postgres_connection" infoVal
|
2022-07-15 11:54:27 +03:00
|
|
|
where
|
2022-08-04 06:21:55 +03:00
|
|
|
Query.ConnInfo retries details = connInfo
|
2022-07-15 11:54:27 +03:00
|
|
|
infoVal = case details of
|
2022-08-04 06:21:55 +03:00
|
|
|
Query.CDDatabaseURI uri -> mkDBUriLog $ Text.unpack $ bsToTxt uri
|
|
|
|
Query.CDOptions co ->
|
|
|
|
Aeson.object
|
|
|
|
[ "host" .= Query.connHost co,
|
|
|
|
"port" .= Query.connPort co,
|
|
|
|
"user" .= Query.connUser co,
|
|
|
|
"database" .= Query.connDatabase co,
|
|
|
|
"retries" .= retries
|
2022-07-15 11:54:27 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
mkDBUriLog uri =
|
|
|
|
case show . censorURI "sslpassword" <$> URI.parseURI uri of
|
|
|
|
Nothing ->
|
2022-08-04 06:21:55 +03:00
|
|
|
Aeson.object
|
|
|
|
["error" .= ("parsing database url failed" :: String)]
|
2022-07-15 11:54:27 +03:00
|
|
|
Just s ->
|
2022-08-04 06:21:55 +03:00
|
|
|
Aeson.object
|
|
|
|
[ "retries" .= retries,
|
|
|
|
"database_url" .= s
|
2022-07-15 11:54:27 +03:00
|
|
|
]
|
|
|
|
|
2022-08-04 06:21:55 +03:00
|
|
|
-- | Generate a 'Logging.StartupLog' from the final 'Config.ServeOptions'.
|
|
|
|
serveOptsToLog :: ToJSON (EngineLogType impl) => ServeOptions impl -> StartupLog
|
2022-07-15 11:54:27 +03:00
|
|
|
serveOptsToLog so =
|
2022-08-04 06:21:55 +03:00
|
|
|
Server.Logging.StartupLog Logging.LevelInfo "server_configuration" infoVal
|
2022-07-15 11:54:27 +03:00
|
|
|
where
|
|
|
|
infoVal =
|
2022-08-04 06:21:55 +03:00
|
|
|
Aeson.object
|
|
|
|
[ "port" .= Config.soPort so,
|
|
|
|
"server_host" .= show (Config.soHost so),
|
|
|
|
"transaction_isolation" .= show (Config.soTxIso so),
|
|
|
|
"admin_secret_set" .= not (HashSet.null (Config.soAdminSecret so)),
|
|
|
|
"auth_hook" .= (Auth.ahUrl <$> Config.soAuthHook so),
|
|
|
|
"auth_hook_mode" .= (show . Auth.ahType <$> Config.soAuthHook so),
|
|
|
|
"jwt_secret" .= (Aeson.toJSON <$> Config.soJwtSecret so),
|
|
|
|
"unauth_role" .= Config.soUnAuthRole so,
|
|
|
|
"cors_config" .= Config.soCorsConfig so,
|
|
|
|
"enable_console" .= Config.soEnableConsole so,
|
|
|
|
"console_assets_dir" .= Config.soConsoleAssetsDir so,
|
|
|
|
"enable_telemetry" .= Config.soEnableTelemetry so,
|
|
|
|
"use_prepared_statements" .= (Query.cpAllowPrepare . Config.soConnParams) so,
|
|
|
|
"stringify_numeric_types" .= case Config.soStringifyNum so of
|
2022-07-15 11:54:27 +03:00
|
|
|
Options.StringifyNumbers -> True
|
|
|
|
Options.Don'tStringifyNumbers -> False,
|
2022-08-04 06:21:55 +03:00
|
|
|
"v1-boolean-null-collapse" .= Config.soDangerousBooleanCollapse so,
|
|
|
|
"enabled_apis" .= Config.soEnabledAPIs so,
|
|
|
|
"live_query_options" .= Config.soLiveQueryOpts so,
|
|
|
|
"enable_allowlist" .= Config.soEnableAllowlist so,
|
|
|
|
"enabled_log_types" .= Config.soEnabledLogTypes so,
|
|
|
|
"log_level" .= Config.soLogLevel so,
|
|
|
|
"remote_schema_permissions" .= Config.soEnableRemoteSchemaPermissions so,
|
|
|
|
"websocket_compression_options" .= show (WebSockets.connectionCompressionOptions . Config.soConnectionOptions $ so),
|
|
|
|
"websocket_keep_alive" .= show (Config.soWebSocketKeepAlive so),
|
|
|
|
"infer_function_permissions" .= Config.soInferFunctionPermissions so,
|
|
|
|
"enable_maintenance_mode" .= Config.soEnableMaintenanceMode so,
|
|
|
|
"experimental_features" .= Config.soExperimentalFeatures so,
|
|
|
|
"events_fetch_batch_size" .= Config.soEventsFetchBatchSize so,
|
|
|
|
"graceful_shutdown_timeout" .= Config.soGracefulShutdownTimeout so,
|
|
|
|
"websocket_connection_init_timeout" .= show (Config.soWebSocketConnectionInitTimeout so),
|
|
|
|
"enable_metadata_query_logging" .= Config.soEnableMetadataQueryLogging so
|
2022-07-15 11:54:27 +03:00
|
|
|
]
|
|
|
|
|
2022-08-04 06:21:55 +03:00
|
|
|
mkGenericStrLog :: LogLevel -> Text -> String -> StartupLog
|
2022-07-15 11:54:27 +03:00
|
|
|
mkGenericStrLog logLevel k msg =
|
2022-08-04 06:21:55 +03:00
|
|
|
Server.Logging.StartupLog logLevel k $ Aeson.toJSON msg
|
2022-07-15 11:54:27 +03:00
|
|
|
|
2022-08-04 06:21:55 +03:00
|
|
|
mkGenericLog :: ToJSON a => LogLevel -> Text -> a -> StartupLog
|
2022-07-15 11:54:27 +03:00
|
|
|
mkGenericLog logLevel k msg =
|
2022-08-04 06:21:55 +03:00
|
|
|
Server.Logging.StartupLog logLevel k $ Aeson.toJSON msg
|
|
|
|
|
|
|
|
data StartupTimeInfo = StartupTimeInfo
|
|
|
|
{ _stiMessage :: !Text,
|
|
|
|
_stiTimeTaken :: !Double
|
|
|
|
}
|
|
|
|
|
|
|
|
instance FromJSON StartupTimeInfo where
|
|
|
|
parseJSON = Aeson.withObject "StartupTimeInfo" \obj -> do
|
|
|
|
_stiMessage <- obj Aeson..: "message"
|
|
|
|
_stiTimeTaken <- obj Aeson..: "time_taken"
|
|
|
|
pure StartupTimeInfo {..}
|
|
|
|
|
|
|
|
instance ToJSON StartupTimeInfo where
|
|
|
|
toJSON StartupTimeInfo {..} =
|
|
|
|
Aeson.object ["message" Aeson..= _stiMessage, "time_taken" Aeson..= _stiTimeTaken]
|