server: Introduce option to add query field in http-log only on errors

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/11029
GitOrigin-RevId: f5c8aa1b81d67b84030215cb68b3bb9ee6021087
This commit is contained in:
Rakesh Emmadi 2024-09-18 14:49:34 +05:30 committed by hasura-bot
parent 0c52ca1d2e
commit f4aafb234d
10 changed files with 70 additions and 16 deletions

View File

@ -55,7 +55,7 @@ import Hasura.Server.Init
ServeOptions (..), ServeOptions (..),
) )
import Hasura.Server.Init qualified as Init import Hasura.Server.Init qualified as Init
import Hasura.Server.Logging (MetadataQueryLoggingMode (MetadataQueryLoggingDisabled)) import Hasura.Server.Logging (HttpLogQueryOnlyOnError (HttpLogQueryOnlyOnErrorDisabled), MetadataQueryLoggingMode (MetadataQueryLoggingDisabled))
import Hasura.Server.Types import Hasura.Server.Types
( ApolloFederationStatus (ApolloFederationDisabled), ( ApolloFederationStatus (ApolloFederationDisabled),
EventingMode (EventingEnabled), EventingMode (EventingEnabled),
@ -321,6 +321,7 @@ serveOptions =
soEventingMode = EventingEnabled, soEventingMode = EventingEnabled,
soReadOnlyMode = ReadOnlyModeDisabled, soReadOnlyMode = ReadOnlyModeDisabled,
soEnableMetadataQueryLogging = MetadataQueryLoggingDisabled, soEnableMetadataQueryLogging = MetadataQueryLoggingDisabled,
soHttpLogQueryOnlyOnError = HttpLogQueryOnlyOnErrorDisabled,
soDefaultNamingConvention = Init._default Init.defaultNamingConventionOption, soDefaultNamingConvention = Init._default Init.defaultNamingConventionOption,
soExtensionsSchema = ExtensionsSchema "public", soExtensionsSchema = ExtensionsSchema "public",
soMetadataDefaults = emptyMetadataDefaults, soMetadataDefaults = emptyMetadataDefaults,

View File

@ -477,7 +477,7 @@ initialiseAppEnv env BasicConnectionInfo {..} serveOptions@ServeOptions {..} liv
appEnvMetadataVersionRef = metaVersionRef, appEnvMetadataVersionRef = metaVersionRef,
appEnvInstanceId = instanceId, appEnvInstanceId = instanceId,
appEnvEnableMaintenanceMode = soEnableMaintenanceMode, appEnvEnableMaintenanceMode = soEnableMaintenanceMode,
appEnvLoggingSettings = LoggingSettings soEnabledLogTypes soEnableMetadataQueryLogging, appEnvLoggingSettings = LoggingSettings soEnabledLogTypes soEnableMetadataQueryLogging soHttpLogQueryOnlyOnError,
appEnvEventingMode = soEventingMode, appEnvEventingMode = soEventingMode,
appEnvEnableReadOnlyMode = soReadOnlyMode, appEnvEnableReadOnlyMode = soReadOnlyMode,
appEnvServerMetrics = serverMetrics, appEnvServerMetrics = serverMetrics,

View File

@ -211,6 +211,9 @@ mkServeOptions sor@ServeOptionsRaw {..} = do
soEnableMetadataQueryLogging <- case rsoEnableMetadataQueryLoggingEnv of soEnableMetadataQueryLogging <- case rsoEnableMetadataQueryLoggingEnv of
Server.Logging.MetadataQueryLoggingDisabled -> withOptionDefault Nothing enableMetadataQueryLoggingOption Server.Logging.MetadataQueryLoggingDisabled -> withOptionDefault Nothing enableMetadataQueryLoggingOption
metadataQueryLoggingEnabled -> pure metadataQueryLoggingEnabled metadataQueryLoggingEnabled -> pure metadataQueryLoggingEnabled
soHttpLogQueryOnlyOnError <- case rsoHttpLogQueryOnlyOnError of
Server.Logging.HttpLogQueryOnlyOnErrorDisabled -> withOptionDefault Nothing httpLogQueryOnlyOnErrorOption
httpLogQueryOnlyOnError -> pure httpLogQueryOnlyOnError
soDefaultNamingConvention <- withOptionDefault rsoDefaultNamingConvention defaultNamingConventionOption soDefaultNamingConvention <- withOptionDefault rsoDefaultNamingConvention defaultNamingConventionOption
soMetadataDefaults <- withOptionDefault rsoMetadataDefaults metadataDefaultsOption soMetadataDefaults <- withOptionDefault rsoMetadataDefaults metadataDefaultsOption
soExtensionsSchema <- withOptionDefault rsoExtensionsSchema metadataDBExtensionsSchemaOption soExtensionsSchema <- withOptionDefault rsoExtensionsSchema metadataDBExtensionsSchemaOption

View File

@ -59,6 +59,7 @@ module Hasura.Server.Init.Arg.Command.Serve
gracefulShutdownOption, gracefulShutdownOption,
webSocketConnectionInitTimeoutOption, webSocketConnectionInitTimeoutOption,
enableMetadataQueryLoggingOption, enableMetadataQueryLoggingOption,
httpLogQueryOnlyOnErrorOption,
defaultNamingConventionOption, defaultNamingConventionOption,
metadataDBExtensionsSchemaOption, metadataDBExtensionsSchemaOption,
parseMetadataDefaults, parseMetadataDefaults,
@ -158,6 +159,7 @@ serveCommandParser =
<*> parseGracefulShutdownTimeout <*> parseGracefulShutdownTimeout
<*> parseWebSocketConnectionInitTimeout <*> parseWebSocketConnectionInitTimeout
<*> parseEnableMetadataQueryLogging <*> parseEnableMetadataQueryLogging
<*> parseHttpLogQueryOnlyOnError
<*> parseDefaultNamingConvention <*> parseDefaultNamingConvention
<*> parseExtensionsSchema <*> parseExtensionsSchema
<*> parseMetadataDefaults <*> parseMetadataDefaults
@ -1165,6 +1167,22 @@ enableMetadataQueryLoggingOption =
Config._helpMessage = "Enables the query field in http-logs for metadata queries (default: false)" Config._helpMessage = "Enables the query field in http-logs for metadata queries (default: false)"
} }
parseHttpLogQueryOnlyOnError :: Opt.Parser Server.Logging.HttpLogQueryOnlyOnError
parseHttpLogQueryOnlyOnError =
fmap (bool Server.Logging.HttpLogQueryOnlyOnErrorDisabled Server.Logging.HttpLogQueryOnlyOnErrorEnabled)
$ Opt.switch
( Opt.long "http-log-query-only-on-error"
<> Opt.help (Config._helpMessage httpLogQueryOnlyOnErrorOption)
)
httpLogQueryOnlyOnErrorOption :: Config.Option Server.Logging.HttpLogQueryOnlyOnError
httpLogQueryOnlyOnErrorOption =
Config.Option
{ Config._default = Server.Logging.HttpLogQueryOnlyOnErrorDisabled,
Config._envVar = "HASURA_GRAPHQL_HTTP_LOG_QUERY_ONLY_ON_ERROR",
Config._helpMessage = "Only add query to http log on error (default: false)"
}
-- TODO(SOLOMON): The defaulting behavior for this occurs inside the Engine. In -- TODO(SOLOMON): The defaulting behavior for this occurs inside the Engine. In
-- an isolated PR we should move that defaulting in the parsing stage. -- an isolated PR we should move that defaulting in the parsing stage.
parseDefaultNamingConvention :: Opt.Parser (Maybe NC.NamingCase) parseDefaultNamingConvention :: Opt.Parser (Maybe NC.NamingCase)

