Skip to content

Commit ff07774

Browse files
committed
feat: introduce new, basic, instrumentation for Apache Sling
1 parent bcd693f commit ff07774

File tree

6 files changed

+282
-0
lines changed

6 files changed

+282
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
dependencies {
6+
bootstrap(project(":instrumentation:executors:bootstrap"))
7+
implementation(project(":instrumentation:servlet:servlet-3.0:javaagent"))
8+
bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap"))
9+
api(project(":instrumentation:servlet:servlet-common:javaagent"))
10+
compileOnly("javax.servlet:javax.servlet-api:3.1.0")
11+
library("org.apache.sling:org.apache.sling.api:2.0.6") // first non-incubator release
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package io.opentelemetry.javaagent.instrumentation.sling;
2+
3+
import io.opentelemetry.context.Context;
4+
import io.opentelemetry.context.Scope;
5+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
6+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
7+
import net.bytebuddy.asm.Advice;
8+
import net.bytebuddy.description.type.TypeDescription;
9+
import net.bytebuddy.matcher.ElementMatcher;
10+
import org.apache.sling.api.SlingHttpServletRequest;
11+
12+
import javax.servlet.Servlet;
13+
14+
import java.util.Deque;
15+
import java.util.LinkedList;
16+
17+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
18+
import static io.opentelemetry.javaagent.instrumentation.sling.SlingSingletons.REQUEST_ATTR_RESOLVED_SERVLET_NAME;
19+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
20+
import static net.bytebuddy.matcher.ElementMatchers.named;
21+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
22+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
23+
24+
public class ServletResolverInstrumentation implements TypeInstrumentation {
25+
26+
@Override
27+
public ElementMatcher<TypeDescription> typeMatcher() {
28+
return implementsInterface(named("org.apache.sling.api.servlets.ServletResolver"));
29+
}
30+
31+
@Override
32+
public void transform(TypeTransformer transformer) {
33+
transformer.applyAdviceToMethod(
34+
isMethod()
35+
.and(named("resolveServlet"))
36+
.and(takesArguments(1))
37+
.and(takesArgument(0, named("org.apache.sling.api.SlingHttpServletRequest"))),
38+
this.getClass().getName()+"$ResolveServletAdvice");
39+
}
40+
41+
@SuppressWarnings("unused")
42+
public static class ResolveServletAdvice {
43+
@Advice.OnMethodEnter(suppress = Throwable.class)
44+
public static void onEnter(
45+
@Advice.Argument(0) SlingHttpServletRequest request,
46+
@Advice.Local("otelContext") Context context,
47+
@Advice.Local("otelScope") Scope scope) {
48+
49+
// Context parentContext = Java8BytecodeBridge.currentContext();
50+
//
51+
// if (!helper().shouldStart(parentContext, request)) {
52+
// return;
53+
// }
54+
//
55+
// context = helper().start(parentContext, request);
56+
// scope = context.makeCurrent();
57+
}
58+
59+
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
60+
public static void onExit(
61+
@Advice.Argument(0) SlingHttpServletRequest request,
62+
@Advice.Return Servlet servlet,
63+
@Advice.Thrown Throwable throwable,
64+
@Advice.Local("otelContext") Context context,
65+
@Advice.Local("otelScope") Scope scope) {
66+
67+
// if (scope == null) {
68+
// return;
69+
// }
70+
// scope.close();
71+
72+
// TODO - copied from RequestUtil
73+
String name = null;
74+
75+
if (servlet.getServletConfig() != null) {
76+
name = servlet.getServletConfig().getServletName();
77+
}
78+
if (name == null || name.isEmpty()) {
79+
name = servlet.getServletInfo();
80+
}
81+
if (name == null || name.isEmpty()) {
82+
name = servlet.getClass().getName();
83+
}
84+
85+
@SuppressWarnings("unchecked")
86+
Deque<String> servletNames = (Deque<String>) request.getAttribute(REQUEST_ATTR_RESOLVED_SERVLET_NAME);
87+
if ( servletNames == null ) {
88+
servletNames = new LinkedList<>();
89+
request.setAttribute(REQUEST_ATTR_RESOLVED_SERVLET_NAME, servletNames);
90+
}
91+
servletNames.addLast(name);
92+
System.out.format("SLING TRACE resolved name %s for servlet %s; current stack is %s%n", name, servlet, servletNames);
93+
94+
// Span.fromContext(context).updateName(name);
95+
// HttpServerRoute.update(context, HttpServerRouteSource.CONTROLLER, name);
96+
//
97+
// helper().end(context, request, null, throwable);
98+
}
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.opentelemetry.javaagent.instrumentation.sling;
2+
3+
import com.google.auto.service.AutoService;
4+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
5+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
6+
import java.util.Arrays;
7+
import java.util.List;
8+
9+
@AutoService(InstrumentationModule.class)
10+
public class SlingInstrumentationModule extends InstrumentationModule {
11+
12+
public SlingInstrumentationModule() {
13+
super("sling", "sling-1.0");
14+
}
15+
16+
@Override
17+
public List<TypeInstrumentation> typeInstrumentations() {
18+
return Arrays.asList(new ServletResolverInstrumentation(), new SlingSafeMethodsServletInstrumentation());
19+
}
20+
21+
@Override
22+
public int order() {
23+
return -1;
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package io.opentelemetry.javaagent.instrumentation.sling;
2+
3+
import static io.opentelemetry.javaagent.instrumentation.sling.SlingSingletons.REQUEST_ATTR_RESOLVED_SERVLET_NAME;
4+
import static io.opentelemetry.javaagent.instrumentation.sling.SlingSingletons.helper;
5+
import static net.bytebuddy.matcher.ElementMatchers.named;
6+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
7+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
8+
9+
import io.opentelemetry.api.trace.Span;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.context.Scope;
12+
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRoute;
13+
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteSource;
14+
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
15+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
16+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
17+
import io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers;
18+
import net.bytebuddy.asm.Advice;
19+
import net.bytebuddy.description.type.TypeDescription;
20+
import net.bytebuddy.matcher.ElementMatcher;
21+
import org.apache.sling.api.SlingHttpServletRequest;
22+
import javax.servlet.ServletRequest;
23+
import javax.servlet.ServletResponse;
24+
import java.util.Deque;
25+
26+
public class SlingSafeMethodsServletInstrumentation implements TypeInstrumentation {
27+
28+
// TODO - classloader optimisation
29+
@Override
30+
public ElementMatcher<TypeDescription> typeMatcher() {
31+
System.out.format("SLING TRACE Got asked about typeMatcher%n");
32+
// return named("org.apache.sling.api.servlets.SlingSafeMethodsServlet");
33+
return AgentElementMatchers.implementsInterface(named("javax.servlet.Servlet"));
34+
}
35+
36+
@Override
37+
public void transform(TypeTransformer transformer) {
38+
39+
System.out.format("SLING TRACE transforming using %s%n", transformer);
40+
41+
String adviceClassName = this.getClass().getName() + "$ServiceServletAdvice";
42+
transformer.applyAdviceToMethod(
43+
named("service")
44+
.and(takesArguments(2))
45+
.and(takesArgument(0, named("javax.servlet.ServletRequest")))
46+
.and(takesArgument(1, named("javax.servlet.ServletResponse"))),
47+
adviceClassName);
48+
49+
System.out.format("SLING TRACE transformed using %s ; adviceClassName = %s %n", transformer, adviceClassName);
50+
}
51+
52+
@SuppressWarnings("unused")
53+
public static class ServiceServletAdvice {
54+
@Advice.OnMethodEnter(suppress = Throwable.class)
55+
public static void onEnter(
56+
@Advice.Argument(0) ServletRequest request,
57+
@Advice.Argument(1) ServletResponse response,
58+
@Advice.Local("otelContext") Context context,
59+
@Advice.Local("otelScope") Scope scope) {
60+
61+
System.out.format("SLING TRACE Handling request %s%n", request);
62+
63+
if ( !(request instanceof SlingHttpServletRequest) ) {
64+
return;
65+
}
66+
67+
SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
68+
69+
Context parentContext = Java8BytecodeBridge.currentContext();
70+
71+
if (!helper().shouldStart(parentContext, slingRequest)) {
72+
System.out.format("SLING TRACE should not handle %s%n", request);
73+
return;
74+
}
75+
76+
context = helper().start(parentContext, slingRequest);
77+
scope = context.makeCurrent();
78+
}
79+
80+
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
81+
public static void onExit(
82+
@Advice.Argument(0) ServletRequest request,
83+
@Advice.Argument(1) ServletResponse response,
84+
@Advice.Thrown Throwable throwable,
85+
@Advice.Local("otelContext") Context context,
86+
@Advice.Local("otelScope") Scope scope) {
87+
88+
System.out.format("SLING TRACE on exit for %s%n", request);
89+
90+
if (scope == null) {
91+
return;
92+
}
93+
scope.close();
94+
95+
SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
96+
// written by ServletResolverInstrumentation
97+
Object servletNameStack = request.getAttribute(REQUEST_ATTR_RESOLVED_SERVLET_NAME);
98+
99+
System.out.format("SLING TRACE servletName attr for %s is %s%n", request, servletNameStack);
100+
101+
if ( servletNameStack instanceof Deque<?>) {
102+
Deque<?> nameStack = (Deque<?>) servletNameStack;
103+
if ( ! nameStack.isEmpty() ) {
104+
String servletName = (String) nameStack.removeLast();
105+
Span.fromContext(context).updateName(servletName);
106+
HttpServerRoute.update(context, HttpServerRouteSource.CONTROLLER,servletName);
107+
}
108+
}
109+
helper().end(context, slingRequest, null, throwable);
110+
}
111+
}
112+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.sling;
7+
8+
import io.opentelemetry.api.GlobalOpenTelemetry;
9+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
10+
import org.apache.sling.api.SlingHttpServletRequest;
11+
12+
public final class SlingSingletons {
13+
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.sling-1.0";
14+
private static final String SPAN_NAME = "sling.request";
15+
16+
static final String REQUEST_ATTR_RESOLVED_SERVLET_NAME = INSTRUMENTATION_NAME + ".resovledServletName";
17+
private static final Instrumenter<SlingHttpServletRequest, Void>
18+
INSTRUMENTER = Instrumenter.
19+
<SlingHttpServletRequest, Void> builder(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, s -> SPAN_NAME)
20+
.buildInstrumenter();
21+
22+
/* ServletInstrumenterBuilder.<HttpServletRequest, HttpServletResponse>create()
23+
.addContextCustomizer(
24+
(context, request, attributes) -> new AppServerBridge.Builder().init(context))
25+
.build(INSTRUMENTATION_NAME, Servlet3Accessor.INSTANCE);*/
26+
27+
public static Instrumenter<SlingHttpServletRequest, Void> helper() {
28+
return INSTRUMENTER;
29+
}
30+
31+
private SlingSingletons() {}
32+
}

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ include(":instrumentation:servlet:servlet-3.0:javaagent")
484484
include(":instrumentation:servlet:servlet-3.0:javaagent-unit-tests")
485485
include(":instrumentation:servlet:servlet-5.0:javaagent")
486486
include(":instrumentation:servlet:servlet-5.0:javaagent-unit-tests")
487+
include(":instrumentation:sling:javaagent")
487488
include(":instrumentation:spark-2.3:javaagent")
488489
include(":instrumentation:spring:spring-batch-3.0:javaagent")
489490
include(":instrumentation:spring:spring-boot-actuator-autoconfigure-2.0:javaagent")

0 commit comments

Comments
 (0)