Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server timing header #1617

Merged
merged 23 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion components/inspectit-ocelot-configdocsgenerator/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies {

'org.mockito:mockito-junit-jupiter',
"org.assertj:assertj-core",
"com.google.guava:guava:${guavaVersion}"
"com.google.guava:guava:${guavaVersionConfigServer}"
)

// This dependency is used by the application.
Expand All @@ -24,6 +24,8 @@ dependencies {
"ch.qos.logback:logback-classic",
"org.apache.commons:commons-lang3",
"commons-beanutils:commons-beanutils:${commonsBeanUtilsVersion}",
// Update dependency, due to Out-of-Support
"org.apache.commons:commons-collections4:${commonsCollectionsVersion}",

"org.springframework.boot:spring-boot-starter-web",
// override snakeyaml due to vulnerabilities in v1.29 used by the SpringBoot version used in this module
Expand Down
4 changes: 2 additions & 2 deletions components/inspectit-ocelot-configurationserver/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ dependencies {

'org.apache.httpcomponents:httpclient', //Required for PATCH-Requests

"org.xerial:sqlite-jdbc",
"org.xerial:sqlite-jdbc:${sqliteVersion}",
"com.github.gwenn:sqlite-dialect:${sqliteDialect}",
"io.jsonwebtoken:jjwt-api:${jsonWebTokenVersion}",
"io.jsonwebtoken:jjwt-impl:${jsonWebTokenVersion}",
Expand All @@ -152,7 +152,7 @@ dependencies {
"org.eclipse.jgit:org.eclipse.jgit:${eclipseJgitVersion}",
"org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:${eclipseJgitVersion}",
"com.google.code.gson:gson",
"com.google.guava:guava:${guavaVersion}",
"com.google.guava:guava:${guavaVersionConfigServer}",

// swagger
"org.springdoc:springdoc-openapi-ui:${springdocOopenapiUiVersion}",
Expand Down
14 changes: 10 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
### https://docs.spring.io/spring-boot/docs/${springBootVersion}/reference/html/dependency-versions.html

# 2.7 is the latest release line which runs on Java 8
springBootVersion=2.7.12
springBootVersion=2.7.15

# Ensure to adapt the netty version when changing the OpenCensus version
openCensusVersion=0.31.1
Expand All @@ -21,25 +21,30 @@ tcnativeVersion=2.0.56.Final
openTelemetryVersion=1.25.0
openTelemetryAlphaVersion=1.25.0-alpha
opentelemetryProtoVersion=1.7.1-alpha
# update okio, due to security concerns
okioVersion=3.5.0

grpcVersion=1.43.1

# snakeYaml version
# We use a newer version as spring-boot because of Security issues
# Version 2.* is not working with the current jackson-dependency of spring 2
snakeYamlVersion=1.33

jsonWebTokenVersion=0.11.5

# If possible keep this version in sync with the assertj-core version used by spring-boot
assertjGuavaVersion=3.24.2

sqliteVersion=3.43.0.0
sqliteDialect=0.1.4

# Starting with version 6.x requires Java 11
eclipseJgitVersion=5.13.1.202206130422-r

springdocOopenapiUiVersion=1.7.0

# Wiremock 3.* doesn't support java 8
wiremockVersion=2.35.0

# We need to use this version, because some database setups do not work
Expand Down Expand Up @@ -77,12 +82,13 @@ protobufJavaUtilVersion=3.22.3
logUnitVersion=1.1.3

commonsBeanUtilsVersion=1.9.4

commonsCollectionsVersion=4.4
commonsIoVersion=2.11.0

commonsMathVersion=3.6.1

guavaVersion=31.1-jre
guavaVersionConfigServer=32.1.2-jre
# ocelot-agent and ocelot-core cannot update guava, because of wiremock
guavaVersionAgent=31.1-jre

### gradle plugin versions
### Check for newer version at https://plugins.gradle.org/
Expand Down
3 changes: 3 additions & 0 deletions inspectit-ocelot-agent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ dependencies {
"io.opentelemetry:opentelemetry-sdk:${openTelemetryVersion}",
"io.opentelemetry:opentelemetry-opencensus-shim:${openTelemetryAlphaVersion}",
"io.opencensus:opencensus-impl:${openCensusVersion}",
"com.google.guava:guava:${guavaVersionAgent}",
)
annotationProcessor "org.projectlombok:lombok"
testImplementation (
Expand Down Expand Up @@ -137,6 +138,7 @@ dependencies {
systemTestImplementation(
"io.opencensus:opencensus-testing:${openCensusVersion}",
"io.opencensus:opencensus-api:${openCensusVersion}",
"com.google.guava:guava:${guavaVersionAgent}",

// for InMemorySpanExporter
platform("io.opentelemetry:opentelemetry-bom:${openTelemetryVersion}"),
Expand All @@ -151,6 +153,7 @@ dependencies {
"org.assertj:assertj-core",
"org.awaitility:awaitility",
"com.github.tomakehurst:wiremock-jre8:${wiremockVersion}",
"org.yaml:snakeyaml:${snakeYamlVersion}",

"org.apache.httpcomponents:httpclient",
// Switching to spring-boot dependency management excludes somehow magically the transitive
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
return Collections.<String, Object>emptyMap().entrySet();
}

@Override
public String createRemoteParentContext() {
return null;

Check warning on line 32 in inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/context/noop/NoopContext.java

View check run for this annotation

Codecov / codecov/patch

inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/context/noop/NoopContext.java#L32

Added line #L32 was not covered by tests
}

@Override
public void makeActive() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

@Override
public AutoCloseable startCorrelatedSpanScope(Supplier<? extends AutoCloseable> spanScopeStarter) {
return spanScopeStarter.get();
if(spanScopeStarter != null) return spanScopeStarter.get();
return null;

Check warning on line 15 in inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/correlation/noop/NoopLogTraceCorrelator.java

View check run for this annotation

Codecov / codecov/patch

inspectit-ocelot-bootstrap/src/main/java/rocks/inspectit/ocelot/bootstrap/correlation/noop/NoopLogTraceCorrelator.java#L15

Added line #L15 was not covered by tests
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ public interface InspectitContext {
*/
Iterable<Map.Entry<String, Object>> getData();

/**
* This function should be called in the entry- or pre-entry-phase, to allow the created span to use the context
*
* Creates a SpanContext locally, which the current InspectitContext can use as a remote-parent-context,
* as long as no REMOTE_PARENT_SPAN_CONTEXT_KEY was specified earlier by down-propagation
*
* @return The trace context of the created SpanContext in the W3C-format
*/
String createRemoteParentContext();

/**
* Generates a map representing the globally down-propagated data stored in this context.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ public class HttpExporterSettings {
* How long the data should be stored in the server
*/
private int timeToLive;

/**
* Header, which will be read during browser-propagation to receive the session-ID
*/
private String sessionIdHeader;
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,5 @@ inspectit:
session-limit: 100
# how long the data should be stored in the server in seconds
time-to-live: 300
# header, which will be read during browser-propagation to receive the session-ID
session-id-header: "Cookie"
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,28 @@ inspectit:
}
}
}

'a_servletapi_remoteParentContext':
docs:
since: '2.5.4'
description: "Writes a parent trace context to the given HttpServletResponse's Server-Timing header"
inputs:
'response': 'The HttpServletResponse to write to'
is-void: true
imports:
- 'javax.servlet'
- 'javax.servlet.http'
input:
'response': 'ServletResponse'
_context: 'InspectitContext'
value-body: |
if(response instanceof HttpServletResponse) {
HttpServletResponse res = (HttpServletResponse) response;
if(!res.isCommitted()) {
String traceContext = _context.createRemoteParentContext();
if(traceContext == null) return;
String key = "Server-Timing";
String value = "traceparent; desc=" + traceContext;
res.addHeader(key, value);
}
}
4 changes: 3 additions & 1 deletion inspectit-ocelot-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ dependencies {
"io.prometheus:simpleclient_httpserver",

// this overwrites version of guava that opencensus-impl pulls in transitively, too.
"com.google.guava:guava:${guavaVersion}",
"com.google.guava:guava:${guavaVersionAgent}",
// we still need the OpenCensus SDK for the metric exporters to work, as the shim only includes opencensus-impl-core
"io.opencensus:opencensus-impl:${openCensusVersion}",

Expand All @@ -106,6 +106,8 @@ dependencies {
"io.opentelemetry:opentelemetry-exporter-jaeger-thrift",
"io.opentelemetry:opentelemetry-exporter-zipkin",
"io.opentelemetry:opentelemetry-exporter-otlp",
// Update okio, due to security concerns
"com.squareup.okio:okio:${okioVersion}",

platform("io.opentelemetry:opentelemetry-bom-alpha:${openTelemetryAlphaVersion}"),
"io.opentelemetry:opentelemetry-exporter-prometheus",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@
private BrowserPropagationSessionStorage sessionStorage;
private BrowserPropagationServlet httpServlet;

/**
* Stores a reference of the InspectITConfig to enable runtime updates of the session limit
*/
private InspectitConfig inspectitConfig;

/**
* Delay to rerun the scheduled method after the method finished in milliseconds
*/
Expand Down Expand Up @@ -65,12 +60,14 @@
String host = settings.getHost();
int port = settings.getPort();
String path = settings.getPath();
int sessionLimit = settings.getSessionLimit();
timeToLive = settings.getTimeToLive();

int sessionLimit = settings.getSessionLimit();

Check warning on line 65 in inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/BrowserPropagationHttpExporterService.java

View check run for this annotation

Codecov / codecov/patch

inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/BrowserPropagationHttpExporterService.java#L65

Added line #L65 was not covered by tests
sessionStorage = BrowserPropagationSessionStorage.getInstance();
sessionStorage.setSessionLimit(sessionLimit);
httpServlet = new BrowserPropagationServlet();
inspectitConfig = configuration;

String sessionIdHeader = settings.getSessionIdHeader();
httpServlet = new BrowserPropagationServlet(sessionIdHeader);

Check warning on line 70 in inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/BrowserPropagationHttpExporterService.java

View check run for this annotation

Codecov / codecov/patch

inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/exporter/BrowserPropagationHttpExporterService.java#L69-L70

Added lines #L69 - L70 were not covered by tests

return startServer(host, port, path, httpServlet);
}
Expand Down Expand Up @@ -107,19 +104,13 @@
}

/**
* Updates the session storage:
* 1. Browser propagation data is cached for a specific amount of time (timeToLive)
* If the time expires, clean up the storage
* 2. Update the session limit
* Note that this will not delete any active sessions, if the new session limit is exceeded
* Updates the session storage
* Browser propagation data is cached for a specific amount of time (timeToLive)
* If the time expires, clean up the storage
*/
@Scheduled(fixedDelay = FIXED_DELAY)
public void updateSessionStorage() {
if(httpServlet == null) return;
sessionStorage.cleanUpData(timeToLive);

if(inspectitConfig == null) return;
int sessionLimit = inspectitConfig.getExporters().getTags().getHttp().getSessionLimit();
sessionStorage.setSessionLimit(sessionLimit);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates of exporter properties will restart the exporter. Thus, no updates have to be done in the scheduled method.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,31 @@
@Slf4j
public class BrowserPropagationServlet extends HttpServlet {

/**
* Header, which should be used to store the session-Ids
* Default-key: "Cookie"
*/
private final String sessionIdHeader;
private final ObjectMapper mapper;
private final BrowserPropagationSessionStorage sessionStorage;

public BrowserPropagationServlet() {
mapper = new ObjectMapper();
sessionStorage = BrowserPropagationSessionStorage.getInstance();
public BrowserPropagationServlet(String sessionIdHeader) {
this.sessionIdHeader = sessionIdHeader;
this.mapper = new ObjectMapper();
this.sessionStorage = BrowserPropagationSessionStorage.getInstance();
}

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
log.debug("Tags HTTP-server received GET-request");
String sessionID = request.getHeader("cookie");
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "GET");
response.setHeader("Access-Control-Allow-Credentials", "true");

String sessionID = request.getHeader(sessionIdHeader);
if(sessionID == null) {
log.warn("Request misses session ID");
log.warn("Request to Tags HTTP-server misses session ID");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
else {
Expand All @@ -63,9 +74,14 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) {
log.debug("Tags HTTP-server received PUT-request");
String sessionID = request.getHeader("cookie");
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "PUT");
response.setHeader("Access-Control-Allow-Credentials", "true");

String sessionID = request.getHeader(sessionIdHeader);
if(sessionID == null) {
log.warn("Request misses session ID");
log.warn("Request to Tags HTTP-server misses session ID");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
else {
Expand All @@ -87,13 +103,28 @@ protected void doPut(HttpServletRequest request, HttpServletResponse response) {
}
}

@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response) {
log.debug("Tags HTTP-server received OPTIONS-request");
String origin = request.getHeader("Origin");
String accessControlRequestMethod = request.getHeader("Access-Control-Request-Method");

if (origin != null && accessControlRequestMethod != null) {
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "GET, PUT");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setStatus(HttpServletResponse.SC_OK);
}
else response.setStatus(HttpServletResponse.SC_FORBIDDEN);
}

private Map<String, String> getRequestBody(HttpServletRequest request) {
try (BufferedReader reader = request.getReader()) {
Set<Map.Entry<String,String>> entrySet = mapper.readValue(reader, new TypeReference<Set<Map.Entry<String,String>>>() {});
return entrySet.stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
} catch (Exception e) {
log.info("Request failed");
log.info("Request to Tags HTTP-server failed");
return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package rocks.inspectit.ocelot.core.instrumentation.browser;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -16,6 +17,7 @@ public class BrowserPropagationSessionStorage {
private static final int KEY_MIN_SIZE = 64;
private static final int KEY_MAX_SIZE = 512;

@Getter
@Setter
private int sessionLimit = 100;
private static BrowserPropagationSessionStorage instance;
Expand Down
Loading
Loading