View File

@ -321,6 +321,7 @@ data ServeOptionsRaw impl = ServeOptionsRaw
rsoGracefulShutdownTimeout :: Maybe (Refined NonNegative Seconds), rsoGracefulShutdownTimeout :: Maybe (Refined NonNegative Seconds),
rsoWebSocketConnectionInitTimeout :: Maybe WSConnectionInitTimeout, rsoWebSocketConnectionInitTimeout :: Maybe WSConnectionInitTimeout,
rsoEnableMetadataQueryLoggingEnv :: Server.Logging.MetadataQueryLoggingMode, rsoEnableMetadataQueryLoggingEnv :: Server.Logging.MetadataQueryLoggingMode,
rsoHttpLogQueryOnlyOnError :: Server.Logging.HttpLogQueryOnlyOnError,
-- | stores global default naming convention -- | stores global default naming convention
rsoDefaultNamingConvention :: Maybe NamingCase, rsoDefaultNamingConvention :: Maybe NamingCase,
rsoExtensionsSchema :: Maybe MonadTx.ExtensionsSchema, rsoExtensionsSchema :: Maybe MonadTx.ExtensionsSchema,
@ -635,6 +636,7 @@ data ServeOptions impl = ServeOptions
-- | See note '$readOnlyMode' -- | See note '$readOnlyMode'
soReadOnlyMode :: Server.Types.ReadOnlyMode, soReadOnlyMode :: Server.Types.ReadOnlyMode,
soEnableMetadataQueryLogging :: Server.Logging.MetadataQueryLoggingMode, soEnableMetadataQueryLogging :: Server.Logging.MetadataQueryLoggingMode,
soHttpLogQueryOnlyOnError :: Server.Logging.HttpLogQueryOnlyOnError,
soDefaultNamingConvention :: NamingCase, soDefaultNamingConvention :: NamingCase,
soExtensionsSchema :: MonadTx.ExtensionsSchema, soExtensionsSchema :: MonadTx.ExtensionsSchema,
soMetadataDefaults :: MetadataDefaults, soMetadataDefaults :: MetadataDefaults,

