mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-09-20 15:09:02 +03:00
server: log operation details for each query in a batch query execution
https://github.com/hasura/graphql-engine-mono/pull/2306 GitOrigin-RevId: 066a02fc57711b1faad447e6e448e3e004376c74
This commit is contained in:
parent
f2971dd515
commit
982b5a3d15
@ -4,8 +4,8 @@
|
||||
|
||||
(Add entries below in the order of server, console, cli, docs, others)
|
||||
- server: support MSSQL transactions
|
||||
- server: log individual operation details in the http-log during a batch graphQL query execution
|
||||
- console: support tracking of functions with return a single row
|
||||
|
||||
- server: update `create_scheduled_event` API to return `event_id` in response
|
||||
|
||||
- server: fix bug which allowed inconsistent metadata to exist after the `replace_metadata` API even though `allow_inconsistent_object` is set to `false`.
|
||||
|
@ -840,9 +840,9 @@ instance (MonadIO m) => HttpLog (PGMetadataStorageAppT m) where
|
||||
unLogger logger $ mkHttpLog $
|
||||
mkHttpErrorLogContext userInfoM enabledLogTypes reqId waiReq req qErr Nothing Nothing headers
|
||||
|
||||
logHttpSuccess logger enabledLogTypes userInfoM reqId waiReq reqBody _response compressedResponse qTime cType headers (CommonHttpLogMetadata rb, ()) =
|
||||
logHttpSuccess logger enabledLogTypes userInfoM reqId waiReq reqBody _response compressedResponse qTime cType headers (CommonHttpLogMetadata rb batchQueryOpLogs, ()) =
|
||||
unLogger logger $ mkHttpLog $
|
||||
mkHttpAccessLogContext userInfoM enabledLogTypes reqId waiReq reqBody compressedResponse qTime cType headers rb
|
||||
mkHttpAccessLogContext userInfoM enabledLogTypes reqId waiReq reqBody compressedResponse qTime cType headers rb batchQueryOpLogs
|
||||
|
||||
instance (Monad m) => MonadExecuteQuery (PGMetadataStorageAppT m) where
|
||||
cacheLookup _ _ _ _ = pure ([], Nothing)
|
||||
|
@ -232,9 +232,9 @@ runGQ
|
||||
-> [HTTP.Header]
|
||||
-> E.GraphQLQueryType
|
||||
-> GQLReqUnparsed
|
||||
-> m (ParameterizedQueryHash, HttpResponse (Maybe GQResponse, EncJSON))
|
||||
-> m (GQLQueryOperationSuccessLog, HttpResponse (Maybe GQResponse, EncJSON))
|
||||
runGQ env logger reqId userInfo ipAddress reqHeaders queryType reqUnparsed = do
|
||||
(telemTimeTot_DT, (telemQueryType, telemTimeIO_DT, telemLocality, resp, parameterizedQueryHash)) <- withElapsedTime $ do
|
||||
(totalTime, (telemQueryType, telemTimeIO_DT, telemLocality, resp, parameterizedQueryHash)) <- withElapsedTime $ do
|
||||
E.ExecutionCtx _ sqlGenCtx sc scVer httpManager enableAL <- ask
|
||||
|
||||
-- run system authorization on the GraphQL API
|
||||
@ -380,10 +380,12 @@ runGQ env logger reqId userInfo ipAddress reqHeaders queryType reqUnparsed = do
|
||||
throw400 UnexpectedPayload "subscriptions are not supported over HTTP, use websockets instead"
|
||||
-- The response and misc telemetry data:
|
||||
let telemTimeIO = convertDuration telemTimeIO_DT
|
||||
telemTimeTot = convertDuration telemTimeTot_DT
|
||||
telemTimeTot = convertDuration totalTime
|
||||
telemTransport = Telem.HTTP
|
||||
requestSize = LBS.length $ J.encode reqUnparsed
|
||||
responseSize = LBS.length $ encJToLBS $ snd $ _hrBody resp
|
||||
Telem.recordTimingMetric Telem.RequestDimensions{..} Telem.RequestTimings{..}
|
||||
return (parameterizedQueryHash, resp)
|
||||
return (GQLQueryOperationSuccessLog reqUnparsed totalTime responseSize requestSize parameterizedQueryHash, resp)
|
||||
where
|
||||
getExecStepActionWithActionInfo acc execStep = case execStep of
|
||||
EB.ExecStepAction _ actionInfo _remoteJoins -> (actionInfo:acc)
|
||||
@ -539,14 +541,14 @@ runGQBatched
|
||||
-> Wai.IpAddress
|
||||
-> [HTTP.Header]
|
||||
-> E.GraphQLQueryType
|
||||
-> GQLBatchedReqs GQLQueryText
|
||||
-> GQLBatchedReqs (GQLReq GQLQueryText)
|
||||
-- ^ the batched request with unparsed GraphQL query
|
||||
-> m (HttpLogMetadata m, HttpResponse EncJSON)
|
||||
runGQBatched env logger reqId responseErrorsConfig userInfo ipAddress reqHdrs queryType query =
|
||||
case query of
|
||||
GQLSingleRequest req -> do
|
||||
(parameterizedQueryHash, httpResp) <- runGQ env logger reqId userInfo ipAddress reqHdrs queryType req
|
||||
let httpLoggingMetadata = buildHttpLogMetadata @m (PQHSetSingleton parameterizedQueryHash) L.RequestModeSingle
|
||||
(gqlQueryOperationLog, httpResp) <- runGQ env logger reqId userInfo ipAddress reqHdrs queryType req
|
||||
let httpLoggingMetadata = buildHttpLogMetadata @m (PQHSetSingleton (gqolParameterizedQueryHash gqlQueryOperationLog)) L.RequestModeSingle (Just (GQLSingleRequest (GQLQueryOperationSuccess gqlQueryOperationLog)))
|
||||
pure (httpLoggingMetadata, snd <$> httpResp)
|
||||
GQLBatchedReqs reqs -> do
|
||||
-- It's unclear what we should do if we receive multiple
|
||||
@ -557,8 +559,15 @@ runGQBatched env logger reqId responseErrorsConfig userInfo ipAddress reqHdrs qu
|
||||
flip HttpResponse []
|
||||
. encJFromList
|
||||
. map (either (encJFromJValue . encodeGQErr includeInternal) _hrBody)
|
||||
responses <- traverse (try . (fmap . fmap . fmap) snd . runGQ env logger reqId userInfo ipAddress reqHdrs queryType) reqs
|
||||
let httpLoggingMetadata = buildHttpLogMetadata @m (PQHSetBatched $ rights $ map (fmap fst) responses) L.RequestModeBatched
|
||||
pure (httpLoggingMetadata, removeHeaders (map (fmap snd) responses))
|
||||
responses <- traverse (\req -> fmap (req, ) . try . (fmap . fmap . fmap) snd . runGQ env logger reqId userInfo ipAddress reqHdrs queryType $ req) reqs
|
||||
let requestsOperationLogs = map fst $ rights $ map snd responses
|
||||
batchOperationLogs = map (\(req, resp) ->
|
||||
case resp of
|
||||
Left err -> GQLQueryOperationError $ GQLQueryOperationErrorLog req err
|
||||
Right (successOpLog, _) -> GQLQueryOperationSuccess successOpLog
|
||||
) responses
|
||||
parameterizedQueryHashes = map gqolParameterizedQueryHash requestsOperationLogs
|
||||
httpLoggingMetadata = buildHttpLogMetadata @m (PQHSetBatched parameterizedQueryHashes) L.RequestModeBatched (Just (GQLBatchedReqs batchOperationLogs))
|
||||
pure (httpLoggingMetadata, removeHeaders (map ((fmap snd) . snd) responses))
|
||||
where
|
||||
try = flip catchError (pure . Left) . fmap Right
|
||||
|
@ -20,6 +20,7 @@ module Hasura.GraphQL.Transport.HTTP.Protocol
|
||||
, GQExecError(..)
|
||||
, GQResponse
|
||||
, isExecError
|
||||
, ReqsText
|
||||
) where
|
||||
|
||||
import Data.Text.Extended (dquote)
|
||||
@ -83,9 +84,9 @@ instance (Hashable a) => Hashable (GQLReq a)
|
||||
--
|
||||
-- See <https://github.com/hasura/graphql-engine/issues/1812>.
|
||||
data GQLBatchedReqs a
|
||||
= GQLSingleRequest (GQLReq a)
|
||||
| GQLBatchedReqs [GQLReq a]
|
||||
deriving (Show, Eq, Generic)
|
||||
= GQLSingleRequest a
|
||||
| GQLBatchedReqs [a]
|
||||
deriving (Show, Eq, Generic, Functor)
|
||||
|
||||
instance J.ToJSON a => J.ToJSON (GQLBatchedReqs a) where
|
||||
toJSON (GQLSingleRequest q) = J.toJSON q
|
||||
@ -112,6 +113,8 @@ type GQLReqUnparsed = GQLReq GQLQueryText
|
||||
-- 'ExecutableDefinitionOperation' in '_grQuery'
|
||||
type GQLReqParsed = GQLReq GQLExecDoc
|
||||
|
||||
type ReqsText = GQLBatchedReqs (GQLReq GQLQueryText)
|
||||
|
||||
-- | A simplified form of 'GQLReqParsed' which is more ergonomic in particular
|
||||
-- for APIs that act as graphql /clients/ (e.g. in remote relationship
|
||||
-- execution). This is a "desugared" request in which fragments have been
|
||||
|
@ -141,8 +141,6 @@ data APIResp
|
||||
= JSONResp !(HttpResponse EncJSON)
|
||||
| RawResp !(HttpResponse BL.ByteString)
|
||||
|
||||
type ReqsText = GH.GQLBatchedReqs GH.GQLQueryText
|
||||
|
||||
-- | API request handlers for different endpoints
|
||||
data APIHandler m a where
|
||||
-- | A simple GET request
|
||||
@ -152,7 +150,7 @@ data APIHandler m a where
|
||||
-- | A general GraphQL request (query or mutation) for which the content of the query
|
||||
-- is made available to the handler for authentication.
|
||||
-- This is a more specific version of the 'AHPost' constructor.
|
||||
AHGraphQLRequest :: !(ReqsText -> Handler m (HttpLogMetadata m, APIResp)) -> APIHandler m ReqsText
|
||||
AHGraphQLRequest :: !(GH.ReqsText -> Handler m (HttpLogMetadata m, APIResp)) -> APIHandler m GH.ReqsText
|
||||
|
||||
boolToText :: Bool -> Text
|
||||
boolToText = bool "false" "true"
|
||||
@ -210,7 +208,7 @@ mkGetHandler = AHGet
|
||||
mkPostHandler :: (a -> Handler m (HttpLogMetadata m, APIResp)) -> APIHandler m a
|
||||
mkPostHandler = AHPost
|
||||
|
||||
mkGQLRequestHandler :: (ReqsText -> Handler m (HttpLogMetadata m, APIResp)) -> APIHandler m ReqsText
|
||||
mkGQLRequestHandler :: (GH.ReqsText -> Handler m (HttpLogMetadata m, APIResp)) -> APIHandler m GH.ReqsText
|
||||
mkGQLRequestHandler = AHGraphQLRequest
|
||||
|
||||
mkAPIRespHandler :: (Functor m) => (a -> Handler m (HttpResponse EncJSON)) -> (a -> Handler m APIResp)
|
||||
@ -550,7 +548,7 @@ v1Alpha1GQHandler
|
||||
, MonadMetadataStorage (MetadataStorageT m)
|
||||
, EB.MonadQueryTags m
|
||||
)
|
||||
=> E.GraphQLQueryType -> GH.GQLBatchedReqs GH.GQLQueryText
|
||||
=> E.GraphQLQueryType -> GH.GQLBatchedReqs (GH.GQLReq GH.GQLQueryText)
|
||||
-> m (HttpLogMetadata m, HttpResponse EncJSON)
|
||||
v1Alpha1GQHandler queryType query = do
|
||||
userInfo <- asks hcUser
|
||||
@ -600,7 +598,7 @@ v1GQHandler
|
||||
, MonadMetadataStorage (MetadataStorageT m)
|
||||
, EB.MonadQueryTags m
|
||||
)
|
||||
=> GH.GQLBatchedReqs GH.GQLQueryText
|
||||
=> GH.GQLBatchedReqs (GH.GQLReq GH.GQLQueryText)
|
||||
-> m (HttpLogMetadata m, HttpResponse EncJSON)
|
||||
v1GQHandler = v1Alpha1GQHandler E.QueryHasura
|
||||
|
||||
@ -618,7 +616,7 @@ v1GQRelayHandler
|
||||
, MonadMetadataStorage (MetadataStorageT m)
|
||||
, EB.MonadQueryTags m
|
||||
)
|
||||
=> GH.GQLBatchedReqs GH.GQLQueryText
|
||||
=> GH.GQLBatchedReqs (GH.GQLReq GH.GQLQueryText)
|
||||
-> m (HttpLogMetadata m, HttpResponse EncJSON)
|
||||
v1GQRelayHandler = v1Alpha1GQHandler E.QueryRelay
|
||||
|
||||
|
@ -25,26 +25,27 @@ module Hasura.Server.Auth
|
||||
|
||||
import Hasura.Prelude
|
||||
|
||||
import qualified Crypto.Hash as Crypto
|
||||
import qualified Data.Text.Encoding as T
|
||||
import qualified Network.HTTP.Client as H
|
||||
import qualified Network.HTTP.Types as N
|
||||
import qualified Crypto.Hash as Crypto
|
||||
import qualified Data.Text.Encoding as T
|
||||
import qualified Network.HTTP.Client as H
|
||||
import qualified Network.HTTP.Types as N
|
||||
|
||||
import Control.Concurrent.Extended (ForkableMonadIO, forkManagedT)
|
||||
import Control.Monad.Morph (hoist)
|
||||
import Control.Monad.Trans.Control (MonadBaseControl)
|
||||
import Control.Monad.Trans.Managed (ManagedT)
|
||||
import Data.IORef (newIORef)
|
||||
import Data.Time.Clock (UTCTime)
|
||||
import Control.Concurrent.Extended (ForkableMonadIO, forkManagedT)
|
||||
import Control.Monad.Morph (hoist)
|
||||
import Control.Monad.Trans.Control (MonadBaseControl)
|
||||
import Control.Monad.Trans.Managed (ManagedT)
|
||||
import Data.IORef (newIORef)
|
||||
import Data.Time.Clock (UTCTime)
|
||||
|
||||
import qualified Hasura.Tracing as Tracing
|
||||
import qualified Hasura.Tracing as Tracing
|
||||
|
||||
import Hasura.Base.Error
|
||||
import Hasura.GraphQL.Transport.HTTP.Protocol (ReqsText)
|
||||
import Hasura.Logging
|
||||
import Hasura.Server.Auth.JWT hiding (processJwt_)
|
||||
import Hasura.Server.Auth.JWT hiding (processJwt_)
|
||||
import Hasura.Server.Auth.WebHook
|
||||
import Hasura.Server.Utils
|
||||
import Hasura.Server.Version (HasVersion)
|
||||
import Hasura.Server.Version (HasVersion)
|
||||
import Hasura.Session
|
||||
|
||||
|
||||
|
@ -3,7 +3,6 @@ module Hasura.Server.Auth.WebHook
|
||||
, AuthHookG (..)
|
||||
, AuthHook
|
||||
, userInfoFromAuthHook
|
||||
, type ReqsText
|
||||
) where
|
||||
|
||||
import Hasura.Prelude
|
||||
@ -59,8 +58,6 @@ hookMethod authHook = case ahType authHook of
|
||||
AHTGet -> N.GET
|
||||
AHTPost -> N.POST
|
||||
|
||||
type ReqsText = GH.GQLBatchedReqs GH.GQLQueryText
|
||||
|
||||
-- | Makes an authentication request to the given AuthHook and returns
|
||||
-- UserInfo parsed from the response, plus an expiration time if one
|
||||
-- was returned. Optionally passes a batch of raw GraphQL requests
|
||||
@ -72,7 +69,7 @@ userInfoFromAuthHook
|
||||
-> H.Manager
|
||||
-> AuthHook
|
||||
-> [N.Header]
|
||||
-> Maybe ReqsText
|
||||
-> Maybe GH.ReqsText
|
||||
-> m (UserInfo, Maybe UTCTime)
|
||||
userInfoFromAuthHook logger manager hook reqHeaders reqs = do
|
||||
resp <- (`onLeft` logAndThrow) =<< try performHTTPRequest
|
||||
|
@ -14,6 +14,9 @@ module Hasura.Server.Logging
|
||||
, WebHookLog(..)
|
||||
, HttpException
|
||||
, HttpLog (..)
|
||||
, GQLBatchQueryOperationLog (..)
|
||||
, GQLQueryOperationSuccessLog (..)
|
||||
, GQLQueryOperationErrorLog (..)
|
||||
, MetadataLog(..)
|
||||
, EnvVarsMovedToMetadata(..)
|
||||
, DeprecatedEnvVars(..)
|
||||
@ -26,20 +29,23 @@ module Hasura.Server.Logging
|
||||
|
||||
import Hasura.Prelude
|
||||
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import qualified Data.Environment as Env
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import qualified Data.HashSet as Set
|
||||
import qualified Data.TByteString as TBS
|
||||
import qualified Data.Text as T
|
||||
import qualified Network.HTTP.Types as HTTP
|
||||
import qualified Network.Wai.Extended as Wai
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import qualified Data.Environment as Env
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import qualified Data.HashSet as Set
|
||||
import qualified Data.List.NonEmpty as NE
|
||||
import qualified Data.TByteString as TBS
|
||||
import qualified Data.Text as T
|
||||
import qualified Network.HTTP.Types as HTTP
|
||||
import qualified Network.Wai.Extended as Wai
|
||||
|
||||
import Data.Aeson
|
||||
import Data.Aeson.TH
|
||||
import Data.Int (Int64)
|
||||
import Data.Int (Int64)
|
||||
import Data.Text.Extended
|
||||
|
||||
import qualified Hasura.GraphQL.Transport.HTTP.Protocol as GH
|
||||
|
||||
import Hasura.Base.Error
|
||||
import Hasura.GraphQL.ParameterizedQueryHash
|
||||
import Hasura.HTTP
|
||||
@ -48,11 +54,11 @@ import Hasura.Metadata.Class
|
||||
import Hasura.RQL.Types
|
||||
import Hasura.Server.Compression
|
||||
import Hasura.Server.Types
|
||||
import Hasura.Server.Utils (DeprecatedEnvVars (..),
|
||||
EnvVarsMovedToMetadata (..),
|
||||
deprecatedEnvVars, envVarsMovedToMetadata)
|
||||
import Hasura.Server.Utils (DeprecatedEnvVars (..),
|
||||
EnvVarsMovedToMetadata (..),
|
||||
deprecatedEnvVars, envVarsMovedToMetadata)
|
||||
import Hasura.Session
|
||||
import Hasura.Tracing (TraceT)
|
||||
import Hasura.Tracing (TraceT)
|
||||
|
||||
|
||||
data StartupLog
|
||||
@ -133,6 +139,36 @@ instance ToJSON WebHookLog where
|
||||
, "message" .= whlMessage whl
|
||||
]
|
||||
|
||||
-- | GQLQueryOperationSuccessLog captures all the data required to construct
|
||||
-- an HTTP success log.
|
||||
data GQLQueryOperationSuccessLog
|
||||
= GQLQueryOperationSuccessLog
|
||||
{ gqolQuery :: !GH.GQLReqUnparsed
|
||||
, gqolQueryExecutionTime :: !DiffTime
|
||||
, gqolResponseSize :: !Int64
|
||||
, gqolRequestSize :: !Int64
|
||||
, gqolParameterizedQueryHash :: !ParameterizedQueryHash
|
||||
} deriving (Show, Eq)
|
||||
$(deriveToJSON hasuraJSON{omitNothingFields = True} ''GQLQueryOperationSuccessLog)
|
||||
|
||||
-- | GQLQueryOperationErrorLog captures the request along with the error message
|
||||
data GQLQueryOperationErrorLog
|
||||
= GQLQueryOperationErrorLog
|
||||
{ gqelQuery :: !GH.GQLReqUnparsed
|
||||
, gqelError :: !QErr
|
||||
} deriving (Show, Eq)
|
||||
$(deriveToJSON hasuraJSON ''GQLQueryOperationErrorLog)
|
||||
|
||||
data GQLBatchQueryOperationLog
|
||||
= GQLQueryOperationSuccess !GQLQueryOperationSuccessLog
|
||||
| GQLQueryOperationError !GQLQueryOperationErrorLog
|
||||
deriving (Show, Eq)
|
||||
|
||||
instance ToJSON GQLBatchQueryOperationLog where
|
||||
toJSON = \case
|
||||
GQLQueryOperationSuccess successLog -> toJSON successLog
|
||||
GQLQueryOperationError errorLog -> toJSON errorLog
|
||||
|
||||
-- | whether a request is executed in batched mode or not
|
||||
data RequestMode
|
||||
= RequestModeBatched
|
||||
@ -152,7 +188,11 @@ instance ToJSON RequestMode where
|
||||
RequestModeNonBatchable -> "non-graphql"
|
||||
RequestModeError -> "error"
|
||||
|
||||
newtype CommonHttpLogMetadata = CommonHttpLogMetadata {_chlmRequestMode :: RequestMode}
|
||||
data CommonHttpLogMetadata
|
||||
= CommonHttpLogMetadata
|
||||
{ _chlmRequestMode :: !RequestMode
|
||||
, _chlmBatchOperationLog :: !(Maybe (GH.GQLBatchedReqs GQLBatchQueryOperationLog))
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
-- | The http-log metadata attached to HTTP requests running in the monad 'm', split into a
|
||||
@ -168,13 +208,14 @@ buildHttpLogMetadata
|
||||
. HttpLog m
|
||||
=> ParameterizedQueryHashList
|
||||
-> RequestMode
|
||||
-> Maybe (GH.GQLBatchedReqs GQLBatchQueryOperationLog)
|
||||
-> HttpLogMetadata m
|
||||
buildHttpLogMetadata xs rb = (CommonHttpLogMetadata rb, buildExtraHttpLogMetadata @m xs)
|
||||
buildHttpLogMetadata paramQueryHashList requestMode batchQueryOperationLog =
|
||||
(CommonHttpLogMetadata requestMode batchQueryOperationLog, buildExtraHttpLogMetadata @m paramQueryHashList)
|
||||
|
||||
-- | synonym for clarity, writing `emptyHttpLogMetadata @m` instead of `def @(HttpLogMetadata m)`
|
||||
emptyHttpLogMetadata :: forall m. HttpLog m => HttpLogMetadata m
|
||||
emptyHttpLogMetadata = (CommonHttpLogMetadata RequestModeNonBatchable, emptyExtraHttpLogMetadata @m)
|
||||
|
||||
emptyHttpLogMetadata = (CommonHttpLogMetadata RequestModeNonBatchable Nothing, emptyExtraHttpLogMetadata @m)
|
||||
|
||||
{- Note [Disable query printing when query-log is disabled]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -312,13 +353,43 @@ data OperationLog
|
||||
|
||||
$(deriveToJSON hasuraJSON{omitNothingFields = True} ''OperationLog)
|
||||
|
||||
-- | @BatchOperationSuccessLog@ contains the information required for a single
|
||||
-- successful operation in a batch request for OSS. This type is a subset of the @GQLQueryOperationSuccessLog@
|
||||
data BatchOperationSuccessLog
|
||||
= BatchOperationSuccessLog
|
||||
{ bolQuery :: !(Maybe Value)
|
||||
, bolResponseSize :: !Int64
|
||||
, bolQueryExecutionTime :: !Seconds
|
||||
} deriving (Show, Eq)
|
||||
$(deriveToJSON hasuraJSON{omitNothingFields = True} ''BatchOperationSuccessLog)
|
||||
|
||||
-- | @BatchOperationSuccessLog@ contains the information required for a single
|
||||
-- erroneous operation in a batch request for OSS. This type is a subset of the @GQLQueryOperationErrorLog@
|
||||
data BatchOperationErrorLog
|
||||
= BatchOperationErrorLog
|
||||
{ belQuery :: !(Maybe Value)
|
||||
, belError :: !QErr
|
||||
} deriving (Show, Eq)
|
||||
$(deriveToJSON hasuraJSON{omitNothingFields = True} ''BatchOperationErrorLog)
|
||||
|
||||
data BatchOperationLog
|
||||
= BatchOperationSuccess !BatchOperationSuccessLog
|
||||
| BatchOperationError !BatchOperationErrorLog
|
||||
deriving (Show, Eq)
|
||||
|
||||
instance ToJSON BatchOperationLog where
|
||||
toJSON = \case
|
||||
BatchOperationSuccess successLog -> toJSON successLog
|
||||
BatchOperationError errorLog -> toJSON errorLog
|
||||
|
||||
data HttpLogContext
|
||||
= HttpLogContext
|
||||
{ hlcHttpInfo :: !HttpInfoLog
|
||||
, hlcOperation :: !OperationLog
|
||||
, hlcRequestId :: !RequestId
|
||||
{ hlcHttpInfo :: !HttpInfoLog
|
||||
, hlcOperation :: !OperationLog
|
||||
, hlcRequestId :: !RequestId
|
||||
, hlcBatchedOperations :: !(Maybe (NE.NonEmpty BatchOperationLog))
|
||||
} deriving (Show, Eq)
|
||||
$(deriveToJSON hasuraJSON ''HttpLogContext)
|
||||
$(deriveToJSON hasuraJSON {omitNothingFields = True} ''HttpLogContext)
|
||||
|
||||
mkHttpAccessLogContext
|
||||
:: Maybe UserInfo
|
||||
@ -332,8 +403,9 @@ mkHttpAccessLogContext
|
||||
-> Maybe CompressionType
|
||||
-> [HTTP.Header]
|
||||
-> RequestMode
|
||||
-> Maybe (GH.GQLBatchedReqs GQLBatchQueryOperationLog)
|
||||
-> HttpLogContext
|
||||
mkHttpAccessLogContext userInfoM enabledLogTypes reqId req (_, parsedReq) res mTiming compressTypeM headers batching =
|
||||
mkHttpAccessLogContext userInfoM enabledLogTypes reqId req (_, parsedReq) res mTiming compressTypeM headers batching queryLogMetadata =
|
||||
let http = HttpInfoLog
|
||||
{ hlStatus = status
|
||||
, hlMethod = bsToTxt $ Wai.requestMethod req
|
||||
@ -355,7 +427,26 @@ mkHttpAccessLogContext userInfoM enabledLogTypes reqId req (_, parsedReq) res mT
|
||||
, olRawQuery = Nothing
|
||||
, olError = Nothing
|
||||
}
|
||||
in HttpLogContext http op reqId
|
||||
batchOpLog =
|
||||
queryLogMetadata >>= (\case
|
||||
GH.GQLSingleRequest _ -> Nothing -- This case is aleady handled in the `OperationLog`
|
||||
GH.GQLBatchedReqs opLogs ->
|
||||
NE.nonEmpty $
|
||||
map (\opLog ->
|
||||
case opLog of
|
||||
GQLQueryOperationSuccess (GQLQueryOperationSuccessLog {..}) ->
|
||||
BatchOperationSuccess $
|
||||
BatchOperationSuccessLog
|
||||
((bool Nothing (Just $ toJSON gqolQuery)) $ Set.member ELTQueryLog enabledLogTypes)
|
||||
gqolResponseSize
|
||||
(convertDuration gqolQueryExecutionTime)
|
||||
GQLQueryOperationError (GQLQueryOperationErrorLog {..}) ->
|
||||
BatchOperationError $
|
||||
BatchOperationErrorLog
|
||||
(bool Nothing (Just $ toJSON gqelQuery) $ Set.member ELTQueryLog enabledLogTypes)
|
||||
gqelError
|
||||
) opLogs)
|
||||
in HttpLogContext http op reqId batchOpLog
|
||||
where
|
||||
status = HTTP.status200
|
||||
respSize = Just $ BL.length res
|
||||
@ -398,7 +489,7 @@ mkHttpErrorLogContext userInfoM enabledLogTypes reqId waiReq (reqBody, parsedReq
|
||||
-- See Note [Disable query printing when query-log is disabled]
|
||||
reqToLog :: Maybe a -> Maybe a
|
||||
reqToLog req = bool Nothing req $ Set.member ELTQueryLog enabledLogTypes
|
||||
in HttpLogContext http op reqId
|
||||
in HttpLogContext http op reqId Nothing -- Batched operation logs are always reported in logHttpSuccess even if there are errors
|
||||
|
||||
data HttpLogLine
|
||||
= HttpLogLine
|
||||
|
@ -161,8 +161,9 @@ runCustomEndpoint env execCtx requestId userInfo reqHeaders ipAddress RestReques
|
||||
-- with the query string from the schema cache, and pass it
|
||||
-- through to the /v1/graphql endpoint.
|
||||
(httpLoggingMetadata, handlerResp) <- flip runReaderT execCtx $ do
|
||||
(parameterizedQueryHash, resp) <- GH.runGQ env (E._ecxLogger execCtx) requestId userInfo ipAddress reqHeaders E.QueryHasura (mkPassthroughRequest queryx resolvedVariables)
|
||||
let httpLogMetadata = buildHttpLogMetadata @m (PQHSetSingleton parameterizedQueryHash) RequestModeNonBatchable
|
||||
(gqlOperationLog, resp) <- GH.runGQ env (E._ecxLogger execCtx) requestId userInfo ipAddress reqHeaders E.QueryHasura (mkPassthroughRequest queryx resolvedVariables)
|
||||
let httpLogMetadata =
|
||||
buildHttpLogMetadata @m (PQHSetSingleton (gqolParameterizedQueryHash gqlOperationLog)) RequestModeNonBatchable Nothing
|
||||
return (httpLogMetadata, fst <$> resp)
|
||||
case sequence handlerResp of
|
||||
Just resp -> pure (httpLoggingMetadata, fmap encodeHTTPResp resp)
|
||||
|
@ -4,25 +4,25 @@ module Hasura.Server.AuthSpec (spec) where
|
||||
|
||||
import Hasura.Prelude
|
||||
|
||||
import qualified Crypto.JOSE.JWK as Jose
|
||||
import qualified Crypto.JWT as JWT
|
||||
import qualified Data.Aeson as J
|
||||
import qualified Data.HashMap.Strict as Map
|
||||
import qualified Network.HTTP.Types as N
|
||||
import qualified Crypto.JOSE.JWK as Jose
|
||||
import qualified Crypto.JWT as JWT
|
||||
import qualified Data.Aeson as J
|
||||
import qualified Data.HashMap.Strict as Map
|
||||
import qualified Network.HTTP.Types as N
|
||||
|
||||
import Control.Lens hiding ((.=))
|
||||
import Control.Lens hiding ((.=))
|
||||
import Control.Monad.Trans.Control
|
||||
import Control.Monad.Trans.Managed (lowerManagedT)
|
||||
import Data.Aeson ((.=))
|
||||
import Control.Monad.Trans.Managed (lowerManagedT)
|
||||
import Data.Aeson ((.=))
|
||||
import Data.Parser.JSONPath
|
||||
|
||||
import qualified Hasura.Tracing as Tracing
|
||||
import qualified Hasura.Tracing as Tracing
|
||||
|
||||
import Hasura.Base.Error
|
||||
import Hasura.GraphQL.Transport.HTTP.Protocol (ReqsText)
|
||||
import Hasura.Logging
|
||||
import Hasura.Server.Auth hiding (getUserInfoWithExpTime, processJwt)
|
||||
import Hasura.Server.Auth.JWT hiding (processJwt)
|
||||
import Hasura.Server.Auth.WebHook (ReqsText)
|
||||
import Hasura.Server.Auth hiding (getUserInfoWithExpTime, processJwt)
|
||||
import Hasura.Server.Auth.JWT hiding (processJwt)
|
||||
import Hasura.Server.Utils
|
||||
import Hasura.Server.Version
|
||||
import Hasura.Session
|
||||
|
Loading…
Reference in New Issue
Block a user