Skip to content

Commit d7a0b01

Browse files
committed
Merge branch 'raphasil-complete-manual-log-traces-correlation' into 'main'
Add complete application for manual instrumentation See merge request observability-bd-projects/one-observability-demo!110
2 parents 607ec61 + 7b4b999 commit d7a0b01

File tree

16 files changed

+1021
-0
lines changed

16 files changed

+1021
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package ca.petsearch;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class Application {
8+
public static void main(String[] args) {
9+
SpringApplication.run(Application.class, args);
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package ca.petsearch;
2+
3+
import org.springframework.web.util.ContentCachingResponseWrapper;
4+
5+
import javax.servlet.*;
6+
import javax.servlet.http.HttpServletRequest;
7+
import javax.servlet.http.HttpServletResponse;
8+
import java.io.IOException;
9+
10+
public class ApplicationFilter implements Filter {
11+
12+
private final MetricEmitter metricEmitter;
13+
14+
public ApplicationFilter(MetricEmitter metricEmitter) {
15+
this.metricEmitter = metricEmitter;
16+
}
17+
18+
@Override
19+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
20+
21+
long requestStartTime = System.currentTimeMillis();
22+
23+
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);
24+
25+
chain.doFilter(request, responseWrapper);
26+
27+
int loadSize = responseWrapper.getContentSize();
28+
29+
responseWrapper.copyBodyToResponse();
30+
31+
String statusCode = String.valueOf(((HttpServletResponse)response).getStatus());
32+
33+
metricEmitter.emitReturnTimeMetric(
34+
System.currentTimeMillis() - requestStartTime, ((HttpServletRequest)request).getServletPath(), statusCode);
35+
36+
37+
metricEmitter.emitBytesSentMetric(loadSize, ((HttpServletRequest)request).getServletPath(), statusCode);
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package ca.petsearch;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.common.AttributeKey;
5+
import io.opentelemetry.api.common.Attributes;
6+
import io.opentelemetry.api.metrics.LongCounter;
7+
import io.opentelemetry.api.metrics.LongHistogram;
8+
import io.opentelemetry.api.metrics.Meter;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
public class MetricEmitter {
13+
14+
private Logger logger = LoggerFactory.getLogger(MetricEmitter.class);
15+
16+
static final String DIMENSION_API_NAME = "apiName";
17+
static final String DIMENSION_STATUS_CODE = "statusCode";
18+
19+
static String API_COUNTER_METRIC = "apiBytesSent";
20+
static String API_LATENCY_METRIC = "latency";
21+
static String PETS_RETURNED_METRIC = "petsReturned";
22+
23+
private LongCounter apiBytesSentCounter;
24+
private LongHistogram apiLatencyHistogram;
25+
private LongCounter petsReturned;
26+
27+
public MetricEmitter(OpenTelemetry otel) {
28+
Meter meter = otel.meterBuilder("aws-otel").setInstrumentationVersion("1.0").build();
29+
30+
logger.debug("OTLP port is: " + System.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"));
31+
32+
String latencyMetricName = API_LATENCY_METRIC;
33+
String apiBytesSentMetricName = API_COUNTER_METRIC;
34+
String petsReturnedMetricName = PETS_RETURNED_METRIC;
35+
36+
String instanceId = System.getenv("INSTANCE_ID");
37+
if (instanceId != null && !instanceId.trim().equals("")) {
38+
latencyMetricName = API_LATENCY_METRIC + "_" + instanceId;
39+
apiBytesSentMetricName = API_COUNTER_METRIC + "_" + instanceId;
40+
petsReturnedMetricName = PETS_RETURNED_METRIC + "_" + instanceId;
41+
}
42+
43+
apiBytesSentCounter =
44+
meter
45+
.counterBuilder(apiBytesSentMetricName)
46+
.setDescription("API request load sent in bytes")
47+
.setUnit("one")
48+
.build();
49+
50+
petsReturned =
51+
meter
52+
.counterBuilder(petsReturnedMetricName)
53+
.setDescription("Number of pets returned by this service")
54+
.setUnit("one")
55+
.build();
56+
57+
58+
apiLatencyHistogram =
59+
meter
60+
.histogramBuilder(latencyMetricName).ofLongs()
61+
.setDescription("API latency time")
62+
.setUnit("ms")
63+
.build();
64+
}
65+
66+
/**
67+
* emit http request latency metrics with summary metric type
68+
*
69+
* @param returnTime
70+
* @param apiName
71+
* @param statusCode
72+
*/
73+
public void emitReturnTimeMetric(Long returnTime, String apiName, String statusCode) {
74+
logger.debug(
75+
"emit metric with return time " + returnTime + "ms, " + apiName + ", status code:" + statusCode);
76+
apiLatencyHistogram.record(
77+
returnTime, Attributes.of(AttributeKey.stringKey(DIMENSION_API_NAME), apiName, AttributeKey.stringKey(DIMENSION_STATUS_CODE), statusCode));
78+
}
79+
80+
/**
81+
* emit http request load size with counter metrics type
82+
*
83+
* @param bytes
84+
* @param apiName
85+
* @param statusCode
86+
*/
87+
public void emitBytesSentMetric(int bytes, String apiName, String statusCode) {
88+
logger.debug("emit metric with http request size " + bytes + " bytes, " + apiName);
89+
apiBytesSentCounter.add(
90+
bytes, Attributes.of(AttributeKey.stringKey(DIMENSION_API_NAME), apiName, AttributeKey.stringKey(DIMENSION_STATUS_CODE), statusCode));
91+
}
92+
93+
public void emitPetsReturnedMetric(int petsCount) {
94+
petsReturned.add(petsCount);
95+
}
96+
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package ca.petsearch;
2+
3+
public class PseudoRandomNumberGenerator implements RandomNumberGenerator {
4+
5+
@Override
6+
public int nextNonNegativeInt(int max) {
7+
if (max < 0) throw new RuntimeException("Wrong parameter value");
8+
return (int) (Math.random() * max);
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package ca.petsearch;
2+
3+
public interface RandomNumberGenerator {
4+
int nextNonNegativeInt(int max);
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package ca.petsearch;
2+
3+
import ch.qos.logback.classic.spi.ILoggingEvent;
4+
import ch.qos.logback.core.filter.Filter;
5+
import ch.qos.logback.core.spi.FilterReply;
6+
import io.opentelemetry.api.trace.Span;
7+
import org.slf4j.MDC;
8+
9+
public class TracingLogFilter extends Filter<ILoggingEvent> {
10+
@Override
11+
public FilterReply decide(ILoggingEvent event) {
12+
Span span = Span.current();
13+
14+
if (span == Span.getInvalid()) {
15+
MDC.remove("AWS-XRAY-TRACE-ID");
16+
return FilterReply.ACCEPT;
17+
}
18+
19+
String traceId = span.getSpanContext().getTraceId();
20+
String entityId = span.getSpanContext().getSpanId();
21+
22+
String traceLog = "1-"
23+
+ traceId.substring(0, 8)
24+
+ "-"
25+
+ traceId.substring(8)
26+
+ "@"
27+
+ entityId;
28+
MDC.put("AWS-XRAY-TRACE-ID", traceLog);
29+
30+
return FilterReply.ACCEPT;
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package ca.petsearch;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.trace.Span;
5+
import io.opentelemetry.api.trace.SpanKind;
6+
import io.opentelemetry.api.trace.StatusCode;
7+
import io.opentelemetry.api.trace.Tracer;
8+
import io.opentelemetry.context.Context;
9+
import io.opentelemetry.context.Scope;
10+
import io.opentelemetry.context.propagation.TextMapGetter;
11+
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
import org.springframework.web.servlet.HandlerInterceptor;
15+
16+
import javax.servlet.http.HttpServletRequest;
17+
import javax.servlet.http.HttpServletResponse;
18+
import java.util.Collections;
19+
20+
public class TracingRequestInterceptor implements HandlerInterceptor {
21+
22+
private Logger logger = LoggerFactory.getLogger(TracingRequestInterceptor.class);
23+
24+
private Tracer tracer;
25+
private OpenTelemetry openTelemetry;
26+
27+
public TracingRequestInterceptor(OpenTelemetry openTelemetry, Tracer tracer) {
28+
this.tracer = tracer;
29+
this.openTelemetry = openTelemetry;
30+
}
31+
32+
private static final TextMapGetter<HttpServletRequest> getter =
33+
new TextMapGetter<HttpServletRequest>() {
34+
@Override
35+
public Iterable<String> keys(HttpServletRequest carrier) {
36+
return Collections.list(carrier.getHeaderNames());
37+
}
38+
39+
@Override
40+
public String get(HttpServletRequest carrier, String key) {
41+
return carrier.getHeader(key);
42+
}
43+
};
44+
45+
@Override
46+
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
47+
logger.info("handling {}", request.toString());
48+
Context context = openTelemetry.getPropagators().getTextMapPropagator().extract(Context.current(), request, getter);
49+
50+
Span span = tracer.spanBuilder(String.format("%s %s", request.getMethod(), request.getRequestURI()))
51+
.setParent(context)
52+
.setSpanKind(SpanKind.SERVER)
53+
.startSpan();
54+
55+
Scope scope = span.makeCurrent();
56+
57+
request.setAttribute("span", span);
58+
request.setAttribute("scope", scope);
59+
return HandlerInterceptor.super.preHandle(request, response, handler);
60+
}
61+
62+
@Override
63+
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
64+
Span span = (Span)request.getAttribute("span");
65+
Scope scope = (Scope)request.getAttribute("scope");
66+
67+
if (ex != null) {
68+
span.setStatus(StatusCode.ERROR);
69+
span.recordException(ex);
70+
}
71+
span.setAttribute(SemanticAttributes.HTTP_METHOD, request.getMethod());
72+
span.setAttribute(SemanticAttributes.HTTP_SCHEME, request.getScheme());
73+
span.setAttribute(SemanticAttributes.NET_HOST_NAME, request.getRemoteHost());
74+
span.setAttribute(SemanticAttributes.HTTP_TARGET, request.getRequestURI());
75+
span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, response.getStatus());
76+
77+
scope.close();
78+
span.end();
79+
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
80+
}
81+
}

0 commit comments

Comments
 (0)