Skip to content

Commit 13b3069

Browse files
authored
Add kamon-opensearch (#1324)
* add: kamon instrumentation for opensearch * refactor: use opensearch for testing
1 parent 6ad4fcc commit 13b3069

File tree

10 files changed

+382
-0
lines changed

10 files changed

+382
-0
lines changed

build.sbt

+19
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ val instrumentationProjects = Seq[ProjectReference](
126126
`kamon-mongo-legacy`,
127127
`kamon-cassandra`,
128128
`kamon-elasticsearch`,
129+
`kamon-opensearch`,
129130
`kamon-spring`,
130131
`kamon-annotation`,
131132
`kamon-annotation-api`,
@@ -404,6 +405,22 @@ lazy val `kamon-elasticsearch` = (project in file("instrumentation/kamon-elastic
404405
)
405406
).dependsOn(`kamon-core`, `kamon-instrumentation-common`, `kamon-testkit` % "test")
406407

408+
lazy val `kamon-opensearch` = (project in file("instrumentation/kamon-opensearch"))
409+
.disablePlugins(AssemblyPlugin)
410+
.enablePlugins(JavaAgent)
411+
.settings(instrumentationSettings)
412+
.settings(
413+
Test / run / fork := true,
414+
libraryDependencies ++= Seq(
415+
kanelaAgent % "provided",
416+
"org.opensearch.client" % "opensearch-rest-client" % "1.3.14" % "provided",
417+
"org.opensearch.client" % "opensearch-rest-high-level-client" % "1.3.14" % "provided",
418+
scalatest % "test",
419+
logbackClassic % "test",
420+
"com.dimafeng" %% "testcontainers-scala" % "0.41.0" % "test",
421+
)
422+
).dependsOn(`kamon-core`, `kamon-instrumentation-common`, `kamon-testkit` % "test")
423+
407424
lazy val `kamon-spring` = (project in file("instrumentation/kamon-spring"))
408425
.disablePlugins(AssemblyPlugin)
409426
.enablePlugins(JavaAgent)
@@ -1058,6 +1075,7 @@ lazy val `kamon-bundle-dependencies-all` = (project in file("bundle/kamon-bundle
10581075
`kamon-mongo-legacy`,
10591076
`kamon-cassandra`,
10601077
`kamon-elasticsearch`,
1078+
`kamon-opensearch`,
10611079
`kamon-spring`,
10621080
`kamon-annotation`,
10631081
`kamon-annotation-api`,
@@ -1117,6 +1135,7 @@ lazy val `kamon-bundle-dependencies-3` = (project in file("bundle/kamon-bundle-d
11171135
`kamon-jdbc`,
11181136
`kamon-kafka`,
11191137
`kamon-elasticsearch`,
1138+
`kamon-opensearch`,
11201139
`kamon-spring`,
11211140
`kamon-annotation`,
11221141
`kamon-annotation-api`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2013-2020 The Kamon Project <https://kamon.io>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kamon.instrumentation.opensearch;
18+
19+
import kamon.Kamon;
20+
import kamon.trace.Span;
21+
import kanela.agent.libs.net.bytebuddy.asm.Advice;
22+
import org.opensearch.client.Request;
23+
import org.opensearch.client.ResponseListener;
24+
25+
public class AsyncOpensearchRestClientInstrumentation {
26+
27+
@Advice.OnMethodEnter
28+
public static void enter(
29+
@Advice.Argument(0) Request request,
30+
@Advice.Argument(value = 1, readOnly = false) ResponseListener responseListener) {
31+
final String operationName =
32+
RequestNameConverter.convert(
33+
HighLevelOpensearchClientInstrumentation.requestClassName.get(),
34+
"AsyncRequest");
35+
36+
Span span = Kamon.clientSpanBuilder(operationName, "opensearch.client")
37+
.tag("opensearch.http.endpoint", request.getEndpoint())
38+
.tag("opensearch.http.method", request.getMethod())
39+
.start();
40+
41+
RequestSizeHistogram.record(request.getEntity());
42+
responseListener = new InstrumentedListener(responseListener, span);
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2013-2020 The Kamon Project <https://kamon.io>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kamon.instrumentation.opensearch;
18+
19+
import kanela.agent.libs.net.bytebuddy.asm.Advice;
20+
21+
public class HighLevelOpensearchClientInstrumentation {
22+
public static final ThreadLocal<String> requestClassName = new ThreadLocal<>();
23+
24+
@Advice.OnMethodEnter
25+
public static <Req> void enter(
26+
@Advice.Argument(0) Req request) {
27+
requestClassName.set(request.getClass().getSimpleName());
28+
}
29+
30+
@Advice.OnMethodExit
31+
public static void exit() {
32+
requestClassName.remove();
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2013-2020 The Kamon Project <https://kamon.io>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kamon.instrumentation.opensearch;
18+
19+
import kamon.Kamon;
20+
import kamon.trace.Span;
21+
import kanela.agent.libs.net.bytebuddy.asm.Advice;
22+
import org.opensearch.client.Request;
23+
24+
public class SyncOpensearchRestClientInstrumentation {
25+
26+
@Advice.OnMethodEnter
27+
public static void enter(
28+
@Advice.Argument(0) Request request,
29+
@Advice.Local("span") Span span) {
30+
final String operationName = RequestNameConverter.convert(
31+
HighLevelOpensearchClientInstrumentation.requestClassName.get(),
32+
"SyncRequest");
33+
34+
RequestSizeHistogram.record(request.getEntity());
35+
span = Kamon.clientSpanBuilder(operationName, "opensearch.client")
36+
.tag("opensearch.http.endpoint", request.getEndpoint())
37+
.tag("opensearch.http.method", request.getMethod())
38+
.start();
39+
}
40+
41+
@Advice.OnMethodExit
42+
public static void exit(
43+
@Advice.Local("span") Span span) {
44+
span.finish();
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# ================================================== #
2+
# kamon Opensearch client reference configuration #
3+
# ================================================== #
4+
5+
kamon.instrumentation.opensearch {
6+
}
7+
8+
kanela {
9+
modules {
10+
opensearch-driver {
11+
12+
name = "Opensearch Client"
13+
description = "Provides tracing of client calls made with the official Opensearch Client library."
14+
instrumentations = [
15+
"kamon.instrumentation.opensearch.OSInstrumentation"
16+
]
17+
18+
within = [
19+
"org.opensearch.client..*"
20+
]
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2013-2020 The Kamon Project <https://kamon.io>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kamon.instrumentation.opensearch
18+
19+
import kamon.Kamon
20+
import kamon.trace.Span
21+
import kanela.agent.api.instrumentation.InstrumentationBuilder
22+
import org.apache.http.HttpEntity
23+
import org.opensearch.client.{Response, ResponseListener}
24+
25+
class OSInstrumentation extends InstrumentationBuilder {
26+
onType("org.opensearch.client.RestClient")
27+
.advise(method("performRequestAsync").and(takesArguments(2)), classOf[AsyncOpensearchRestClientInstrumentation])
28+
.advise(method("performRequest").and(takesArguments(1)), classOf[SyncOpensearchRestClientInstrumentation])
29+
30+
onType("org.opensearch.client.RestHighLevelClient")
31+
.advise(method("internalPerformRequest").and(takesArguments(5)), classOf[HighLevelOpensearchClientInstrumentation])
32+
.advise(method("internalPerformRequestAsync").and(takesArguments(6)), classOf[HighLevelOpensearchClientInstrumentation])
33+
}
34+
35+
class InstrumentedListener(inner: ResponseListener, span: Span) extends ResponseListener {
36+
override def onSuccess(response: Response): Unit = {
37+
span.finish()
38+
inner.onSuccess(response)
39+
}
40+
41+
override def onFailure(exception: Exception): Unit = {
42+
span.fail(exception)
43+
inner.onFailure(exception)
44+
}
45+
}
46+
47+
object RequestSizeHistogram {
48+
private val histogram = Kamon.histogram("opensearch.request.size").withoutTags()
49+
50+
def record(entity: HttpEntity): Unit = {
51+
Option(entity)
52+
.map(_.getContentLength)
53+
.filter(_ >= 0)
54+
.foreach(histogram.record)
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2013-2020 The Kamon Project <https://kamon.io>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kamon.instrumentation.opensearch
18+
19+
object RequestNameConverter {
20+
def convert(className: String, fallback: String): String = {
21+
val requestType = Option(className).getOrElse(fallback)
22+
s"opensearch/$requestType"
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kamon.instrumentation.opensearch {
2+
}
3+
kanela {
4+
# debug-mode = true
5+
# log-level = "DEBUG"
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<configuration>
2+
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
3+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
4+
<encoder>
5+
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
6+
</encoder>
7+
</appender>
8+
9+
<logger name="org.hyperic" level="OFF"/>
10+
<logger name="org.apache" level="OFF"/>
11+
<logger name="org.testcontainers" level="OFF"/>
12+
13+
<root level="INFO">
14+
<appender-ref ref="STDOUT"/>
15+
</root>
16+
</configuration>

0 commit comments

Comments
 (0)