Skip to content

Commit 9ca5669

Browse files
authored
feat(matchexpressions): expressions can reference target JFR event type IDs (#735)
1 parent 29cfed3 commit 9ca5669

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

src/main/java/io/cryostat/expressions/MatchExpressionEvaluator.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,18 @@
2323
import java.util.concurrent.CompletionException;
2424
import java.util.stream.Collectors;
2525

26+
import io.cryostat.events.SerializableEventTypeInfo;
2627
import io.cryostat.expressions.MatchExpression.ExpressionEvent;
2728
import io.cryostat.targets.Target;
2829
import io.cryostat.targets.Target.Annotations;
2930
import io.cryostat.targets.Target.TargetDiscovery;
31+
import io.cryostat.targets.TargetConnectionManager;
3032

3133
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
3234
import io.quarkus.cache.CacheManager;
3335
import io.quarkus.cache.CacheResult;
3436
import io.quarkus.cache.CompositeCacheKey;
37+
import io.quarkus.logging.Log;
3538
import io.quarkus.vertx.ConsumeEvent;
3639
import jakarta.annotation.Nullable;
3740
import jakarta.enterprise.context.ApplicationScoped;
@@ -42,7 +45,12 @@
4245
import jdk.jfr.Label;
4346
import jdk.jfr.Name;
4447
import org.jboss.logging.Logger;
48+
import org.projectnessie.cel.EnvOption;
49+
import org.projectnessie.cel.Library;
50+
import org.projectnessie.cel.ProgramOption;
4551
import org.projectnessie.cel.checker.Decls;
52+
import org.projectnessie.cel.common.types.ListT;
53+
import org.projectnessie.cel.interpreter.functions.Overload;
4654
import org.projectnessie.cel.tools.Script;
4755
import org.projectnessie.cel.tools.ScriptCreateException;
4856
import org.projectnessie.cel.tools.ScriptException;
@@ -56,6 +64,7 @@ public class MatchExpressionEvaluator {
5664
@Inject ScriptHost scriptHost;
5765
@Inject Logger logger;
5866
@Inject CacheManager cacheManager;
67+
@Inject TargetConnectionManager connectionManager;
5968

6069
@ConsumeEvent(value = MatchExpression.EXPRESSION_ADDRESS, blocking = true)
6170
void onMessage(ExpressionEvent event) {
@@ -104,6 +113,7 @@ Script createScript(String matchExpression) throws ScriptCreateException {
104113
Decls.newVar(
105114
"target",
106115
Decls.newObjectType(SimplifiedTarget.class.getName())))
116+
.withLibraries(List.of(new EventTypesLibrary(connectionManager)))
107117
.build();
108118
} finally {
109119
evt.end();
@@ -221,6 +231,7 @@ public static class ScriptCreation extends Event {}
221231
* expression-relevant fields exposed, connection URI exposed as a String, etc.
222232
*/
223233
private static record SimplifiedTarget(
234+
long id,
224235
boolean agent,
225236
String connectUrl,
226237
String alias,
@@ -240,6 +251,7 @@ private static record SimplifiedTarget(
240251

241252
static SimplifiedTarget from(Target target) {
242253
return new SimplifiedTarget(
254+
target.id,
243255
target.isAgent(),
244256
target.connectUrl.toString(),
245257
target.alias,
@@ -248,4 +260,57 @@ static SimplifiedTarget from(Target target) {
248260
target.annotations);
249261
}
250262
}
263+
264+
static class EventTypesLibrary implements Library {
265+
266+
private final TargetConnectionManager connectionManager;
267+
268+
EventTypesLibrary(TargetConnectionManager connectionManager) {
269+
this.connectionManager = connectionManager;
270+
}
271+
272+
@Override
273+
public List<EnvOption> getCompileOptions() {
274+
return List.of(
275+
EnvOption.declarations(
276+
Decls.newFunction(
277+
"jfrEventTypeIds",
278+
Decls.newOverload(
279+
"jfrEventTypeIds_void",
280+
List.of(
281+
Decls.newObjectType(
282+
SimplifiedTarget.class.getName())),
283+
Decls.newListType(Decls.String)))));
284+
}
285+
286+
@Override
287+
public List<ProgramOption> getProgramOptions() {
288+
return List.of(
289+
ProgramOption.functions(
290+
Overload.unary(
291+
"jfrEventTypeIds",
292+
(arg) ->
293+
ListT.newStringArrayList(
294+
getJfrEventTypeIds(
295+
(SimplifiedTarget) arg.value())))));
296+
}
297+
298+
private String[] getJfrEventTypeIds(SimplifiedTarget st) {
299+
Target target = Target.find("id", st.id()).singleResult();
300+
try {
301+
return connectionManager.executeConnectedTask(
302+
target,
303+
connection ->
304+
connection.getService().getAvailableEventTypes().stream()
305+
.map(SerializableEventTypeInfo::fromEventTypeInfo)
306+
.map(SerializableEventTypeInfo::typeId)
307+
.distinct()
308+
.toList()
309+
.toArray(new String[0]));
310+
} catch (Exception e) {
311+
Log.error(e);
312+
return new String[0];
313+
}
314+
}
315+
}
251316
}

src/test/java/io/cryostat/expressions/MatchExpressionsTest.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,18 @@ public void testPostWithoutTargets() {
7272
}
7373

7474
@ParameterizedTest
75-
@CsvSource(value = {"true, 200, true", "false, 200, false", "this is garbage, 400, false"})
75+
@CsvSource(
76+
delimiter = '|',
77+
value = {
78+
"true | 200 | true",
79+
"false | 200 | false",
80+
"this is garbage | 400 | false",
81+
"target.alias == 'selftest' | 200 | true",
82+
"target.alias == '' | 200 | false",
83+
"jfrEventTypeIds(target).exists(x, x.contains('jdk')) | 200 | true",
84+
"jfrEventTypeIds(target).exists(t, t.contains('nonsense')) | 200 | false",
85+
"jfrEventTypeIds(target).exists(x, t.contains('wrong binding')) | 400 | false",
86+
})
7687
public void testExpressionTest(String expr, int status, boolean expectTargets) {
7788
int id = defineSelfCustomTarget();
7889

0 commit comments

Comments
 (0)