Skip to content

Commit

Permalink
feat: instruments com.twitter.util-stats
Browse files Browse the repository at this point in the history
  • Loading branch information
dmarkwat committed Jul 7, 2024
1 parent ae704b9 commit ad6630e
Show file tree
Hide file tree
Showing 9 changed files with 1,031 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
plugins {
id("otel.javaagent-instrumentation")
id("otel.scala-conventions")
}

muzzle {
pass {
group.set("com.twitter")
module.set("util-stats_2.12")
versions.set("[23.11.0,]")
}

pass {
group.set("com.twitter")
module.set("util-stats_2.13")
versions.set("[23.11.0,]")
}
}

val twitterUtilVersion = "23.11.0"
val scalaVersion = "2.13.10"

val scalaMinor = Regex("""^([0-9]+\.[0-9]+)\.?.*$""").find(scalaVersion)!!.run {
val (minorVersion) = this.destructured
minorVersion
}

val scalified = fun(pack: String): String {
return "${pack}_$scalaMinor"
}

dependencies {
bootstrap(project(":instrumentation:executors:bootstrap"))

compileOnly("com.google.auto.value:auto-value-annotations")
annotationProcessor("com.google.auto.value:auto-value")

library("${scalified("com.twitter:util-stats")}:$twitterUtilVersion")

testImplementation("${scalified("com.twitter:finagle-http")}:$twitterUtilVersion")
// get all the metric services loaded
testImplementation("${scalified("com.twitter:finagle-stats")}:$twitterUtilVersion")

// should wire netty contexts
// testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent"))
}

tasks {
test {
jvmArgs("-Dotel.instrumentation.twitter-util-stats.stats-receiver.mode=additive")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.opentelemetry.javaagent.instrumentation.twitterutilstats.v23_11;

import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer;
import static net.bytebuddy.matcher.ElementMatchers.named;

import com.twitter.finagle.stats.BroadcastStatsReceiver;
import com.twitter.finagle.stats.StatsReceiver;
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class LoadedStatsReceiverInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
// $ => we want the scala object, not the class
return named("com.twitter.finagle.stats.LoadedStatsReceiver$");
}

@Override
public void transform(TypeTransformer transformer) {
// perform this on the class initializer bc LoadedStatsReceiver::self is a var, not a val;
// we don't want to wrap this every time, and we need to respect behavior when someone sets it
transformer.applyAdviceToMethod(
isTypeInitializer(), LoadedStatsReceiverInstrumentation.class.getName() + "$ClinitAdvice");
}

@SuppressWarnings("unused")
public static class ClinitAdvice {

@Advice.OnMethodExit(suppress = Throwable.class)
public static void methodEnter(
@Advice.FieldValue(value = "self", readOnly = false) StatsReceiver self) {
String mode =
AgentInstrumentationConfig.get()
.getString("otel.instrumentation.twitter-util-stats.metrics.mode", "additive");
List<StatsReceiver> sr;
if (Objects.equals(mode, "additive")) {
sr = Arrays.asList(new OtelStatsReceiverProxy(), self);

} else {
sr = Collections.singletonList(new OtelStatsReceiverProxy());
}
// emulate the original invocation to avoid downstream side effects;
// iow make no assumptions about how BroadcastStatsReceiver behaves
self =
BroadcastStatsReceiver.apply(
scala.jdk.CollectionConverters.ListHasAsScala(sr).asScala().toSeq());
}
}
}
Loading

0 comments on commit ad6630e

Please sign in to comment.