View File

@ -272,6 +272,9 @@ instance FromEnv (Server.Types.MaintenanceMode ()) where
instance FromEnv Server.Logging.MetadataQueryLoggingMode where instance FromEnv Server.Logging.MetadataQueryLoggingMode where
fromEnv = fmap (bool Server.Logging.MetadataQueryLoggingDisabled Server.Logging.MetadataQueryLoggingEnabled) . fromEnv @Bool fromEnv = fmap (bool Server.Logging.MetadataQueryLoggingDisabled Server.Logging.MetadataQueryLoggingEnabled) . fromEnv @Bool
instance FromEnv Server.Logging.HttpLogQueryOnlyOnError where
fromEnv = fmap (bool Server.Logging.HttpLogQueryOnlyOnErrorDisabled Server.Logging.HttpLogQueryOnlyOnErrorEnabled) . fromEnv @Bool
instance FromEnv Query.TxIsolation where instance FromEnv Query.TxIsolation where
fromEnv = Utils.readIsoLevel fromEnv = Utils.readIsoLevel

View File

@ -120,7 +120,8 @@ serveOptsToLog so =
"events_fetch_batch_size" .= Config.soEventsFetchBatchSize so, "events_fetch_batch_size" .= Config.soEventsFetchBatchSize so,
"graceful_shutdown_timeout" .= Config.soGracefulShutdownTimeout so, "graceful_shutdown_timeout" .= Config.soGracefulShutdownTimeout so,
"websocket_connection_init_timeout" .= show (Config.soWebSocketConnectionInitTimeout so), "websocket_connection_init_timeout" .= show (Config.soWebSocketConnectionInitTimeout so),
"enable_metadata_query_logging" .= Config.soEnableMetadataQueryLogging so "enable_metadata_query_logging" .= Config.soEnableMetadataQueryLogging so,
"http_log_query_only_on_error" .= Config.soHttpLogQueryOnlyOnError so
] ]
mkGenericLog :: (ToJSON a) => Logging.LogLevel -> Text -> a -> Server.Logging.StartupLog mkGenericLog :: (ToJSON a) => Logging.LogLevel -> Text -> a -> Server.Logging.StartupLog

View File

