原文:http://blog.mygraphql.com/wordpress/?p=112
攔截器Instrumentation
通過實現(xiàn) graphql.execution.instrumentation.Instrumentation
接口,你可以在執(zhí)行查詢的過程中注入定制代碼。并可以修改運行期的行為。
它的主要用途是性能監(jiān)控和定制日志,但也可以完成其它任務(wù)。
當(dāng)創(chuàng)建 `Graphql 對象時,可以綁定相關(guān)的 Instrumentation 。
GraphQL.newGraphQL(schema)
.instrumentation(new TracingInstrumentation())
.build();
定制攔截器(Custom Instrumentation)
要實現(xiàn) Instrumentation ,需要實現(xiàn)多個 “begin”
開頭的方法。這方法會在查詢執(zhí)行過程中,每一步驟開始前被調(diào)用。
所有回調(diào)方法,都應(yīng)該返回
graphql.execution.instrumentation.InstrumentationContext
對象,這個對象會在本步驟完成時被回調(diào)用,回調(diào)用時會告知數(shù)據(jù)的獲取結(jié)果,如果出錯,可以獲取
Throwable 對象。.
下面是一個定制的 Instrumentation 。作用是測量執(zhí)行時間。
class CustomInstrumentationState implements InstrumentationState {
private Map<String, Object> anyStateYouLike = new HashMap<>();
void recordTiming(String key, long time) {
anyStateYouLike.put(key, time);
}
}
class CustomInstrumentation implements Instrumentation {
@Override
public InstrumentationState createState() {
//
// instrumentation state is passed during each invocation of an Instrumentation method
// and allows you to put stateful data away and reference it during the query execution
//
return new CustomInstrumentationState();
}
@Override
public InstrumentationContext<ExecutionResult> beginExecution(InstrumentationExecutionParameters parameters) {
long startNanos = System.nanoTime();
return (result, throwable) -> {
CustomInstrumentationState state = parameters.getInstrumentationState();
state.recordTiming(parameters.getQuery(), System.nanoTime() - startNanos);
};
}
@Override
public InstrumentationContext<Document> beginParse(InstrumentationExecutionParameters parameters) {
//
// You MUST return a non null object but it does not have to do anything and hence
// you use this class to return a no-op object
//
return new NoOpInstrumentation.NoOpInstrumentationContext<>();
}
@Override
public InstrumentationContext<List<ValidationError>> beginValidation(InstrumentationValidationParameters parameters) {
return new NoOpInstrumentation.NoOpInstrumentationContext<>();
}
@Override
public InstrumentationContext<ExecutionResult> beginDataFetch(InstrumentationDataFetchParameters parameters) {
return new NoOpInstrumentation.NoOpInstrumentationContext<>();
}
@Override
public InstrumentationContext<CompletableFuture<ExecutionResult>> beginExecutionStrategy(InstrumentationExecutionStrategyParameters parameters) {
return new NoOpInstrumentation.NoOpInstrumentationContext<>();
}
@Override
public InstrumentationContext<ExecutionResult> beginField(InstrumentationFieldParameters parameters) {
return new NoOpInstrumentation.NoOpInstrumentationContext<>();
}
@Override
public InstrumentationContext<Object> beginFieldFetch(InstrumentationFieldFetchParameters parameters) {
return new NoOpInstrumentation.NoOpInstrumentationContext<>();
}
@Override
public DataFetcher<?> instrumentDataFetcher(DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
//
// this allows you to intercept the data fetcher used ot fetch a field and provide another one, perhaps
// that enforces certain behaviours or has certain side effects on the data
//
return dataFetcher;
}
@Override
public CompletableFuture<ExecutionResult> instrumentExecutionResult(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) {
//
// this allows you to instrument the execution result some how. For example the Tracing support uses this to put
// the `extensions` map of data in place
//
return CompletableFuture.completedFuture(executionResult);
}
}
鏈式攔截(Chaining Instrumentation)
你可以用 graphql.execution.instrumentation.ChainedInstrumentation
把多個 Instrumentation 連接起來。這些 Instrumentation
對象會按順序被調(diào)用。
List<Instrumentation> chainedList = new ArrayList<>();
chainedList.add(new FooInstrumentation());
chainedList.add(new BarInstrumentation());
ChainedInstrumentation chainedInstrumentation = new ChainedInstrumentation(chainedList);
GraphQL.newGraphQL(schema)
.instrumentation(chainedInstrumentation)
.build();
Apollo跟蹤與攔截( Tracing Instrumentation)
graphql.execution.instrumentation.tracing.TracingInstrumentation
是一個可以收集跟蹤信息的攔截器。
它按照 Apollo 跟蹤格式 https://github.com/apollographql/apollo-tracing
來收集跟蹤信息。
詳細的跟蹤信息( tracing map)會放在查詢結(jié)果的 extensions(擴展)
部分。
如以下的查詢:
query {
hero {
name
friends {
name
}
}
}
會返回如下的結(jié)果:
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
},
"extensions": {
"tracing": {
"version": 1,
"startTime": "2017-08-14T23:13:39.362Z",
"endTime": "2017-08-14T23:13:39.497Z",
"duration": 135589186,
"execution": {
"resolvers": [
{
"path": [
"hero"
],
"parentType": "Query",
"returnType": "Character",
"fieldName": "hero",
"startOffset": 105697585,
"duration": 79111240
},
{
"path": [
"hero",
"name"
],
"parentType": "Droid",
"returnType": "String",
"fieldName": "name",
"startOffset": 125010028,
"duration": 20213
},
{
"path": [
"hero",
"friends"
],
"parentType": "Droid",
"returnType": "[Character]",
"fieldName": "friends",
"startOffset": 133352819,
"duration": 7927560
},
{
"path": [
"hero",
"friends",
0,
"name"
],
"parentType": "Human",
"returnType": "String",
"fieldName": "name",
"startOffset": 134105887,
"duration": 6783
},
{
"path": [
"hero",
"friends",
1,
"name"
],
"parentType": "Human",
"returnType": "String",
"fieldName": "name",
"startOffset": 134725922,
"duration": 7016
},
{
"path": [
"hero",
"friends",
2,
"name"
],
"parentType": "Human",
"returnType": "String",
"fieldName": "name",
"startOffset": 134875089,
"duration": 6342
}
]
}
}
}
}
字段校驗攔截器(Field Validation Instrumentation)
graphql.execution.instrumentation.fieldvalidation.FieldValidationInstrumentation
攔截器,可以在執(zhí)行查詢前校驗字段和字段參數(shù)。如果校驗失敗,查詢將停止,并返回錯誤信息。
你可以編寫自己的FieldValidation 實現(xiàn),或者直接用
SimpleFieldValidation 去為每個field定義校驗邏輯。
ExecutionPath fieldPath = ExecutionPath.parse("/user");
FieldValidation fieldValidation = new SimpleFieldValidation()
.addRule(fieldPath, new BiFunction<FieldAndArguments, FieldValidationEnvironment, Optional<GraphQLError>>() {
@Override
public Optional<GraphQLError> apply(FieldAndArguments fieldAndArguments, FieldValidationEnvironment environment) {
String nameArg = fieldAndArguments.getFieldArgument("name");
if (nameArg.length() > 255) {
return Optional.of(environment.mkError("Invalid user name", fieldAndArguments));
}
return Optional.empty();
}
});
FieldValidationInstrumentation instrumentation = new FieldValidationInstrumentation(
fieldValidation
);
GraphQL.newGraphQL(schema)
.instrumentation(instrumentation)
.build();