From cf41d2abd93a26023b965c2fce79cd9eec8f945a Mon Sep 17 00:00:00 2001 From: paritosh-08 <85472423+paritosh-08@users.noreply.github.com> Date: Thu, 6 Jun 2024 23:54:33 +0530 Subject: [PATCH] add postgres query to traces PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10853 GitOrigin-RevId: b47c7077fc8fc7115ab05dffe93195abfeb4131d --- .../graphql-engine-flags/reference.mdx | 13 ++++++ .../lib/test-harness/src/Harness/Constants.hs | 3 +- server/src-lib/Hasura/App/State.hs | 6 ++- .../Backends/BigQuery/Instances/Execute.hs | 13 +++--- .../Backends/DataConnector/Adapter/Execute.hs | 6 +-- .../Backends/MSSQL/Instances/Execute.hs | 11 +++-- .../Backends/Postgres/Instances/Execute.hs | 45 +++++++------------ server/src-lib/Hasura/GraphQL/Execute.hs | 8 +++- .../src-lib/Hasura/GraphQL/Execute/Backend.hs | 3 ++ .../Hasura/GraphQL/Execute/Mutation.hs | 6 ++- .../src-lib/Hasura/GraphQL/Execute/Query.hs | 8 ++-- .../Hasura/GraphQL/Execute/RemoteJoin/Join.hs | 12 +++-- .../GraphQL/Execute/RemoteJoin/Source.hs | 9 ++-- .../src-lib/Hasura/GraphQL/Transport/HTTP.hs | 22 +++++---- .../Hasura/GraphQL/Transport/WSServerApp.hs | 3 +- .../Hasura/GraphQL/Transport/WebSocket.hs | 20 +++++---- server/src-lib/Hasura/Server/App.hs | 4 +- server/src-lib/Hasura/Server/Init.hs | 1 + .../Hasura/Server/Init/Arg/Command/Serve.hs | 19 ++++++++ server/src-lib/Hasura/Server/Init/Config.hs | 6 ++- server/src-lib/Hasura/Server/Init/Env.hs | 3 ++ server/src-lib/Hasura/Server/Rest.hs | 5 ++- server/src-lib/Hasura/Server/Types.hs | 16 +++++++ server/src-test/Hasura/Server/InitSpec.hs | 3 +- server/test-postgres/Constants.hs | 3 +- 25 files changed, 165 insertions(+), 83 deletions(-) diff --git a/docs/docs/deployment/graphql-engine-flags/reference.mdx b/docs/docs/deployment/graphql-engine-flags/reference.mdx index 01807d0babd..81f23fe3322 100644 --- a/docs/docs/deployment/graphql-engine-flags/reference.mdx +++ b/docs/docs/deployment/graphql-engine-flags/reference.mdx @@ -1229,6 +1229,19 @@ This sets transaction isolation. | **Default** | `read-committed` | | **Supported in** | Deprecated in versions > `v2.0.0` | +### Trace SQL Queries + +Add SQL queries to the OTLP traces. + +| | | +| ------------------- | ---------------------------------------- | +| **Flag** | `--trace-sql-query` | +| **Env var** | `HASURA_GRAPHQL_ENABLE_QUERY_TRACING` | +| **Accepted values** | Boolean | +| **Options** | `true` or `false` | +| **Default** | `false` | +| **Supported in** | CE, Enterprise Edition | + ### Unauthorized Role This identifies an [unauthorized role](/auth/authentication/unauthenticated-access.mdx), used when the diff --git a/server/lib/test-harness/src/Harness/Constants.hs b/server/lib/test-harness/src/Harness/Constants.hs index 7ed51c7d415..66f1b9d0420 100644 --- a/server/lib/test-harness/src/Harness/Constants.hs +++ b/server/lib/test-harness/src/Harness/Constants.hs @@ -332,7 +332,8 @@ serveOptions = soPersistedQueries = Init._default Init.persistedQueriesOption, soPersistedQueriesTtl = Init._default Init.persistedQueriesTtlOption, soRemoteSchemaResponsePriority = Init._default Init.remoteSchemaResponsePriorityOption, - soHeaderPrecedence = Init._default Init.configuredHeaderPrecedenceOption + soHeaderPrecedence = Init._default Init.configuredHeaderPrecedenceOption, + soTraceQueryStatus = Init._default Init.traceQueryStatusOption } -- | What log level should be used by the engine; this is not exported, and diff --git a/server/src-lib/Hasura/App/State.hs b/server/src-lib/Hasura/App/State.hs index 22525c8a4af..21c38e3a62b 100644 --- a/server/src-lib/Hasura/App/State.hs +++ b/server/src-lib/Hasura/App/State.hs @@ -174,7 +174,8 @@ data AppContext = AppContext acCloseWebsocketsOnMetadataChangeStatus :: CloseWebsocketsOnMetadataChangeStatus, acSchemaSampledFeatureFlags :: SchemaSampledFeatureFlags, acRemoteSchemaResponsePriority :: RemoteSchemaResponsePriority, - acHeaderPrecedence :: HeaderPrecedence + acHeaderPrecedence :: HeaderPrecedence, + acTraceQueryStatus :: TraceQueryStatus } -- | Collection of the LoggerCtx, the regular Logger and the PGLogger @@ -297,7 +298,8 @@ buildAppContextRule = proc (ServeOptions {..}, env, _keys, checkFeatureFlag) -> acCloseWebsocketsOnMetadataChangeStatus = soCloseWebsocketsOnMetadataChangeStatus, acSchemaSampledFeatureFlags = schemaSampledFeatureFlags, acRemoteSchemaResponsePriority = soRemoteSchemaResponsePriority, - acHeaderPrecedence = soHeaderPrecedence + acHeaderPrecedence = soHeaderPrecedence, + acTraceQueryStatus = soTraceQueryStatus } where buildEventEngineCtx = Inc.cache proc (httpPoolSize, fetchInterval, fetchBatchSize) -> do diff --git a/server/src-lib/Hasura/Backends/BigQuery/Instances/Execute.hs b/server/src-lib/Hasura/Backends/BigQuery/Instances/Execute.hs index 798e13c3a6b..5c25b527847 100644 --- a/server/src-lib/Hasura/Backends/BigQuery/Instances/Execute.hs +++ b/server/src-lib/Hasura/Backends/BigQuery/Instances/Execute.hs @@ -38,7 +38,7 @@ import Hasura.RQL.Types.Column import Hasura.RQL.Types.Common import Hasura.RQL.Types.Schema.Options qualified as Options import Hasura.SQL.AnyBackend qualified as AB -import Hasura.Server.Types (HeaderPrecedence) +import Hasura.Server.Types (HeaderPrecedence, TraceQueryStatus) import Hasura.Session import Language.GraphQL.Draft.Syntax qualified as G import Network.HTTP.Client as HTTP @@ -78,8 +78,9 @@ bqDBQueryPlan :: QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery) -> [HTTP.Header] -> Maybe G.Name -> + TraceQueryStatus -> m (DBStepInfo 'BigQuery, [ModelInfoPart]) -bqDBQueryPlan userInfo sourceName sourceConfig qrf _ _ = do +bqDBQueryPlan userInfo sourceName sourceConfig qrf _ _ _ = do -- TODO (naveen): Append query tags to the query select <- planNoPlan (BigQuery.bigQuerySourceConfigToFromIrConfig sourceConfig) userInfo qrf let action = OnBaseMonad do @@ -147,8 +148,9 @@ bqDBMutationPlan :: Maybe G.Name -> Maybe (HashMap G.Name (G.Value G.Variable)) -> HeaderPrecedence -> + TraceQueryStatus -> m (DBStepInfo 'BigQuery, [ModelInfoPart]) -bqDBMutationPlan _env _manager _logger _userInfo _stringifyNum _sourceName _sourceConfig _mrf _headers _gName _maybeSelSetArgs _ = +bqDBMutationPlan _env _manager _logger _userInfo _stringifyNum _sourceName _sourceConfig _mrf _headers _gName _maybeSelSetArgs _ _traceQueryStatus = throw500 "mutations are not supported in BigQuery; this should be unreachable" -- explain @@ -230,9 +232,10 @@ bqDBRemoteRelationshipPlan :: [HTTP.Header] -> Maybe G.Name -> Options.StringifyNumbers -> + TraceQueryStatus -> m (DBStepInfo 'BigQuery, [ModelInfoPart]) -bqDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argumentId relationship reqHeaders operationName stringifyNumbers = do - (dbStepInfo, modelInfo) <- flip runReaderT emptyQueryTagsComment $ bqDBQueryPlan userInfo sourceName sourceConfig rootSelection reqHeaders operationName +bqDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argumentId relationship reqHeaders operationName stringifyNumbers traceQueryStatus = do + (dbStepInfo, modelInfo) <- flip runReaderT emptyQueryTagsComment $ bqDBQueryPlan userInfo sourceName sourceConfig rootSelection reqHeaders operationName traceQueryStatus pure (dbStepInfo, modelInfo) where coerceToColumn = BigQuery.ColumnName . getFieldNameTxt diff --git a/server/src-lib/Hasura/Backends/DataConnector/Adapter/Execute.hs b/server/src-lib/Hasura/Backends/DataConnector/Adapter/Execute.hs index 71f2a140cc2..e03f13ed4c1 100644 --- a/server/src-lib/Hasura/Backends/DataConnector/Adapter/Execute.hs +++ b/server/src-lib/Hasura/Backends/DataConnector/Adapter/Execute.hs @@ -54,7 +54,7 @@ instance BackendExecute 'DataConnector where type MultiplexedQuery 'DataConnector = Void type ExecutionMonad 'DataConnector = AgentClientT - mkDBQueryPlan UserInfo {..} sourceName sourceConfig ir _headers _gName = do + mkDBQueryPlan UserInfo {..} sourceName sourceConfig ir _headers _gName _traceQueryStatus = do queryPlan@Plan {..} <- flip runReaderT (API._cScalarTypes $ _scCapabilities sourceConfig, _uiSession) $ Plan.mkQueryPlan ir transformedSourceConfig <- transformSourceConfig sourceConfig (Just _uiSession) modelNames <- irToModelInfoGen sourceName ModelSourceTypeDataConnector ir @@ -84,7 +84,7 @@ instance BackendExecute 'DataConnector where dbsiResolvedConnectionTemplate = () } - mkDBMutationPlan _env _manager _logger UserInfo {..} _stringifyNum sourceName sourceConfig mutationDB _headers _gName _maybeSelSetArgs _ = do + mkDBMutationPlan _env _manager _logger UserInfo {..} _stringifyNum sourceName sourceConfig mutationDB _headers _gName _maybeSelSetArgs _ _traceQueryStatus = do (mutationPlan@Plan {..}, modelNames) <- flip runReaderT (API._cScalarTypes $ _scCapabilities sourceConfig, _uiSession) $ Plan.mkMutationPlan sourceName ModelSourceTypeDataConnector mutationDB transformedSourceConfig <- transformSourceConfig sourceConfig (Just _uiSession) let modelInfo = getModelInfoPartfromModelNames modelNames (ModelOperationType G.OperationTypeMutation) @@ -105,7 +105,7 @@ instance BackendExecute 'DataConnector where mkDBStreamingSubscriptionPlan _ _ _ _ _ _ = throw400 NotSupported "mkLiveQuerySubscriptionPlan: not implemented for the Data Connector backend." - mkDBRemoteRelationshipPlan UserInfo {..} sourceName sourceConfig joinIds joinIdsSchema argumentIdFieldName (resultFieldName, ir) _ _ _ = do + mkDBRemoteRelationshipPlan UserInfo {..} sourceName sourceConfig joinIds joinIdsSchema argumentIdFieldName (resultFieldName, ir) _ _ _ _ = do (remoteRelationshipPlan@Plan {..}, modelInfo) <- flip runReaderT (API._cScalarTypes $ _scCapabilities sourceConfig, _uiSession) $ Plan.mkRemoteRelationshipPlan sourceName sourceConfig joinIds joinIdsSchema argumentIdFieldName resultFieldName ir transformedSourceConfig <- transformSourceConfig sourceConfig (Just _uiSession) pure diff --git a/server/src-lib/Hasura/Backends/MSSQL/Instances/Execute.hs b/server/src-lib/Hasura/Backends/MSSQL/Instances/Execute.hs index 06bc5b03893..87324ecb6fc 100644 --- a/server/src-lib/Hasura/Backends/MSSQL/Instances/Execute.hs +++ b/server/src-lib/Hasura/Backends/MSSQL/Instances/Execute.hs @@ -55,7 +55,7 @@ import Hasura.RQL.Types.Column qualified as RQLColumn import Hasura.RQL.Types.Common as RQLTypes import Hasura.RQL.Types.Schema.Options qualified as Options import Hasura.SQL.AnyBackend qualified as AB -import Hasura.Server.Types (HeaderPrecedence) +import Hasura.Server.Types (HeaderPrecedence, TraceQueryStatus) import Hasura.Session import Language.GraphQL.Draft.Syntax qualified as G import Network.HTTP.Client as HTTP @@ -100,8 +100,9 @@ msDBQueryPlan :: QueryDB 'MSSQL Void (UnpreparedValue 'MSSQL) -> [HTTP.Header] -> Maybe G.Name -> + TraceQueryStatus -> m (DBStepInfo 'MSSQL, [ModelInfoPart]) -msDBQueryPlan userInfo sourceName sourceConfig qrf _ _ = do +msDBQueryPlan userInfo sourceName sourceConfig qrf _ _ _ = do let sessionVariables = _uiSession userInfo QueryWithDDL {qwdBeforeSteps, qwdAfterSteps, qwdQuery = statement} <- planQuery sessionVariables qrf queryTags <- ask @@ -319,8 +320,9 @@ msDBMutationPlan :: Maybe G.Name -> Maybe (HashMap G.Name (G.Value G.Variable)) -> HeaderPrecedence -> + TraceQueryStatus -> m (DBStepInfo 'MSSQL, [ModelInfoPart]) -msDBMutationPlan _env _manager _logger userInfo stringifyNum sourceName sourceConfig mrf _headers _gName _maybeSelSetArgs _ = do +msDBMutationPlan _env _manager _logger userInfo stringifyNum sourceName sourceConfig mrf _headers _gName _maybeSelSetArgs _ _ = do go <$> case mrf of MDBInsert annInsert -> executeInsert userInfo stringifyNum sourceName ModelSourceTypeMSSQL sourceConfig annInsert MDBDelete annDelete -> executeDelete userInfo stringifyNum sourceName ModelSourceTypeMSSQL sourceConfig annDelete @@ -525,8 +527,9 @@ msDBRemoteRelationshipPlan :: [HTTP.Header] -> Maybe G.Name -> Options.StringifyNumbers -> + TraceQueryStatus -> m (DBStepInfo 'MSSQL, [ModelInfoPart]) -msDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argumentId relationship _headers _gName _stringifyNumbers = do +msDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argumentId relationship _headers _gName _stringifyNumbers _traceQueryStatus = do -- `stringifyNumbers` is not currently handled in any SQL Server operation statement <- planSourceRelationship (_uiSession userInfo) lhs lhsSchema argumentId relationship diff --git a/server/src-lib/Hasura/Backends/Postgres/Instances/Execute.hs b/server/src-lib/Hasura/Backends/Postgres/Instances/Execute.hs index 06b7b398b8f..6932d1e5006 100644 --- a/server/src-lib/Hasura/Backends/Postgres/Instances/Execute.hs +++ b/server/src-lib/Hasura/Backends/Postgres/Instances/Execute.hs @@ -10,7 +10,6 @@ -- This module includes the Postgres implementation of queries, mutations, and more. module Hasura.Backends.Postgres.Instances.Execute ( PreparedSql (..), - pgDBQueryPlanSimple, ) where @@ -93,7 +92,7 @@ import Hasura.RQL.Types.Common (FieldName (..), JsonAggSelect (..), SourceName ( import Hasura.RQL.Types.Permission (ValidateInput (..), ValidateInputHttpDefinition (..)) import Hasura.RQL.Types.Schema.Options qualified as Options import Hasura.SQL.AnyBackend qualified as AB -import Hasura.Server.Types (HeaderPrecedence) +import Hasura.Server.Types (HeaderPrecedence, TraceQueryStatus (TraceQueryEnabled)) import Hasura.Session (UserInfo (..)) import Hasura.Tracing qualified as Tracing import Language.GraphQL.Draft.Syntax qualified as G @@ -140,8 +139,9 @@ pgDBQueryPlan :: QueryDB ('Postgres pgKind) Void (UnpreparedValue ('Postgres pgKind)) -> [HTTP.Header] -> Maybe G.Name -> + TraceQueryStatus -> m ((DBStepInfo ('Postgres pgKind)), [ModelInfoPart]) -pgDBQueryPlan userInfo sourceName sourceConfig qrf reqHeaders operationName = do +pgDBQueryPlan userInfo sourceName sourceConfig qrf reqHeaders operationName traceQueryStatus = do (preparedQuery, PlanningSt {_psPrepped = planVals}) <- flip runStateT initPlanningSt $ traverse (prepareWithPlan userInfo) qrf queryTagsComment <- ask @@ -157,26 +157,9 @@ pgDBQueryPlan userInfo sourceName sourceConfig qrf reqHeaders operationName = do modelNames <- irToModelInfoGen sourceName ModelSourceTypePostgres preparedQuery let modelInfo = getModelInfoPartfromModelNames modelNames (ModelOperationType G.OperationTypeQuery) let preparedSQLWithQueryTags = appendPreparedSQLWithQueryTags rootFieldPlan queryTagsComment - let (action, preparedSQL) = mkCurPlanTx userInfo preparedSQLWithQueryTags + let (action, preparedSQL) = mkCurPlanTx userInfo preparedSQLWithQueryTags traceQueryStatus pure $ (DBStepInfo @('Postgres pgKind) sourceName sourceConfig preparedSQL (fmap withNoStatistics action) resolvedConnectionTemplate, modelInfo) --- | Used by the @dc-postgres-agent to compile a query. -pgDBQueryPlanSimple :: - (MonadError QErr m, MonadIO m) => - UserInfo -> - QueryTagsComment -> - QueryDB ('Postgres 'Vanilla) Void (UnpreparedValue ('Postgres 'Vanilla)) -> - m (OnBaseMonad (PG.TxET QErr) EncJSON, Maybe PreparedSql) -pgDBQueryPlanSimple userInfo queryTagsComment query = do - (preparedQuery, PlanningSt {_psPrepped = planVals}) <- - flip runStateT initPlanningSt $ traverse (prepareWithPlan userInfo) query - rootFieldPlan <- irToRootFieldPlan userInfo planVals preparedQuery - -- seems like this function is not being used anywhere in graphql-engine, so we're not going to count the models used - let preparedSQLWithQueryTags = - appendPreparedSQLWithQueryTags rootFieldPlan queryTagsComment - let (action, preparedSQL) = mkCurPlanTx userInfo preparedSQLWithQueryTags - pure (action, preparedSQL) - pgDBQueryExplain :: forall pgKind m. ( MonadError QErr m, @@ -401,11 +384,12 @@ convertFunction :: SourceName -> ModelSourceType -> UserInfo -> + TraceQueryStatus -> JsonAggSelect -> -- | VOLATILE function as 'SelectExp' IR.AnnSimpleSelectG ('Postgres pgKind) Void (UnpreparedValue ('Postgres pgKind)) -> m (OnBaseMonad (PG.TxET QErr) EncJSON, [ModelNameInfo]) -convertFunction sourceName modelSourceType userInfo jsonAggSelect unpreparedQuery = do +convertFunction sourceName modelSourceType userInfo traceQueryStatus jsonAggSelect unpreparedQuery = do queryTags <- ask -- Transform the RQL AST into a prepared SQL query (preparedQuery, PlanningSt {_psPrepped = planVals}) <- @@ -419,7 +403,7 @@ convertFunction sourceName modelSourceType userInfo jsonAggSelect unpreparedQuer modelNames <- irToModelInfoGen sourceName modelSourceType $ queryResultFn preparedQuery let preparedSQLWithQueryTags = appendPreparedSQLWithQueryTags rootFieldPlan queryTags pure - ( fst (mkCurPlanTx userInfo preparedSQLWithQueryTags), -- forget (Maybe PreparedSql) + ( fst (mkCurPlanTx userInfo preparedSQLWithQueryTags traceQueryStatus), -- forget (Maybe PreparedSql) modelNames ) @@ -444,8 +428,9 @@ pgDBMutationPlan :: Maybe G.Name -> Maybe (HashMap G.Name (G.Value G.Variable)) -> HeaderPrecedence -> + TraceQueryStatus -> m (DBStepInfo ('Postgres pgKind), [ModelInfoPart]) -pgDBMutationPlan env manager logger userInfo stringifyNum sourceName sourceConfig mrf reqHeaders operationName selSetArguments headerPrecedence = do +pgDBMutationPlan env manager logger userInfo stringifyNum sourceName sourceConfig mrf reqHeaders operationName selSetArguments headerPrecedence traceQueryStatus = do resolvedConnectionTemplate <- let connectionTemplateResolver = connectionTemplateConfigResolver (_pscConnectionTemplateConfig sourceConfig) @@ -458,7 +443,7 @@ pgDBMutationPlan env manager logger userInfo stringifyNum sourceName sourceConfi MDBInsert s -> convertInsert sourceName ModelSourceTypePostgres env manager logger userInfo s stringifyNum reqHeaders headerPrecedence MDBUpdate s -> convertUpdate sourceName ModelSourceTypePostgres env manager logger userInfo s stringifyNum reqHeaders selSetArguments headerPrecedence MDBDelete s -> convertDelete sourceName ModelSourceTypePostgres env manager logger userInfo s stringifyNum reqHeaders selSetArguments headerPrecedence - MDBFunction returnsSet s -> convertFunction sourceName ModelSourceTypePostgres userInfo returnsSet s + MDBFunction returnsSet s -> convertFunction sourceName ModelSourceTypePostgres userInfo traceQueryStatus returnsSet s where modelInfoList v = getModelInfoPartfromModelNames (snd v) (ModelOperationType G.OperationTypeMutation) go resolvedConnectionTemplate v = @@ -647,8 +632,9 @@ testMultiplexedQueryTx (PGL.MultiplexedQuery query) cohortId cohortVariables = d mkCurPlanTx :: UserInfo -> PreparedSql -> + TraceQueryStatus -> (OnBaseMonad (PG.TxET QErr) EncJSON, Maybe PreparedSql) -mkCurPlanTx userInfo ps@(PreparedSql q prepMap) = +mkCurPlanTx userInfo ps@(PreparedSql q prepMap) traceQueryStatus = -- generate the SQL and prepared vars or the bytestring let args = withUserVars (_uiSession userInfo) prepMap -- WARNING: this quietly assumes the intmap keys are contiguous @@ -656,6 +642,8 @@ mkCurPlanTx userInfo ps@(PreparedSql q prepMap) = in (,Just ps) $ OnBaseMonad do -- https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/database/#connection-level-attributes Tracing.attachMetadata [("db.system", "postgresql")] + when (traceQueryStatus == TraceQueryEnabled) + $ Tracing.attachMetadata [("db.query", PG.getQueryText q)] runIdentity . PG.getRow <$> PG.rawQE dmlTxErrorHandler q prepArgs True @@ -724,14 +712,15 @@ pgDBRemoteRelationshipPlan :: [HTTP.Header] -> Maybe G.Name -> Options.StringifyNumbers -> + TraceQueryStatus -> m (DBStepInfo ('Postgres pgKind), [ModelInfoPart]) -pgDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argumentId relationship reqHeaders operationName stringifyNumbers = do +pgDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argumentId relationship reqHeaders operationName stringifyNumbers traceQueryStatus = do -- NOTE: 'QueryTags' currently cannot support remote relationship queries. -- -- In the future if we want to add support we'll need to add a new type of -- metadata (e.g. 'ParameterizedQueryHash' doesn't make sense here) and find -- a root field name that makes sense to attach to it. - (dbStepInfo, modelInfo) <- flip runReaderT emptyQueryTagsComment $ pgDBQueryPlan userInfo sourceName sourceConfig rootSelection reqHeaders operationName + (dbStepInfo, modelInfo) <- flip runReaderT emptyQueryTagsComment $ pgDBQueryPlan userInfo sourceName sourceConfig rootSelection reqHeaders operationName traceQueryStatus pure (dbStepInfo, modelInfo) where coerceToColumn = Postgres.unsafePGCol . getFieldNameTxt diff --git a/server/src-lib/Hasura/GraphQL/Execute.hs b/server/src-lib/Hasura/GraphQL/Execute.hs index 214ef33f819..4b4d69641e6 100644 --- a/server/src-lib/Hasura/GraphQL/Execute.hs +++ b/server/src-lib/Hasura/GraphQL/Execute.hs @@ -63,7 +63,7 @@ import Hasura.RQL.Types.Subscription import Hasura.SQL.AnyBackend qualified as AB import Hasura.Server.Init qualified as Init import Hasura.Server.Prometheus (PrometheusMetrics) -import Hasura.Server.Types (HeaderPrecedence, MonadGetPolicies, ReadOnlyMode (..), RequestId (..)) +import Hasura.Server.Types (HeaderPrecedence, MonadGetPolicies, ReadOnlyMode (..), RequestId (..), TraceQueryStatus) import Hasura.Services import Hasura.Session (BackendOnlyFieldAccess (..), UserInfo (..)) import Hasura.Tracing qualified as Tracing @@ -364,6 +364,7 @@ getResolvedExecPlan :: RequestId -> Init.ResponseInternalErrorsConfig -> HeaderPrecedence -> + TraceQueryStatus -> m (ParameterizedQueryHash, ResolvedExecutionPlan, [ModelInfoPart]) getResolvedExecPlan env @@ -380,7 +381,8 @@ getResolvedExecPlan maybeOperationName reqId responseErrorsConfig - headerPrecedence = do + headerPrecedence + traceQueryStatus = do let gCtx = makeGQLContext userInfo sc queryType tracesPropagator = getOtelTracesPropagator $ scOpenTelemetryConfig sc @@ -407,6 +409,7 @@ getResolvedExecPlan maybeOperationName responseErrorsConfig headerPrecedence + traceQueryStatus Tracing.attachMetadata [("graphql.operation.type", "query"), ("parameterized_query_hash", bsToTxt $ unParamQueryHash parameterizedQueryHash)] pure (parameterizedQueryHash, QueryExecutionPlan executionPlan queryRootFields dirMap, modelInfoList) G.TypedOperationDefinition G.OperationTypeMutation _ varDefs directives inlinedSelSet -> Tracing.newSpan "Resolve mutation execution plan" $ do @@ -430,6 +433,7 @@ getResolvedExecPlan reqId maybeOperationName headerPrecedence + traceQueryStatus Tracing.attachMetadata [("graphql.operation.type", "mutation")] pure (parameterizedQueryHash, MutationExecutionPlan executionPlan, modelInfoList) G.TypedOperationDefinition G.OperationTypeSubscription _ varDefs directives inlinedSelSet -> Tracing.newSpan "Resolve subscription execution plan" $ do diff --git a/server/src-lib/Hasura/GraphQL/Execute/Backend.hs b/server/src-lib/Hasura/GraphQL/Execute/Backend.hs index e88d0d3b525..1a9b903eee2 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/Backend.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/Backend.hs @@ -82,6 +82,7 @@ class QueryDB b Void (UnpreparedValue b) -> [HTTP.Header] -> Maybe G.Name -> + TraceQueryStatus -> m ((DBStepInfo b), [ModelInfoPart]) mkDBMutationPlan :: forall m. @@ -103,6 +104,7 @@ class Maybe G.Name -> Maybe (HashMap G.Name (G.Value G.Variable)) -> HeaderPrecedence -> + TraceQueryStatus -> m (DBStepInfo b, [ModelInfoPart]) mkLiveQuerySubscriptionPlan :: forall m. @@ -178,6 +180,7 @@ class [HTTP.Header] -> Maybe G.Name -> Options.StringifyNumbers -> + TraceQueryStatus -> m (DBStepInfo b, [ModelInfoPart]) -- | This is a helper function to convert a remote source's relationship to a diff --git a/server/src-lib/Hasura/GraphQL/Execute/Mutation.hs b/server/src-lib/Hasura/GraphQL/Execute/Mutation.hs index bef56a1db6c..62ee023e69f 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/Mutation.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/Mutation.hs @@ -98,6 +98,7 @@ convertMutationSelectionSet :: -- | Graphql Operation Name Maybe G.Name -> HeaderPrecedence -> + TraceQueryStatus -> m (ExecutionPlan, ParameterizedQueryHash, [ModelInfoPart]) convertMutationSelectionSet env @@ -115,7 +116,8 @@ convertMutationSelectionSet introspectionDisabledRoles reqId maybeOperationName - headerPrecedence = do + headerPrecedence + traceQueryStatus = do mutationParser <- onNothing (gqlMutationParser gqlContext) $ throw400 ValidationFailed "no mutations exist" @@ -146,7 +148,7 @@ convertMutationSelectionSet httpManager <- askHTTPManager let selSetArguments = getSelSetArgsFromRootField resolvedSelSet rootFieldName - (dbStepInfo, dbModelInfoList) <- flip runReaderT queryTagsComment $ mkDBMutationPlan @b env httpManager logger userInfo stringifyNum sourceName sourceConfig noRelsDBAST reqHeaders maybeOperationName selSetArguments headerPrecedence + (dbStepInfo, dbModelInfoList) <- flip runReaderT queryTagsComment $ mkDBMutationPlan @b env httpManager logger userInfo stringifyNum sourceName sourceConfig noRelsDBAST reqHeaders maybeOperationName selSetArguments headerPrecedence traceQueryStatus pure $ (ExecStepDB [] (AB.mkAnyBackend dbStepInfo) remoteJoins, dbModelInfoList) RFRemote (RemoteSchemaName rName) remoteField -> do RemoteSchemaRootField remoteSchemaInfo resultCustomizer resolvedRemoteField <- runVariableCache $ resolveRemoteField userInfo remoteField diff --git a/server/src-lib/Hasura/GraphQL/Execute/Query.hs b/server/src-lib/Hasura/GraphQL/Execute/Query.hs index 284e1f58f88..747fb153a34 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/Query.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/Query.hs @@ -39,7 +39,7 @@ import Hasura.RemoteSchema.Metadata.Base (RemoteSchemaName (..)) import Hasura.SQL.AnyBackend qualified as AB import Hasura.Server.Init.Config (ResponseInternalErrorsConfig (..)) import Hasura.Server.Prometheus (PrometheusMetrics (..)) -import Hasura.Server.Types (HeaderPrecedence, MonadGetPolicies, RequestId (..)) +import Hasura.Server.Types (HeaderPrecedence, MonadGetPolicies, RequestId (..), TraceQueryStatus) import Hasura.Services.Network import Hasura.Session import Hasura.Tracing (MonadTrace) @@ -94,6 +94,7 @@ convertQuerySelSet :: Maybe G.Name -> ResponseInternalErrorsConfig -> HeaderPrecedence -> + TraceQueryStatus -> m (ExecutionPlan, [QueryRootField UnpreparedValue], DirectiveMap, ParameterizedQueryHash, [ModelInfoPart]) convertQuerySelSet env @@ -112,7 +113,8 @@ convertQuerySelSet reqId maybeOperationName responseErrorsConfig - headerPrecedence = do + headerPrecedence + traceQueryStatus = do -- 1. Parse the GraphQL query into the 'RootFieldMap' and a 'SelectionSet' (unpreparedQueries, normalizedDirectives, normalizedSelectionSet) <- Tracing.newSpan "Parse query IR" $ parseGraphQLQuery nullInNonNullableVariables gqlContext varDefs (GH._grVariables gqlUnparsed) directives fields @@ -142,7 +144,7 @@ convertQuerySelSet queryTagsAttributes = encodeQueryTags $ QTQuery $ QueryMetadata mReqId maybeOperationName rootFieldName parameterizedQueryHash queryTagsComment = Tagged.untag $ createQueryTags @m queryTagsAttributes queryTagsConfig (noRelsDBAST, remoteJoins) = RJ.getRemoteJoinsQueryDB db - (dbStepInfo, dbModelInfoList) <- flip runReaderT queryTagsComment $ mkDBQueryPlan @b userInfo sourceName sourceConfig noRelsDBAST reqHeaders maybeOperationName + (dbStepInfo, dbModelInfoList) <- flip runReaderT queryTagsComment $ mkDBQueryPlan @b userInfo sourceName sourceConfig noRelsDBAST reqHeaders maybeOperationName traceQueryStatus pure $ (ExecStepDB [] (AB.mkAnyBackend dbStepInfo) remoteJoins, dbModelInfoList) RFRemote (RemoteSchemaName rName) rf -> do RemoteSchemaRootField remoteSchemaInfo resultCustomizer remoteField <- runVariableCache $ for rf $ resolveRemoteVariable userInfo diff --git a/server/src-lib/Hasura/GraphQL/Execute/RemoteJoin/Join.hs b/server/src-lib/Hasura/GraphQL/Execute/RemoteJoin/Join.hs index 7c7ac51aac4..a701b5c277d 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/RemoteJoin/Join.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/RemoteJoin/Join.hs @@ -38,7 +38,7 @@ import Hasura.RQL.IR.ModelInformation (ModelInfoPart (..), ModelOperationType (M import Hasura.RQL.Types.Common import Hasura.RemoteSchema.SchemaCache import Hasura.SQL.AnyBackend qualified as AB -import Hasura.Server.Types (MonadGetPolicies, RequestId) +import Hasura.Server.Types (MonadGetPolicies, RequestId, TraceQueryStatus) import Hasura.Services.Network import Hasura.Session import Hasura.Tracing qualified as Tracing @@ -78,8 +78,9 @@ processRemoteJoins :: Maybe RemoteJoins -> GQLReqUnparsed -> Tracing.HttpPropagator -> + TraceQueryStatus -> m (EncJSON, [ModelInfoPart]) -processRemoteJoins requestId logger agentLicenseKey env requestHeaders userInfo lhs maybeJoinTree gqlreq tracesPropagator = +processRemoteJoins requestId logger agentLicenseKey env requestHeaders userInfo lhs maybeJoinTree gqlreq tracesPropagator traceQueryStatus = Tracing.newSpan "Process remote joins" $ forRemoteJoins maybeJoinTree (lhs, []) \joinTree -> do lhsParsed <- JO.eitherDecode (encJToLBS lhs) @@ -93,6 +94,7 @@ processRemoteJoins requestId logger agentLicenseKey env requestHeaders userInfo joinTree requestHeaders (_unOperationName <$> _grOperationName gqlreq) + traceQueryStatus pure $ (encJFromOrderedValue $ runIdentity jsonResult, (modelInfoList)) where -- How to process a source join call over the network. @@ -153,8 +155,9 @@ foldJoinTreeWith :: RemoteJoins -> [HTTP.Header] -> Maybe G.Name -> + TraceQueryStatus -> m (f JO.Value, [ModelInfoPart]) -foldJoinTreeWith callSource callRemoteSchema userInfo lhs joinTree reqHeaders operationName = do +foldJoinTreeWith callSource callRemoteSchema userInfo lhs joinTree reqHeaders operationName traceQueryStatus = do (compositeValue, joins) <- collectJoinArguments (assignJoinIds joinTree) lhs (joinIndices) <- fmap catMaybes $ for joins @@ -167,7 +170,7 @@ foldJoinTreeWith callSource callRemoteSchema userInfo lhs joinTree reqHeaders op let remoteSchemaModel = ModelInfoPart (toTxt $ _vrsdName remoteSchemaInfo) ModelTypeRemoteSchema Nothing Nothing (ModelOperationType G.OperationTypeQuery) pure $ (fmap (childJoinTree,) maybeJoinIndex, Just [remoteSchemaModel]) RemoteJoinSource sourceJoin childJoinTree -> do - maybeJoinIndex <- S.makeSourceJoinCall callSource userInfo sourceJoin _jalFieldName joinArguments reqHeaders operationName + maybeJoinIndex <- S.makeSourceJoinCall callSource userInfo sourceJoin _jalFieldName joinArguments reqHeaders operationName traceQueryStatus pure $ (fmap (childJoinTree,) $ fst <$> maybeJoinIndex, snd <$> maybeJoinIndex) result <- for previousStep $ \((childJoinTree, joinIndex)) -> do forRemoteJoins childJoinTree (joinIndex, []) $ \childRemoteJoins -> do @@ -180,6 +183,7 @@ foldJoinTreeWith callSource callRemoteSchema userInfo lhs joinTree reqHeaders op childRemoteJoins reqHeaders operationName + traceQueryStatus pure $ ((IntMap.fromAscList $ zip (IntMap.keys joinIndex) results), modelInfo) pure $ fmap (\(iMap, newModelInfo) -> (iMap, newModelInfo <> fromMaybe [] modelInfo')) result let (key, (compositeValue')) = unzip (IntMap.toList joinIndices) diff --git a/server/src-lib/Hasura/GraphQL/Execute/RemoteJoin/Source.hs b/server/src-lib/Hasura/GraphQL/Execute/RemoteJoin/Source.hs index c8ab32df6c8..9ac73d3232c 100644 --- a/server/src-lib/Hasura/GraphQL/Execute/RemoteJoin/Source.hs +++ b/server/src-lib/Hasura/GraphQL/Execute/RemoteJoin/Source.hs @@ -70,9 +70,10 @@ makeSourceJoinCall :: IntMap.IntMap JoinArgument -> [HTTP.Header] -> Maybe G.Name -> + TraceQueryStatus -> -- | The resulting join index (see 'buildJoinIndex') if any. m (Maybe (IntMap.IntMap AO.Value, [ModelInfoPart])) -makeSourceJoinCall networkFunction userInfo remoteSourceJoin jaFieldName joinArguments reqHeaders operationName = +makeSourceJoinCall networkFunction userInfo remoteSourceJoin jaFieldName joinArguments reqHeaders operationName traceQueryStatus = Tracing.newSpan ("Remote join to data source " <> sourceName <<> " for field " <>> jaFieldName) do -- step 1: create the SourceJoinCall -- maybeSourceCall <- @@ -80,7 +81,7 @@ makeSourceJoinCall networkFunction userInfo remoteSourceJoin jaFieldName joinArg -- buildSourceJoinCall @b userInfo jaFieldName joinArguments sjc maybeSourceCall <- AB.dispatchAnyBackend @EB.BackendExecute remoteSourceJoin - $ buildSourceJoinCall userInfo jaFieldName joinArguments reqHeaders operationName + $ buildSourceJoinCall userInfo jaFieldName joinArguments reqHeaders operationName traceQueryStatus -- if there actually is a remote call: for maybeSourceCall \(sourceCall, modelInfoList) -> do -- step 2: send this call over the network @@ -115,9 +116,10 @@ buildSourceJoinCall :: IntMap.IntMap JoinArgument -> [HTTP.Header] -> Maybe G.Name -> + TraceQueryStatus -> RemoteSourceJoin b -> m (Maybe (AB.AnyBackend SourceJoinCall, [ModelInfoPart])) -buildSourceJoinCall userInfo jaFieldName joinArguments reqHeaders operationName remoteSourceJoin = do +buildSourceJoinCall userInfo jaFieldName joinArguments reqHeaders operationName traceQueryStatus remoteSourceJoin = do Tracing.newSpan "Resolve execution step for remote join field" do let rows = IntMap.toList joinArguments <&> \(argumentId, argument) -> @@ -142,6 +144,7 @@ buildSourceJoinCall userInfo jaFieldName joinArguments reqHeaders operationName reqHeaders operationName (_rsjStringifyNum remoteSourceJoin) + traceQueryStatus -- This should never fail, as field names in remote relationships are -- validated when building the schema cache. fieldName <- diff --git a/server/src-lib/Hasura/GraphQL/Transport/HTTP.hs b/server/src-lib/Hasura/GraphQL/Transport/HTTP.hs index ceb56803fb5..3870ea7c2ac 100644 --- a/server/src-lib/Hasura/GraphQL/Transport/HTTP.hs +++ b/server/src-lib/Hasura/GraphQL/Transport/HTTP.hs @@ -95,7 +95,7 @@ import Hasura.Server.Prometheus recordGraphqlOperationMetric, ) import Hasura.Server.Telemetry.Counters qualified as Telem -import Hasura.Server.Types (HeaderPrecedence, ModelInfoLogState (..), MonadGetPolicies (..), ReadOnlyMode (..), RemoteSchemaResponsePriority (..), RequestId (..)) +import Hasura.Server.Types (HeaderPrecedence, ModelInfoLogState (..), MonadGetPolicies (..), ReadOnlyMode (..), RemoteSchemaResponsePriority (..), RequestId (..), TraceQueryStatus) import Hasura.Services import Hasura.Session (SessionVariable, SessionVariableValue, SessionVariables, UserInfo (..), filterSessionVariables) import Hasura.Tracing (MonadTrace, attachMetadata) @@ -320,6 +320,7 @@ runGQ :: ReadOnlyMode -> RemoteSchemaResponsePriority -> HeaderPrecedence -> + TraceQueryStatus -> PrometheusMetrics -> L.Logger L.Hasura -> Maybe (CredentialCache AgentLicenseKey) -> @@ -331,7 +332,7 @@ runGQ :: GQLReqUnparsed -> ResponseInternalErrorsConfig -> m (GQLQueryOperationSuccessLog, HttpResponse (Maybe GQResponse, EncJSON)) -runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence prometheusMetrics logger agentLicenseKey reqId userInfo ipAddress reqHeaders queryType reqUnparsed responseErrorsConfig = do +runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence traceQueryStatus prometheusMetrics logger agentLicenseKey reqId userInfo ipAddress reqHeaders queryType reqUnparsed responseErrorsConfig = do granularPrometheusMetricsState <- runGetPrometheusMetricsGranularity getModelInfoLogStatus' <- runGetModelInfoLogStatus modelInfoLogStatus <- liftIO getModelInfoLogStatus' @@ -376,6 +377,7 @@ runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority header reqId responseErrorsConfig headerPrecedence + traceQueryStatus -- 4. Execute the execution plan producing a 'AnnotatedResponse'. (response, queryCachedStatus, modelInfoFromExecution) <- executePlan reqParsed runLimits execPlan @@ -512,7 +514,7 @@ runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority header \(EB.DBStepInfo _ sourceConfig genSql tx resolvedConnectionTemplate :: EB.DBStepInfo b) -> runDBQuery @b reqId reqUnparsed fieldName userInfo logger agentLicenseKey sourceConfig (fmap (statsToAnyBackend @b) tx) genSql resolvedConnectionTemplate (finalResponse, modelInfo) <- - RJ.processRemoteJoins reqId logger agentLicenseKey env reqHeaders userInfo resp remoteJoins reqUnparsed tracesPropagator + RJ.processRemoteJoins reqId logger agentLicenseKey env reqHeaders userInfo resp remoteJoins reqUnparsed tracesPropagator traceQueryStatus pure $ (AnnotatedResponsePart telemTimeIO_DT Telem.Local finalResponse [], modelInfo) E.ExecStepRemote rsi resultCustomizer gqlReq remoteJoins -> do logQueryLog logger $ QueryLog reqUnparsed Nothing reqId QueryLogKindRemoteSchema @@ -522,7 +524,7 @@ runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority header (time, resp, modelInfo) <- doQErr $ do (time, (resp, _)) <- EA.runActionExecution userInfo aep (finalResponse, modelInfo) <- - RJ.processRemoteJoins reqId logger agentLicenseKey env reqHeaders userInfo resp remoteJoins reqUnparsed tracesPropagator + RJ.processRemoteJoins reqId logger agentLicenseKey env reqHeaders userInfo resp remoteJoins reqUnparsed tracesPropagator traceQueryStatus pure (time, finalResponse, modelInfo) pure $ (AnnotatedResponsePart time Telem.Empty resp [], modelInfo) E.ExecStepRaw json -> do @@ -546,7 +548,7 @@ runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority header \(EB.DBStepInfo _ sourceConfig genSql tx resolvedConnectionTemplate :: EB.DBStepInfo b) -> runDBMutation @b reqId reqUnparsed fieldName userInfo logger agentLicenseKey sourceConfig (fmap EB.arResult tx) genSql resolvedConnectionTemplate (finalResponse, modelInfo) <- - RJ.processRemoteJoins reqId logger agentLicenseKey env reqHeaders userInfo resp remoteJoins reqUnparsed tracesPropagator + RJ.processRemoteJoins reqId logger agentLicenseKey env reqHeaders userInfo resp remoteJoins reqUnparsed tracesPropagator traceQueryStatus pure $ (AnnotatedResponsePart telemTimeIO_DT Telem.Local finalResponse responseHeaders, modelInfo) E.ExecStepRemote rsi resultCustomizer gqlReq remoteJoins -> do logQueryLog logger $ QueryLog reqUnparsed Nothing reqId QueryLogKindRemoteSchema @@ -556,7 +558,7 @@ runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority header (time, (resp, hdrs), modelInfo) <- doQErr $ do (time, (resp, hdrs)) <- EA.runActionExecution userInfo aep (finalResponse, modelInfo) <- - RJ.processRemoteJoins reqId logger agentLicenseKey env reqHeaders userInfo resp remoteJoins reqUnparsed tracesPropagator + RJ.processRemoteJoins reqId logger agentLicenseKey env reqHeaders userInfo resp remoteJoins reqUnparsed tracesPropagator traceQueryStatus pure (time, (finalResponse, hdrs), modelInfo) pure $ (AnnotatedResponsePart time Telem.Empty resp $ fromMaybe [] hdrs, modelInfo) E.ExecStepRaw json -> do @@ -586,6 +588,7 @@ runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority header remoteJoins reqUnparsed tracesPropagator + traceQueryStatus let filteredHeaders = filter ((== "Set-Cookie") . fst) remoteResponseHeaders pure $ (AnnotatedResponsePart telemTimeIO_DT Telem.Remote finalResponse filteredHeaders, modelInfo) @@ -809,6 +812,7 @@ runGQBatched :: ResponseInternalErrorsConfig -> RemoteSchemaResponsePriority -> HeaderPrecedence -> + TraceQueryStatus -> UserInfo -> Wai.IpAddress -> [HTTP.Header] -> @@ -816,10 +820,10 @@ runGQBatched :: -- | the batched request with unparsed GraphQL query GQLBatchedReqs (GQLReq GQLQueryText) -> m (HttpLogGraphQLInfo, HttpResponse EncJSON) -runGQBatched env sqlGenCtx sc enableAL readOnlyMode prometheusMetrics logger agentLicenseKey reqId responseErrorsConfig remoteSchemaResponsePriority headerPrecedence userInfo ipAddress reqHdrs queryType query = +runGQBatched env sqlGenCtx sc enableAL readOnlyMode prometheusMetrics logger agentLicenseKey reqId responseErrorsConfig remoteSchemaResponsePriority headerPrecedence traceQueryStatus userInfo ipAddress reqHdrs queryType query = case query of GQLSingleRequest req -> do - (gqlQueryOperationLog, httpResp) <- runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence prometheusMetrics logger agentLicenseKey reqId userInfo ipAddress reqHdrs queryType req responseErrorsConfig + (gqlQueryOperationLog, httpResp) <- runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence traceQueryStatus prometheusMetrics logger agentLicenseKey reqId userInfo ipAddress reqHdrs queryType req responseErrorsConfig let httpLoggingGQInfo = (CommonHttpLogMetadata L.RequestModeSingle (Just (GQLSingleRequest (GQLQueryOperationSuccess gqlQueryOperationLog))), (PQHSetSingleton (gqolParameterizedQueryHash gqlQueryOperationLog))) pure (httpLoggingGQInfo, snd <$> httpResp) GQLBatchedReqs reqs -> do @@ -832,7 +836,7 @@ runGQBatched env sqlGenCtx sc enableAL readOnlyMode prometheusMetrics logger age flip HttpResponse [] . encJFromList . map (either (encJFromJEncoding . encodeGQErr includeInternal) _hrBody) - responses <- for reqs \req -> fmap (req,) $ try $ (fmap . fmap . fmap) snd $ runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence prometheusMetrics logger agentLicenseKey reqId userInfo ipAddress reqHdrs queryType req responseErrorsConfig + responses <- for reqs \req -> fmap (req,) $ try $ (fmap . fmap . fmap) snd $ runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence traceQueryStatus prometheusMetrics logger agentLicenseKey reqId userInfo ipAddress reqHdrs queryType req responseErrorsConfig let requestsOperationLogs = map fst $ rights $ map snd responses batchOperationLogs = map diff --git a/server/src-lib/Hasura/GraphQL/Transport/WSServerApp.hs b/server/src-lib/Hasura/GraphQL/Transport/WSServerApp.hs index f5dba51d638..dc05cc5513e 100644 --- a/server/src-lib/Hasura/GraphQL/Transport/WSServerApp.hs +++ b/server/src-lib/Hasura/GraphQL/Transport/WSServerApp.hs @@ -98,9 +98,10 @@ createWSServerApp enabledLogTypes serverEnv connInitTimeout licenseKeyCache = \ onMessageHandler conn bs sp = do headerPrecedence <- liftIO $ acHeaderPrecedence <$> getAppContext (_wseAppStateRef serverEnv) + traceQueryStatus <- liftIO $ acTraceQueryStatus <$> getAppContext (_wseAppStateRef serverEnv) responseErrorsConfig <- liftIO $ acResponseInternalErrorsConfig <$> getAppContext (_wseAppStateRef serverEnv) mask_ - $ onMessage enabledLogTypes getAuthMode serverEnv conn bs (wsActions sp) licenseKeyCache responseErrorsConfig headerPrecedence + $ onMessage enabledLogTypes getAuthMode serverEnv conn bs (wsActions sp) licenseKeyCache responseErrorsConfig headerPrecedence traceQueryStatus onCloseHandler conn = mask_ do granularPrometheusMetricsState <- runGetPrometheusMetricsGranularity diff --git a/server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs b/server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs index c5ac0300107..22682232fc1 100644 --- a/server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs +++ b/server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs @@ -105,7 +105,7 @@ import Hasura.Server.Prometheus recordGraphqlOperationMetric, ) import Hasura.Server.Telemetry.Counters qualified as Telem -import Hasura.Server.Types (GranularPrometheusMetricsState (..), HeaderPrecedence, ModelInfoLogState (..), MonadGetPolicies (..), RemoteSchemaResponsePriority, RequestId, getRequestId) +import Hasura.Server.Types (GranularPrometheusMetricsState (..), HeaderPrecedence, ModelInfoLogState (..), MonadGetPolicies (..), RemoteSchemaResponsePriority, RequestId, TraceQueryStatus, getRequestId) import Hasura.Services.Network import Hasura.Session import Hasura.Tracing qualified as Tracing @@ -449,8 +449,9 @@ onStart :: WS.WSActions WSConnData -> ResponseInternalErrorsConfig -> HeaderPrecedence -> + TraceQueryStatus -> m () -onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables (StartMsg opId q) onMessageActions responseErrorsConfig headerPrecedence = catchAndIgnore $ do +onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables (StartMsg opId q) onMessageActions responseErrorsConfig headerPrecedence traceQueryStatus = catchAndIgnore $ do modelInfoLogStatus' <- runGetModelInfoLogStatus modelInfoLogStatus <- liftIO modelInfoLogStatus' granularPrometheusMetricsState <- runGetPrometheusMetricsGranularity @@ -521,6 +522,7 @@ onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables requestId responseErrorsConfig headerPrecedence + traceQueryStatus (parameterizedQueryHash, execPlan, modelInfoList) <- onLeft execPlanE (withComplete . preExecErr granularPrometheusMetricsState requestId (Just gqlOpType) opName Nothing) @@ -564,7 +566,7 @@ onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables genSql resolvedConnectionTemplate (finalResponse, modelInfo) <- - RJ.processRemoteJoins requestId logger agentLicenseKey env reqHdrs userInfo resp remoteJoins q tracesPropagator + RJ.processRemoteJoins requestId logger agentLicenseKey env reqHdrs userInfo resp remoteJoins q tracesPropagator traceQueryStatus pure $ (AnnotatedResponsePart telemTimeIO_DT Telem.Local finalResponse [], modelInfo) E.ExecStepRemote rsi resultCustomizer gqlReq remoteJoins -> do logQueryLog logger $ QueryLog q Nothing requestId QueryLogKindRemoteSchema @@ -574,7 +576,7 @@ onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables (time, (resp, _), modelInfo) <- doQErr $ do (time, (resp, hdrs)) <- EA.runActionExecution userInfo actionExecPlan (finalResponse, modelInfo) <- - RJ.processRemoteJoins requestId logger agentLicenseKey env reqHdrs userInfo resp remoteJoins q tracesPropagator + RJ.processRemoteJoins requestId logger agentLicenseKey env reqHdrs userInfo resp remoteJoins q tracesPropagator traceQueryStatus pure (time, (finalResponse, hdrs), modelInfo) pure $ (AnnotatedResponsePart time Telem.Empty resp [], modelInfo) E.ExecStepRaw json -> do @@ -648,14 +650,14 @@ onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables genSql resolvedConnectionTemplate (finalResponse, modelInfo) <- - RJ.processRemoteJoins requestId logger agentLicenseKey env reqHdrs userInfo resp remoteJoins q tracesPropagator + RJ.processRemoteJoins requestId logger agentLicenseKey env reqHdrs userInfo resp remoteJoins q tracesPropagator traceQueryStatus pure $ (AnnotatedResponsePart telemTimeIO_DT Telem.Local finalResponse [], modelInfo) E.ExecStepAction actionExecPlan _ remoteJoins -> do logQueryLog logger $ QueryLog q Nothing requestId QueryLogKindAction (time, (resp, hdrs), modelInfo) <- doQErr $ do (time, (resp, hdrs)) <- EA.runActionExecution userInfo actionExecPlan (finalResponse, modelInfo) <- - RJ.processRemoteJoins requestId logger agentLicenseKey env reqHdrs userInfo resp remoteJoins q tracesPropagator + RJ.processRemoteJoins requestId logger agentLicenseKey env reqHdrs userInfo resp remoteJoins q tracesPropagator traceQueryStatus pure (time, (finalResponse, hdrs), modelInfo) pure $ (AnnotatedResponsePart time Telem.Empty resp $ fromMaybe [] hdrs, modelInfo) E.ExecStepRemote rsi resultCustomizer gqlReq remoteJoins -> do @@ -859,6 +861,7 @@ onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables remoteJoins reqUnparsed tracesPropagator + traceQueryStatus return $ (AnnotatedResponsePart telemTimeIO_DT Telem.Remote finalResponse [], modelInfo) WSServerEnv @@ -1113,8 +1116,9 @@ onMessage :: Maybe (CredentialCache AgentLicenseKey) -> ResponseInternalErrorsConfig -> HeaderPrecedence -> + TraceQueryStatus -> m () -onMessage enabledLogTypes authMode serverEnv wsConn msgRaw onMessageActions agentLicenseKey responseErrorsConfig headerPrecedence = do +onMessage enabledLogTypes authMode serverEnv wsConn msgRaw onMessageActions agentLicenseKey responseErrorsConfig headerPrecedence traceQueryStatus = do Tracing.newTrace (_wseTraceSamplingPolicy serverEnv) "websocket" do case J.eitherDecode msgRaw of Left e -> do @@ -1138,7 +1142,7 @@ onMessage enabledLogTypes authMode serverEnv wsConn msgRaw onMessageActions agen if _mcAnalyzeQueryVariables (scMetricsConfig schemaCache) then CaptureQueryVariables else DoNotCaptureQueryVariables - onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables startMsg onMessageActions responseErrorsConfig headerPrecedence + onStart enabledLogTypes agentLicenseKey serverEnv wsConn shouldCaptureVariables startMsg onMessageActions responseErrorsConfig headerPrecedence traceQueryStatus CMStop stopMsg -> do granularPrometheusMetricsState <- runGetPrometheusMetricsGranularity onStop serverEnv wsConn stopMsg granularPrometheusMetricsState diff --git a/server/src-lib/Hasura/Server/App.hs b/server/src-lib/Hasura/Server/App.hs index 3b46e900631..7a4d7ae0ab6 100644 --- a/server/src-lib/Hasura/Server/App.hs +++ b/server/src-lib/Hasura/Server/App.hs @@ -591,7 +591,7 @@ v1Alpha1GQHandler queryType query = do reqHeaders <- asks hcReqHeaders ipAddress <- asks hcSourceIpAddress requestId <- asks hcRequestId - GH.runGQBatched acEnvironment acSQLGenCtx schemaCache acEnableAllowlist appEnvEnableReadOnlyMode appEnvPrometheusMetrics (_lsLogger appEnvLoggers) appEnvLicenseKeyCache requestId acResponseInternalErrorsConfig acRemoteSchemaResponsePriority acHeaderPrecedence userInfo ipAddress reqHeaders queryType query + GH.runGQBatched acEnvironment acSQLGenCtx schemaCache acEnableAllowlist appEnvEnableReadOnlyMode appEnvPrometheusMetrics (_lsLogger appEnvLoggers) appEnvLicenseKeyCache requestId acResponseInternalErrorsConfig acRemoteSchemaResponsePriority acHeaderPrecedence acTraceQueryStatus userInfo ipAddress reqHeaders queryType query v1GQHandler :: ( MonadIO m, @@ -960,7 +960,7 @@ httpApp setupHook appStateRef AppEnv {..} consoleType ekgStore closeWebsocketsOn Spock.PATCH -> pure EP.PATCH other -> throw400 BadRequest $ "Method " <> tshow other <> " not supported." _ -> throw400 BadRequest $ "Nonstandard method not allowed for REST endpoints" - fmap JSONResp <$> runCustomEndpoint acEnvironment acSQLGenCtx schemaCache acEnableAllowlist appEnvEnableReadOnlyMode acRemoteSchemaResponsePriority acHeaderPrecedence appEnvPrometheusMetrics (_lsLogger appEnvLoggers) appEnvLicenseKeyCache requestId userInfo reqHeaders ipAddress req endpoints responseErrorsConfig + fmap JSONResp <$> runCustomEndpoint acEnvironment acSQLGenCtx schemaCache acEnableAllowlist appEnvEnableReadOnlyMode acRemoteSchemaResponsePriority acHeaderPrecedence acTraceQueryStatus appEnvPrometheusMetrics (_lsLogger appEnvLoggers) appEnvLicenseKeyCache requestId userInfo reqHeaders ipAddress req endpoints responseErrorsConfig -- See Issue #291 for discussion around restified feature Spock.hookRouteAll ("api" "rest" Spock.wildcard) $ \wildcard -> do diff --git a/server/src-lib/Hasura/Server/Init.hs b/server/src-lib/Hasura/Server/Init.hs index d685e9d48af..2bdb7de6850 100644 --- a/server/src-lib/Hasura/Server/Init.hs +++ b/server/src-lib/Hasura/Server/Init.hs @@ -225,6 +225,7 @@ mkServeOptions sor@ServeOptionsRaw {..} = do soPersistedQueriesTtl <- withOptionDefault rsoPersistedQueriesTtl persistedQueriesTtlOption soRemoteSchemaResponsePriority <- withOptionDefault rsoRemoteSchemaResponsePriority remoteSchemaResponsePriorityOption soHeaderPrecedence <- withOptionDefault rsoHeaderPrecedence configuredHeaderPrecedenceOption + soTraceQueryStatus <- withOptionDefault rsoTraceQueryStatus traceQueryStatusOption pure ServeOptions {..} -- | Fetch Postgres 'Query.ConnParams' components from the environment diff --git a/server/src-lib/Hasura/Server/Init/Arg/Command/Serve.hs b/server/src-lib/Hasura/Server/Init/Arg/Command/Serve.hs index 828812c3ef9..54616eb6ddd 100644 --- a/server/src-lib/Hasura/Server/Init/Arg/Command/Serve.hs +++ b/server/src-lib/Hasura/Server/Init/Arg/Command/Serve.hs @@ -71,6 +71,7 @@ module Hasura.Server.Init.Arg.Command.Serve persistedQueriesTtlOption, remoteSchemaResponsePriorityOption, configuredHeaderPrecedenceOption, + traceQueryStatusOption, -- * Pretty Printer serveCmdFooter, @@ -167,6 +168,7 @@ serveCommandParser = <*> parsePersistedQueriesTtl <*> parseRemoteSchemaResponsePriority <*> parseConfiguredHeaderPrecedence + <*> parseTraceQueryStatus -------------------------------------------------------------------------------- -- Serve Options @@ -1341,6 +1343,14 @@ parseConfiguredHeaderPrecedence = <> Opt.help (Config._helpMessage configuredHeaderPrecedenceOption) ) +parseTraceQueryStatus :: Opt.Parser (Maybe Types.TraceQueryStatus) +parseTraceQueryStatus = + (bool Nothing (Just Types.TraceQueryEnabled)) + <$> Opt.switch + ( Opt.long "trace-sql-query" + <> Opt.help (Config._helpMessage traceQueryStatusOption) + ) + configuredHeaderPrecedenceOption :: Config.Option Types.HeaderPrecedence configuredHeaderPrecedenceOption = Config.Option @@ -1351,6 +1361,15 @@ configuredHeaderPrecedenceOption = <> "when delivering payload to webhook for actions and input validations. (default: false)" } +traceQueryStatusOption :: Config.Option Types.TraceQueryStatus +traceQueryStatusOption = + Config.Option + { Config._default = Types.TraceQueryDisabled, + Config._envVar = "HASURA_GRAPHQL_ENABLE_QUERY_TRACING", + Config._helpMessage = + "Enable query tracing for all queries. (default: false)" + } + -------------------------------------------------------------------------------- -- Pretty Printer diff --git a/server/src-lib/Hasura/Server/Init/Config.hs b/server/src-lib/Hasura/Server/Init/Config.hs index 949608180c1..866cf77b863 100644 --- a/server/src-lib/Hasura/Server/Init/Config.hs +++ b/server/src-lib/Hasura/Server/Init/Config.hs @@ -332,7 +332,8 @@ data ServeOptionsRaw impl = ServeOptionsRaw rsoPersistedQueries :: Maybe Server.Types.PersistedQueriesState, rsoPersistedQueriesTtl :: Maybe Int, rsoRemoteSchemaResponsePriority :: Maybe Server.Types.RemoteSchemaResponsePriority, - rsoHeaderPrecedence :: Maybe Server.Types.HeaderPrecedence + rsoHeaderPrecedence :: Maybe Server.Types.HeaderPrecedence, + rsoTraceQueryStatus :: Maybe Server.Types.TraceQueryStatus } deriving stock instance (Show (Logging.EngineLogType impl)) => Show (ServeOptionsRaw impl) @@ -643,7 +644,8 @@ data ServeOptions impl = ServeOptions soPersistedQueries :: Server.Types.PersistedQueriesState, soPersistedQueriesTtl :: Int, soRemoteSchemaResponsePriority :: Server.Types.RemoteSchemaResponsePriority, - soHeaderPrecedence :: Server.Types.HeaderPrecedence + soHeaderPrecedence :: Server.Types.HeaderPrecedence, + soTraceQueryStatus :: Server.Types.TraceQueryStatus } -- | 'ResponseInternalErrorsConfig' represents the encoding of the diff --git a/server/src-lib/Hasura/Server/Init/Env.hs b/server/src-lib/Hasura/Server/Init/Env.hs index ad499cbe5a5..5288463c1e4 100644 --- a/server/src-lib/Hasura/Server/Init/Env.hs +++ b/server/src-lib/Hasura/Server/Init/Env.hs @@ -393,3 +393,6 @@ instance FromEnv Server.Types.RemoteSchemaResponsePriority where instance FromEnv Server.Types.HeaderPrecedence where fromEnv = fmap (bool Server.Types.ClientHeadersFirst Server.Types.ConfiguredHeadersFirst) . fromEnv @Bool + +instance FromEnv Server.Types.TraceQueryStatus where + fromEnv = fmap (bool Server.Types.TraceQueryDisabled Server.Types.TraceQueryEnabled) . fromEnv @Bool diff --git a/server/src-lib/Hasura/Server/Rest.hs b/server/src-lib/Hasura/Server/Rest.hs index 112eb0138a7..dbd444e1e19 100644 --- a/server/src-lib/Hasura/Server/Rest.hs +++ b/server/src-lib/Hasura/Server/Rest.hs @@ -120,6 +120,7 @@ runCustomEndpoint :: ReadOnlyMode -> RemoteSchemaResponsePriority -> HeaderPrecedence -> + TraceQueryStatus -> PrometheusMetrics -> L.Logger L.Hasura -> Maybe (CredentialCache AgentLicenseKey) -> @@ -131,7 +132,7 @@ runCustomEndpoint :: EndpointTrie GQLQueryWithText -> Init.ResponseInternalErrorsConfig -> m (HttpLogGraphQLInfo, HttpResponse EncJSON) -runCustomEndpoint env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence prometheusMetrics logger agentLicenseKey requestId userInfo reqHeaders ipAddress RestRequest {..} endpoints responseErrorsConfig = do +runCustomEndpoint env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence traceQueryStatus prometheusMetrics logger agentLicenseKey requestId userInfo reqHeaders ipAddress RestRequest {..} endpoints responseErrorsConfig = do -- First match the path to an endpoint. case matchPath reqMethod (T.split (== '/') reqPath) endpoints of MatchFound (queryx :: EndpointMetadata GQLQueryWithText) matches -> @@ -161,7 +162,7 @@ runCustomEndpoint env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePri -- with the query string from the schema cache, and pass it -- through to the /v1/graphql endpoint. (httpLoggingMetadata, handlerResp) <- do - (gqlOperationLog, resp) <- GH.runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence prometheusMetrics logger agentLicenseKey requestId userInfo ipAddress reqHeaders E.QueryHasura (mkPassthroughRequest queryx resolvedVariables) responseErrorsConfig + (gqlOperationLog, resp) <- GH.runGQ env sqlGenCtx sc enableAL readOnlyMode remoteSchemaResponsePriority headerPrecedence traceQueryStatus prometheusMetrics logger agentLicenseKey requestId userInfo ipAddress reqHeaders E.QueryHasura (mkPassthroughRequest queryx resolvedVariables) responseErrorsConfig let httpLoggingGQInfo = (CommonHttpLogMetadata RequestModeNonBatchable Nothing, (PQHSetSingleton (gqolParameterizedQueryHash gqlOperationLog))) return (httpLoggingGQInfo, fst <$> resp) case sequence handlerResp of diff --git a/server/src-lib/Hasura/Server/Types.hs b/server/src-lib/Hasura/Server/Types.hs index abbe7b9d68d..9c28920b579 100644 --- a/server/src-lib/Hasura/Server/Types.hs +++ b/server/src-lib/Hasura/Server/Types.hs @@ -33,6 +33,7 @@ module Hasura.Server.Types MonadGetPolicies (..), RemoteSchemaResponsePriority (..), HeaderPrecedence (..), + TraceQueryStatus (..), ) where @@ -398,3 +399,18 @@ instance ToJSON HeaderPrecedence where toJSON = \case ConfiguredHeadersFirst -> Bool True ClientHeadersFirst -> Bool False + +data TraceQueryStatus + = TraceQueryEnabled + | TraceQueryDisabled + deriving (Eq, Show, Generic) + +instance FromJSON TraceQueryStatus where + parseJSON = withBool "TraceQueryStatus" $ \case + False -> pure TraceQueryDisabled + True -> pure TraceQueryEnabled + +instance ToJSON TraceQueryStatus where + toJSON = \case + TraceQueryDisabled -> Bool False + TraceQueryEnabled -> Bool True diff --git a/server/src-test/Hasura/Server/InitSpec.hs b/server/src-test/Hasura/Server/InitSpec.hs index 8601391ad7a..180c6dccf00 100644 --- a/server/src-test/Hasura/Server/InitSpec.hs +++ b/server/src-test/Hasura/Server/InitSpec.hs @@ -101,7 +101,8 @@ emptyServeOptionsRaw = rsoPersistedQueries = Nothing, rsoPersistedQueriesTtl = Nothing, rsoRemoteSchemaResponsePriority = Nothing, - rsoHeaderPrecedence = Nothing + rsoHeaderPrecedence = Nothing, + rsoTraceQueryStatus = Nothing } mkServeOptionsSpec :: Hspec.Spec diff --git a/server/test-postgres/Constants.hs b/server/test-postgres/Constants.hs index 3ccf6f81909..e5dfd519be5 100644 --- a/server/test-postgres/Constants.hs +++ b/server/test-postgres/Constants.hs @@ -100,7 +100,8 @@ serveOptions = soPersistedQueries = Init._default Init.persistedQueriesOption, soPersistedQueriesTtl = Init._default Init.persistedQueriesTtlOption, soRemoteSchemaResponsePriority = Init._default Init.remoteSchemaResponsePriorityOption, - soHeaderPrecedence = Init._default Init.configuredHeaderPrecedenceOption + soHeaderPrecedence = Init._default Init.configuredHeaderPrecedenceOption, + soTraceQueryStatus = Init._default Init.traceQueryStatusOption } -- | What log level should be used by the engine; this is not exported, and