@ -25,6 +25,7 @@ module Hasura.Server.Logging
buildHttpLogMetadata, buildHttpLogMetadata,
emptyHttpLogMetadata, emptyHttpLogMetadata,
MetadataQueryLoggingMode (..), MetadataQueryLoggingMode (..),
HttpLogQueryOnlyOnError (..),
LoggingSettings (..), LoggingSettings (..),
SchemaSyncThreadType (..), SchemaSyncThreadType (..),
SchemaSyncLog (..), SchemaSyncLog (..),
@ -254,13 +255,28 @@ instance J.ToJSON MetadataQueryLoggingMode where
MetadataQueryLoggingEnabled -> J.Bool True MetadataQueryLoggingEnabled -> J.Bool True
MetadataQueryLoggingDisabled -> J.Bool False MetadataQueryLoggingDisabled -> J.Bool False
data HttpLogQueryOnlyOnError = HttpLogQueryOnlyOnErrorEnabled | HttpLogQueryOnlyOnErrorDisabled
deriving (Show, Eq)
instance J.FromJSON HttpLogQueryOnlyOnError where
parseJSON =
J.withBool "HttpLogQueryOnlyOnError"
$ pure
. bool HttpLogQueryOnlyOnErrorDisabled HttpLogQueryOnlyOnErrorEnabled
instance J.ToJSON HttpLogQueryOnlyOnError where
toJSON = \case
HttpLogQueryOnlyOnErrorEnabled -> J.Bool True
HttpLogQueryOnlyOnErrorDisabled -> J.Bool False
-- | Setting used to control the information in logs -- | Setting used to control the information in logs
data LoggingSettings = LoggingSettings data LoggingSettings = LoggingSettings
{ -- | this is only required for the short-term fix in https://github.com/hasura/graphql-engine-mono/issues/1770 { -- | this is only required for the short-term fix in https://github.com/hasura/graphql-engine-mono/issues/1770
-- See Note [Disable query printing when query-log is disabled] -- See Note [Disable query printing when query-log is disabled]
_lsEnabledLogTypes :: HashSet (EngineLogType Hasura), _lsEnabledLogTypes :: HashSet (EngineLogType Hasura),
-- See Note [Disable query printing for metadata queries] -- See Note [Disable query printing for metadata queries]
_lsMetadataQueryLoggingMode :: MetadataQueryLoggingMode _lsMetadataQueryLoggingMode :: MetadataQueryLoggingMode,
_lsHttpLogQueryOnlyOnError :: HttpLogQueryOnlyOnError
} }
deriving (Eq) deriving (Eq)
@ -470,9 +486,16 @@ instance J.ToJSON HttpLogContext where
toJSON = J.genericToJSON hasuraJSON {J.omitNothingFields = True} toJSON = J.genericToJSON hasuraJSON {J.omitNothingFields = True}
toEncoding = J.genericToEncoding hasuraJSON {J.omitNothingFields = True} toEncoding = J.genericToEncoding hasuraJSON {J.omitNothingFields = True}
data RequestStatus
= RequestStatusSuccess
| RequestStatusError
deriving (Eq, Show)
-- | Check if the 'query' field should be included in the http-log -- | Check if the 'query' field should be included in the http-log
isQueryIncludedInLogs :: Text -> LoggingSettings -> Bool isQueryIncludedInLogs :: RequestStatus -> Text -> LoggingSettings -> Bool
isQueryIncludedInLogs urlPath LoggingSettings {..} isQueryIncludedInLogs requestStatus urlPath LoggingSettings {..}
-- if HttpLogQueryOnlyOnError is enabled, log the query when request is failed
| _lsHttpLogQueryOnlyOnError == HttpLogQueryOnlyOnErrorEnabled = requestStatus == RequestStatusError
-- See Note [Disable query printing for metadata queries] -- See Note [Disable query printing for metadata queries]
| isQueryLogEnabled && isMetadataRequest = _lsMetadataQueryLoggingMode == MetadataQueryLoggingEnabled | isQueryLogEnabled && isMetadataRequest = _lsMetadataQueryLoggingMode == MetadataQueryLoggingEnabled
-- See Note [Disable query printing when query-log is disabled] -- See Note [Disable query printing when query-log is disabled]
@ -485,9 +508,9 @@ isQueryIncludedInLogs urlPath LoggingSettings {..}
-- | Add the 'query' field to the http-log if `MetadataQueryLoggingMode` -- | Add the 'query' field to the http-log if `MetadataQueryLoggingMode`
-- is set to `MetadataQueryLoggingEnabled` else only adds the `query.type` field. -- is set to `MetadataQueryLoggingEnabled` else only adds the `query.type` field.
addQuery :: Maybe J.Value -> Text -> LoggingSettings -> Maybe J.Value addQuery :: RequestStatus -> Maybe J.Value -> Text -> LoggingSettings -> Maybe J.Value
addQuery parsedReq path loggingSettings = addQuery requestStatus parsedReq path loggingSettings =
if isQueryIncludedInLogs path loggingSettings if isQueryIncludedInLogs requestStatus path loggingSettings
then parsedReq then parsedReq
else Just $ J.object ["type" J..= (fmap (^? key "type" . _String)) parsedReq] else Just $ J.object ["type" J..= (fmap (^? key "type" . _String)) parsedReq]
@ -527,7 +550,7 @@ mkHttpAccessLogContext userInfoM loggingSettings reqId req (_, parsedReq) uncomp
olRequestReadTime = Seconds . fst <$> mTiming, olRequestReadTime = Seconds . fst <$> mTiming,
olQueryExecutionTime = Seconds . snd <$> mTiming, olQueryExecutionTime = Seconds . snd <$> mTiming,
olRequestMode = batching, olRequestMode = batching,
olQuery = addQuery parsedReq (hlPath http) loggingSettings, olQuery = addQuery RequestStatusSuccess parsedReq (hlPath http) loggingSettings,
olRawQuery = Nothing, olRawQuery = Nothing,
olError = Nothing olError = Nothing
} }
@ -542,13 +565,13 @@ mkHttpAccessLogContext userInfoM loggingSettings reqId req (_, parsedReq) uncomp
GQLQueryOperationSuccess (GQLQueryOperationSuccessLog {..}) -> GQLQueryOperationSuccess (GQLQueryOperationSuccessLog {..}) ->
BatchOperationSuccess BatchOperationSuccess
$ BatchOperationSuccessLog $ BatchOperationSuccessLog
(addQuery parsedReq (hlPath http) loggingSettings) (addQuery RequestStatusSuccess parsedReq (hlPath http) loggingSettings)
gqolResponseSize gqolResponseSize
(convertDuration gqolQueryExecutionTime) (convertDuration gqolQueryExecutionTime)
GQLQueryOperationError (GQLQueryOperationErrorLog {..}) -> GQLQueryOperationError (GQLQueryOperationErrorLog {..}) ->
BatchOperationError BatchOperationError
$ BatchOperationErrorLog $ BatchOperationErrorLog
(addQuery parsedReq (hlPath http) loggingSettings) (addQuery RequestStatusError parsedReq (hlPath http) loggingSettings)
gqelError gqelError
) )
opLogs opLogs
@ -571,7 +594,8 @@ mkHttpErrorLogContext ::
[HTTP.Header] -> [HTTP.Header] ->
HttpLogContext HttpLogContext
mkHttpErrorLogContext userInfoM loggingSettings reqId waiReq (reqBody, parsedReq) err mTiming compressTypeM headers = mkHttpErrorLogContext userInfoM loggingSettings reqId waiReq (reqBody, parsedReq) err mTiming compressTypeM headers =
let http = let requestStatus = RequestStatusError
http =
HttpInfoLog HttpInfoLog
{ hlStatus = qeStatus err, { hlStatus = qeStatus err,
hlMethod = bsToTxt $ Wai.requestMethod waiReq, hlMethod = bsToTxt $ Wai.requestMethod waiReq,
@ -590,7 +614,7 @@ mkHttpErrorLogContext userInfoM loggingSettings reqId waiReq (reqBody, parsedReq
olUncompressedResponseSize = responseSize, olUncompressedResponseSize = responseSize,
olRequestReadTime = Seconds . fst <$> mTiming, olRequestReadTime = Seconds . fst <$> mTiming,
olQueryExecutionTime = Seconds . snd <$> mTiming, olQueryExecutionTime = Seconds . snd <$> mTiming,
olQuery = addQuery parsedReq (hlPath http) loggingSettings, olQuery = addQuery requestStatus parsedReq (hlPath http) loggingSettings,
-- if parsedReq is Nothing, add the raw query -- if parsedReq is Nothing, add the raw query
olRawQuery = maybe (reqToLog $ Just $ bsToTxt $ BL.toStrict reqBody) (const Nothing) parsedReq, olRawQuery = maybe (reqToLog $ Just $ bsToTxt $ BL.toStrict reqBody) (const Nothing) parsedReq,
olError = Just err, olError = Just err,
@ -598,7 +622,7 @@ mkHttpErrorLogContext userInfoM loggingSettings reqId waiReq (reqBody, parsedReq
} }
reqToLog :: Maybe a -> Maybe a reqToLog :: Maybe a -> Maybe a
reqToLog req = if (isQueryIncludedInLogs (hlPath http) loggingSettings) then req else Nothing reqToLog req = if (isQueryIncludedInLogs requestStatus (hlPath http) loggingSettings) then req else Nothing
in HttpLogContext http op reqId Nothing -- Batched operation logs are always reported in logHttpSuccess even if there are errors in HttpLogContext http op reqId Nothing -- Batched operation logs are always reported in logHttpSuccess even if there are errors
data HttpLogLine = HttpLogLine data HttpLogLine = HttpLogLine

View File

@ -91,6 +91,7 @@ emptyServeOptionsRaw =
rsoGracefulShutdownTimeout = Nothing, rsoGracefulShutdownTimeout = Nothing,
rsoWebSocketConnectionInitTimeout = Nothing, rsoWebSocketConnectionInitTimeout = Nothing,
rsoEnableMetadataQueryLoggingEnv = Logging.MetadataQueryLoggingDisabled, rsoEnableMetadataQueryLoggingEnv = Logging.MetadataQueryLoggingDisabled,
rsoHttpLogQueryOnlyOnError = Logging.HttpLogQueryOnlyOnErrorDisabled,
rsoDefaultNamingConvention = Nothing, rsoDefaultNamingConvention = Nothing,
rsoExtensionsSchema = Nothing, rsoExtensionsSchema = Nothing,
rsoMetadataDefaults = Nothing, rsoMetadataDefaults = Nothing,

View File

@ -24,7 +24,7 @@ import Hasura.Server.Init
ServeOptions (..), ServeOptions (..),
) )
import Hasura.Server.Init qualified as Init import Hasura.Server.Init qualified as Init
import Hasura.Server.Logging (MetadataQueryLoggingMode (MetadataQueryLoggingDisabled)) import Hasura.Server.Logging (HttpLogQueryOnlyOnError (HttpLogQueryOnlyOnErrorDisabled), MetadataQueryLoggingMode (MetadataQueryLoggingDisabled))
import Hasura.Server.Types import Hasura.Server.Types
( ApolloFederationStatus (ApolloFederationDisabled), ( ApolloFederationStatus (ApolloFederationDisabled),
EventingMode (EventingEnabled), EventingMode (EventingEnabled),
@ -89,6 +89,7 @@ serveOptions =
soEventingMode = EventingEnabled, soEventingMode = EventingEnabled,
soReadOnlyMode = ReadOnlyModeDisabled, soReadOnlyMode = ReadOnlyModeDisabled,
soEnableMetadataQueryLogging = MetadataQueryLoggingDisabled, soEnableMetadataQueryLogging = MetadataQueryLoggingDisabled,
soHttpLogQueryOnlyOnError = HttpLogQueryOnlyOnErrorDisabled,
soDefaultNamingConvention = Init._default Init.defaultNamingConventionOption, soDefaultNamingConvention = Init._default Init.defaultNamingConventionOption,
soExtensionsSchema = ExtensionsSchema "public", soExtensionsSchema = ExtensionsSchema "public",
soMetadataDefaults = emptyMetadataDefaults, soMetadataDefaults = emptyMetadataDefaults,