Skip to content

Commit 26029a6

Browse files
authored
Merge pull request opentripplanner#6712 from HSLdevcom/add-tracing-tags-to-gtfs-api
Add tracing tags for headers and query parameters to GTFS API
2 parents 08f742d + b4de1da commit 26029a6

17 files changed

Lines changed: 210 additions & 33 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.opentripplanner.apis.gtfs;
2+
3+
import java.util.Collection;
4+
5+
/**
6+
* GTFS API parameters. These parameters configure the behaviour of some aspects of the
7+
* GTFS GraphQL API.
8+
*/
9+
public interface GtfsApiParameters {
10+
/**
11+
* Which HTTP headers or query parameters should be used as tags for performance metering in the
12+
* Actuator API.
13+
*
14+
* @see org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation
15+
*/
16+
Collection<String> tracingTags();
17+
}

application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLAPI.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
import jakarta.ws.rs.core.HttpHeaders;
1313
import jakarta.ws.rs.core.MediaType;
1414
import jakarta.ws.rs.core.Response;
15+
import jakarta.ws.rs.core.UriInfo;
1516
import java.io.IOException;
1617
import java.util.HashMap;
1718
import java.util.Locale;
1819
import java.util.Map;
20+
import org.opentripplanner.apis.support.TracingUtils;
1921
import org.opentripplanner.standalone.api.OtpServerRequestContext;
2022
import org.slf4j.Logger;
2123
import org.slf4j.LoggerFactory;
@@ -50,12 +52,13 @@ public GtfsGraphQLAPIOldPath(
5052
@POST
5153
@Consumes(MediaType.APPLICATION_JSON)
5254
public Response getGraphQL(
53-
HashMap<String, Object> queryParameters,
55+
HashMap<String, Object> jsonParameters,
5456
@HeaderParam("OTPTimeout") @DefaultValue("30000") int timeout,
5557
@HeaderParam("OTPMaxResolves") @DefaultValue("1000000") int maxResolves,
56-
@Context HttpHeaders headers
58+
@Context HttpHeaders headers,
59+
@Context UriInfo uriInfo
5760
) {
58-
if (queryParameters == null || !queryParameters.containsKey("query")) {
61+
if (jsonParameters == null || !jsonParameters.containsKey("query")) {
5962
LOG.debug("No query found in body");
6063
return Response.status(Response.Status.BAD_REQUEST)
6164
.type(MediaType.TEXT_PLAIN_TYPE)
@@ -67,9 +70,9 @@ public Response getGraphQL(
6770
? headers.getAcceptableLanguages().get(0)
6871
: serverContext.defaultRouteRequest().preferences().locale();
6972

70-
String query = (String) queryParameters.get("query");
71-
Object queryVariables = queryParameters.getOrDefault("variables", null);
72-
String operationName = (String) queryParameters.getOrDefault("operationName", null);
73+
String query = (String) jsonParameters.get("query");
74+
Object queryVariables = jsonParameters.getOrDefault("variables", null);
75+
String operationName = (String) jsonParameters.getOrDefault("operationName", null);
7376
Map<String, Object> variables;
7477

7578
if (queryVariables instanceof Map) {
@@ -93,7 +96,12 @@ public Response getGraphQL(
9396
maxResolves,
9497
timeout,
9598
locale,
96-
GraphQLRequestContext.ofServerContext(serverContext)
99+
GraphQLRequestContext.ofServerContext(serverContext),
100+
TracingUtils.findTagsInHeadersOrQueryParameters(
101+
serverContext.gtfsApiParameters().tracingTags(),
102+
headers,
103+
uriInfo.getQueryParameters()
104+
)
97105
);
98106
}
99107

@@ -103,7 +111,8 @@ public Response getGraphQL(
103111
String query,
104112
@HeaderParam("OTPTimeout") @DefaultValue("30000") int timeout,
105113
@HeaderParam("OTPMaxResolves") @DefaultValue("1000000") int maxResolves,
106-
@Context HttpHeaders headers
114+
@Context HttpHeaders headers,
115+
@Context UriInfo uriInfo
107116
) {
108117
Locale locale = headers.getAcceptableLanguages().size() > 0
109118
? headers.getAcceptableLanguages().get(0)
@@ -115,7 +124,12 @@ public Response getGraphQL(
115124
maxResolves,
116125
timeout,
117126
locale,
118-
GraphQLRequestContext.ofServerContext(serverContext)
127+
GraphQLRequestContext.ofServerContext(serverContext),
128+
TracingUtils.findTagsInHeadersOrQueryParameters(
129+
serverContext.gtfsApiParameters().tracingTags(),
130+
headers,
131+
uriInfo.getQueryParameters()
132+
)
119133
);
120134
}
121135
}

application/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
import graphql.execution.instrumentation.ChainedInstrumentation;
99
import graphql.execution.instrumentation.Instrumentation;
1010
import io.micrometer.core.instrument.Metrics;
11+
import io.micrometer.core.instrument.Tag;
1112
import jakarta.ws.rs.core.Response;
1213
import java.util.HashMap;
13-
import java.util.List;
1414
import java.util.Locale;
1515
import java.util.Map;
1616
import java.util.concurrent.ExecutionException;
@@ -30,13 +30,14 @@ static ExecutionResult getGraphQLExecutionResult(
3030
int maxResolves,
3131
int timeoutMs,
3232
Locale locale,
33-
GraphQLRequestContext requestContext
33+
GraphQLRequestContext requestContext,
34+
Iterable<Tag> tracingTags
3435
) {
3536
Instrumentation instrumentation = new MaxQueryComplexityInstrumentation(maxResolves);
3637

3738
if (OTPFeature.ActuatorAPI.isOn()) {
3839
instrumentation = new ChainedInstrumentation(
39-
new MicrometerGraphQLInstrumentation(Metrics.globalRegistry, List.of()),
40+
new MicrometerGraphQLInstrumentation(Metrics.globalRegistry, tracingTags),
4041
instrumentation
4142
);
4243
}
@@ -71,7 +72,8 @@ static Response getGraphQLResponse(
7172
int maxResolves,
7273
int timeoutMs,
7374
Locale locale,
74-
GraphQLRequestContext requestContext
75+
GraphQLRequestContext requestContext,
76+
Iterable<Tag> tracingTags
7577
) {
7678
ExecutionResult executionResult = getGraphQLExecutionResult(
7779
query,
@@ -80,7 +82,8 @@ static Response getGraphQLResponse(
8082
maxResolves,
8183
timeoutMs,
8284
locale,
83-
requestContext
85+
requestContext,
86+
tracingTags
8487
);
8588

8689
return Response.status(Response.Status.OK)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.opentripplanner.apis.support;
2+
3+
import io.micrometer.core.instrument.Tag;
4+
import jakarta.ws.rs.core.HttpHeaders;
5+
import jakarta.ws.rs.core.MultivaluedMap;
6+
import java.util.Collection;
7+
8+
public final class TracingUtils {
9+
10+
private static final String UNKNOWN_VALUE = "__UNKNOWN__";
11+
12+
/**
13+
* This method tries to find a tracing tag from a request's headers.
14+
* If no value is found for a tag, the value is set to "__UNKNOWN__".
15+
*
16+
* @param tracingHeaderTags a collection of tag names to match against headers
17+
* @param headers headers from a request
18+
* @return a list of tracing tags with computed values
19+
*/
20+
public static Iterable<Tag> findTagsInHeaders(
21+
Collection<String> tracingHeaderTags,
22+
HttpHeaders headers
23+
) {
24+
return tracingHeaderTags
25+
.stream()
26+
.map(header -> {
27+
String value = headers.getHeaderString(header);
28+
return Tag.of(header, value == null ? UNKNOWN_VALUE : value);
29+
})
30+
.toList();
31+
}
32+
33+
/**
34+
* This method tries to find a tracing tag from either a request's headers or query parameters.
35+
* The value from headers is favored if a value is present in both.
36+
* If no value is found for a tag, the value is set to "__UNKNOWN__".
37+
*
38+
* @param tracingTags a collection of tag names to match against headers or query parameters
39+
* @param headers headers from a request
40+
* @param queryParameters query parameters from a request
41+
* @return a list of tracing tags with computed values
42+
*/
43+
public static Iterable<Tag> findTagsInHeadersOrQueryParameters(
44+
Collection<String> tracingTags,
45+
HttpHeaders headers,
46+
MultivaluedMap<String, String> queryParameters
47+
) {
48+
return tracingTags
49+
.stream()
50+
.map(header -> {
51+
String headerValue = headers.getHeaderString(header);
52+
String queryParameterValue = queryParameters.getFirst(header);
53+
if (headerValue != null) {
54+
return Tag.of(header, headerValue);
55+
} else if (queryParameterValue != null) {
56+
return Tag.of(header, queryParameterValue);
57+
} else {
58+
return Tag.of(header, UNKNOWN_VALUE);
59+
}
60+
})
61+
.toList();
62+
}
63+
}

application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import graphql.schema.GraphQLSchema;
55
import graphql.schema.idl.SchemaPrinter;
6-
import io.micrometer.core.instrument.Tag;
76
import jakarta.ws.rs.BadRequestException;
87
import jakarta.ws.rs.Consumes;
98
import jakarta.ws.rs.GET;
@@ -20,7 +19,7 @@
2019
import java.util.Collections;
2120
import java.util.HashMap;
2221
import java.util.Map;
23-
import java.util.stream.Collectors;
22+
import org.opentripplanner.apis.support.TracingUtils;
2423
import org.opentripplanner.apis.support.graphql.injectdoc.ApiDocumentationProfile;
2524
import org.opentripplanner.apis.transmodel.mapping.TransitIdMapper;
2625
import org.opentripplanner.routing.api.request.RouteRequest;
@@ -134,7 +133,7 @@ public Response getGraphQL(
134133
variables,
135134
operationName,
136135
maxNumberOfResultFields,
137-
getTagsFromHeaders(headers)
136+
TracingUtils.findTagsInHeaders(tracingHeaderTags, headers)
138137
);
139138
}
140139

@@ -147,7 +146,7 @@ public Response getGraphQL(String query, @Context HttpHeaders headers) {
147146
null,
148147
null,
149148
maxNumberOfResultFields,
150-
getTagsFromHeaders(headers)
149+
TracingUtils.findTagsInHeaders(tracingHeaderTags, headers)
151150
);
152151
}
153152

@@ -157,14 +156,4 @@ public Response getGraphQLSchema() {
157156
var text = SCHEMA_DOC_HEADER + new SchemaPrinter().print(schema);
158157
return Response.ok().encoding("UTF-8").entity(text).build();
159158
}
160-
161-
private static Iterable<Tag> getTagsFromHeaders(HttpHeaders headers) {
162-
return tracingHeaderTags
163-
.stream()
164-
.map(header -> {
165-
String value = headers.getHeaderString(header);
166-
return Tag.of(header, value == null ? "__UNKNOWN__" : value);
167-
})
168-
.collect(Collectors.toList());
169-
}
170159
}

application/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPIParameters.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.opentripplanner.apis.transmodel;
22

33
import java.util.Collection;
4-
import org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation;
54

65
/**
76
* Transmodel API parameters. These parameters configure the behaviour of some aspects of the
@@ -16,7 +15,7 @@ public interface TransmodelAPIParameters {
1615
/**
1716
* Which HTTP headers should be used as tags for performance metering in the Actuator API
1817
*
19-
* @see MicrometerGraphQLInstrumentation
18+
* @see org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation
2019
*/
2120
Collection<String> tracingHeaderTags();
2221

application/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.micrometer.core.instrument.MeterRegistry;
55
import java.util.List;
66
import javax.annotation.Nullable;
7+
import org.opentripplanner.apis.gtfs.GtfsApiParameters;
78
import org.opentripplanner.astar.spi.TraverseVisitor;
89
import org.opentripplanner.ext.dataoverlay.routing.DataOverlayContext;
910
import org.opentripplanner.ext.flex.FlexParameters;
@@ -128,6 +129,8 @@ default GraphFinder graphFinder() {
128129

129130
TriasApiParameters triasApiParameters();
130131

132+
GtfsApiParameters gtfsApiParameters();
133+
131134
/* Sandbox modules */
132135

133136
@Nullable

application/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.fasterxml.jackson.databind.node.MissingNode;
99
import java.io.Serializable;
1010
import java.util.List;
11+
import org.opentripplanner.apis.gtfs.GtfsApiParameters;
1112
import org.opentripplanner.ext.flex.FlexParameters;
1213
import org.opentripplanner.ext.ridehailing.RideHailingServiceParameters;
1314
import org.opentripplanner.ext.trias.config.TriasApiConfig;
@@ -20,6 +21,7 @@
2021
import org.opentripplanner.standalone.config.routerconfig.UpdatersConfig;
2122
import org.opentripplanner.standalone.config.routerconfig.VectorTileConfig;
2223
import org.opentripplanner.standalone.config.sandbox.FlexConfig;
24+
import org.opentripplanner.standalone.config.sandbox.GtfsApiConfig;
2325
import org.opentripplanner.standalone.config.sandbox.TransmodelAPIConfig;
2426
import org.opentripplanner.updater.UpdatersParameters;
2527
import org.slf4j.Logger;
@@ -50,6 +52,7 @@ public class RouterConfig implements Serializable {
5052
private final RideHailingServicesConfig rideHailingConfig;
5153
private final FlexConfig flexConfig;
5254
private final TransmodelAPIConfig transmodelApi;
55+
private final GtfsApiConfig gtfsApi;
5356
private final VectorTileConfig vectorTileConfig;
5457
private final TriasApiParameters triasApiParameters;
5558

@@ -69,6 +72,7 @@ public RouterConfig(JsonNode node, String source, boolean logUnusedParams) {
6972

7073
this.server = new ServerConfig("server", root);
7174
this.transmodelApi = new TransmodelAPIConfig("transmodelApi", root);
75+
this.gtfsApi = new GtfsApiConfig("gtfsApi", root);
7276
var request = mapDefaultRouteRequest("routingDefaults", root);
7377
this.transitConfig = new TransitRoutingConfig("transit", root, request);
7478
this.routingRequestDefaults = request
@@ -142,6 +146,10 @@ public TriasApiParameters triasApiParameters() {
142146
return triasApiParameters;
143147
}
144148

149+
public GtfsApiParameters gtfsApiParameters() {
150+
return gtfsApi;
151+
}
152+
145153
public NodeAdapter asNodeAdapter() {
146154
return root;
147155
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.opentripplanner.standalone.config.sandbox;
2+
3+
import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_8;
4+
5+
import java.util.Collection;
6+
import java.util.Set;
7+
import org.opentripplanner.apis.gtfs.GtfsApiParameters;
8+
import org.opentripplanner.standalone.config.framework.json.NodeAdapter;
9+
10+
/**
11+
* @see GtfsApiParameters for documentation of parameters
12+
*/
13+
public class GtfsApiConfig implements GtfsApiParameters {
14+
15+
private final Collection<String> tracingTags;
16+
17+
public GtfsApiConfig(String parameterName, NodeAdapter root) {
18+
var c = root
19+
.of(parameterName)
20+
.since(V2_8)
21+
.summary("Configuration for the GTFS GraphQL API.")
22+
.asObject();
23+
24+
tracingTags = c
25+
.of("tracingTags")
26+
.summary("Used to group requests based on headers or query parameters when monitoring OTP.")
27+
.asStringList(Set.of());
28+
}
29+
30+
@Override
31+
public Collection<String> tracingTags() {
32+
return tracingTags;
33+
}
34+
}

application/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ OtpServerRequestContext providesServerContext(
6363

6464
var transitRoutingConfig = routerConfig.transitTuningConfig();
6565
var triasApiParameters = routerConfig.triasApiParameters();
66+
var gtfsApiConfig = routerConfig.gtfsApiParameters();
6667
var vectorTileConfig = routerConfig.vectorTileConfig();
6768
var flexParameters = routerConfig.flexParameters();
6869

@@ -80,6 +81,7 @@ OtpServerRequestContext providesServerContext(
8081
transitRoutingConfig,
8182
transitService,
8283
triasApiParameters,
84+
gtfsApiConfig,
8385
vectorTileConfig,
8486
vehicleParkingService,
8587
vehicleRentalService,

0 commit comments

Comments
 (0)