1+ package com .oembedler .moon .graphql .boot .metrics ;
2+
3+ import graphql .ExecutionResult ;
4+ import graphql .execution .instrumentation .parameters .InstrumentationExecutionParameters ;
5+ import graphql .execution .instrumentation .tracing .TracingInstrumentation ;
6+ import io .micrometer .core .instrument .MeterRegistry ;
7+ import io .micrometer .core .instrument .Timer ;
8+
9+ import java .util .List ;
10+ import java .util .Map ;
11+ import java .util .concurrent .CompletableFuture ;
12+ import java .util .concurrent .TimeUnit ;
13+
14+ /**
15+ * @author Bruno Rodrigues
16+ */
17+ public class MetricsInstrumentation extends TracingInstrumentation {
18+
19+ private MeterRegistry meterRegistry ;
20+
21+ private static final String QUERY_TIME_METRIC_NAME = "graphql.timer.query" ;
22+ private static final String RESOLVER_TIME_METRIC_NAME = "graphql.timer.resolver" ;
23+ private static final String OPERATION_NAME_TAG = "operationName" ;
24+ private static final String OPERATION = "operation" ;
25+ private static final String UNKNOWN_OPERATION_NAME = "unknown" ;
26+ private static final String PARENT = "parent" ;
27+ private static final String FIELD = "field" ;
28+ private static final String TIMER_DESCRIPTION = "Timer that records the time to fetch the data by Operation Name" ;
29+
30+ private Boolean tracingEnabled ;
31+
32+ public MetricsInstrumentation (MeterRegistry meterRegistry , Boolean tracingEnabled ) {
33+ this .meterRegistry = meterRegistry ;
34+ this .tracingEnabled = tracingEnabled ;
35+ }
36+
37+ @ Override
38+ public CompletableFuture <ExecutionResult > instrumentExecutionResult (ExecutionResult executionResult , InstrumentationExecutionParameters parameters ) {
39+
40+ if (executionResult .getExtensions () != null && executionResult .getExtensions ().containsKey ("tracing" )) {
41+
42+ Map <String , Object > tracingData = (Map <String , Object >) executionResult .getExtensions ().get ("tracing" );
43+ Timer executionTimer = buildQueryTimer (parameters .getOperation (), "execution" );
44+ executionTimer .record ((long ) tracingData .get ("duration" ), TimeUnit .NANOSECONDS );
45+
46+ //These next 2 ifs might not run if the document is cached on the document provider
47+ if (tracingData .containsKey ("validation" ) && ((Map <String , Object >)tracingData .get ("validation" )).containsKey ("duration" )) {
48+ Timer validationTimer = buildQueryTimer (parameters .getOperation (), "validation" );
49+ validationTimer .record ((long ) ((Map <String , Object >)tracingData .get ("validation" )).get ("duration" ), TimeUnit .NANOSECONDS );
50+ }
51+ if (tracingData .containsKey ("parsing" ) && ((Map <String , Object >)tracingData .get ("parsing" )).containsKey ("duration" )) {
52+ Timer parsingTimer = buildQueryTimer (parameters .getOperation (), "parsing" );
53+ parsingTimer .record ((long ) ((Map <String , Object >)tracingData .get ("parsing" )).get ("duration" ), TimeUnit .NANOSECONDS );
54+ }
55+
56+ if (((Map <String , String >)tracingData .get ("execution" )).containsKey ("resolvers" )) {
57+
58+ ((List <Map <String , Object >>)((Map <String , Object >)tracingData .get ("execution" )).get ("resolvers" )).forEach (field -> {
59+
60+ Timer fieldTimer = buildFieldTimer (parameters .getOperation (), "resolvers" , (String )field .get ("parentType" ), (String )field .get ("fieldName" ));
61+ fieldTimer .record ((long ) field .get ("duration" ), TimeUnit .NANOSECONDS );
62+
63+ });
64+
65+ }
66+
67+ if (!tracingEnabled ) {
68+ executionResult .getExtensions ().remove ("tracing" );
69+ }
70+ }
71+
72+ return CompletableFuture .completedFuture (executionResult );
73+ }
74+
75+ private Timer buildQueryTimer (String operationName , String operation ) {
76+ return Timer .builder (QUERY_TIME_METRIC_NAME )
77+ .description (TIMER_DESCRIPTION )
78+ .tag (OPERATION_NAME_TAG , operationName != null ? operationName : UNKNOWN_OPERATION_NAME )
79+ .tag (OPERATION , operation )
80+ .register (meterRegistry );
81+ }
82+
83+ private Timer buildFieldTimer (String operationName , String operation , String parent , String field ) {
84+ return Timer .builder (RESOLVER_TIME_METRIC_NAME )
85+ .description (TIMER_DESCRIPTION )
86+ .tag (OPERATION_NAME_TAG , operationName != null ? operationName : UNKNOWN_OPERATION_NAME )
87+ .tag (PARENT , parent )
88+ .tag (FIELD , field )
89+ .tag (OPERATION , operation )
90+ .register (meterRegistry );
91+ }
92+ }
0 commit comments