Skip to content

Commit

Permalink
Cleanup lettuce instrumentation and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
twcrone committed Jun 7, 2022
1 parent c7ab06e commit 5de5c4f
Show file tree
Hide file tree
Showing 88 changed files with 2,152 additions and 1,288 deletions.
1 change: 1 addition & 0 deletions .github/workflows/AITs-Frameworks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
# - ejb/ejb.py
- jaxrs/jaxrs.py
# - jms/jms.py
- lettuce/lettuce.py
# - netty/netty.py
# - play/play2.py
- quartz/quartz.py
Expand Down
41 changes: 0 additions & 41 deletions .github/workflows/Java-Instrumentation-Tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@ jobs:
default-branch: "main"
# we use these in env vars for conditionals (secrets can't be used in conditionals)
AWS_KEY: ${{ secrets.aws-secret-access-key }}
GRADLE_KEY: ${{ secrets.gradle-enterprise-access-key }}
steps:
- uses: actions/checkout@v2

#NEW Install required JDKs
# Install 8
- name: Set up Java 8
# https://github.com/actions/setup-java
Expand Down Expand Up @@ -119,31 +117,10 @@ jobs:

# Is newrelicJar present in newrelic-agent/build
# TO DO: Below version number has to be dynamic
- name: Build newrelicJar
if: ${{ env.GRADLE_KEY != '' }}
env:
CI: GHA
JAVA_HOME: ${{ env.ORG_GRADLE_PROJECT_jdk8 }}
GRADLE_ENTERPRISE_ACCESS_KEY: 34.229.76.228=${{ secrets.gradle-enterprise-access-key }}
GRADLE_CACHE_USER: gha
GRADLE_CACHE_PASSWORD: ${{ secrets.gradle-cache-password }}
JRE: jre${{ matrix.jre }}
LANGUAGE: java
TEST_TYPE: instrumentation
run: |
# echo "JAVA_HOME=${ORG_GRADLE_PROJECT_jdk8}" >> $GITHUB_ENV
echo "REVIEW ANY NEW ITEMS IN WORKSPACE"
ls -la
cat settings.gradle
./gradlew $GRADLE_OPTIONS clean jar --parallel
ls -la newrelic-agent/build/
- name: Build newrelicJar w/o GE
if: ${{ env.GRADLE_KEY == '' }}
env:
JAVA_HOME: ${{ env.ORG_GRADLE_PROJECT_jdk8 }}
run: |
echo "*** NOT PUBLISHING BUILD SCANS ***"
ls -la
cat settings.gradle
./gradlew $GRADLE_OPTIONS clean jar --parallel
Expand All @@ -162,28 +139,10 @@ jobs:
rm gradle.properties
mv gradle.properties.gha gradle.properties
# Run the build with Gradle Enterprise
- name: Run instrumentation tests for Java ${{ matrix.jre }}
if: ${{ env.GRADLE_KEY != '' }}
env:
CI: GHA
JAVA_HOME: ${{ env.ORG_GRADLE_PROJECT_jdk8 }}
GRADLE_ENTERPRISE_ACCESS_KEY: 34.229.76.228=${{ secrets.gradle-enterprise-access-key }}
GRADLE_CACHE_USER: gha
GRADLE_CACHE_PASSWORD: ${{ secrets.gradle-cache-password }}
JRE: jre${{ matrix.jre }}
LANGUAGE: java
TEST_TYPE: instrumentation
run: |
./gradlew $GRADLE_OPTIONS --console=plain :instrumentation:test -Ptest${{ matrix.jre }} --continue
# Run the build without Gradle Enterprise
- name: Run instrumentation tests for Java ${{ matrix.jre }} w/o GE
if: ${{ env.GRADLE_KEY == '' }}
env:
JAVA_HOME: ${{ env.ORG_GRADLE_PROJECT_jdk8 }}
run: |
echo "*** NOT PUBLISHING BUILD SCANS OR CACHE ***"
./gradlew $GRADLE_OPTIONS --console=plain :instrumentation:test -Ptest${{ matrix.jre }} --continue
# Capture HTML build result in artifacts
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ Run an individual functional test:

The instrumentation module tests are also JUnit tests. The framework is the industry-standard JUnit dependency modified by a custom test runner and class loader that support bytecode weaving within the test without the need to fully initialize the agent. Note: fully initializing the agent is not possible when running in an uninstrumented reusable test process like an IntelliJ test subprocess or Gradle daemon. There is also an "introspector" (somewhat equivalent to a local mock collector) for test assertions.

Some of the instrumentation tests use [Testcontainers](https://www.testcontainers.org/) so you might need to have Docker [installed](https://docs.docker.com/get-docker/) to run them locally.

Instrumentation tests are located in each instrumentation module at `newrelic-java-agent/instrumentation/<INSTRUMENTATION MODULE>/src/test` and are run from the root `newrelic-java-agent` directory as follows:

Run all instrumentation module tests:
Expand Down
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ plugins {

allprojects {
apply plugin: 'idea'
apply plugin: 'eclipse'

// ignores MANIFEST.MF
normalization {
Expand Down
Binary file removed instrumentation/akka-http-core-10.2.0/.cache-tests
Binary file not shown.
8 changes: 8 additions & 0 deletions instrumentation/lettuce-4.3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Lettuce 4.3 Instrumentation
===========================

This instrumentation only supports synchronous and asynchronous API usage.

It does NOT support reactive as this version uses RxJava Observables which at
the time of this documentation, is not supported by the Java Agent. For reactive
Lettuce instrumentation, please use 5 or 6 for reactive support with Reactor Core.
24 changes: 24 additions & 0 deletions instrumentation/lettuce-4.3/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
dependencies {
implementation(project(":newrelic-weaver-api"))
implementation(project(":agent-bridge"))
implementation 'biz.paluch.redis:lettuce:4.4.1.Final'
testImplementation('org.testcontainers:testcontainers:1.17.1')
testImplementation('junit:junit:4.13.1')

}

jar {
manifest {
attributes 'Implementation-Title': 'com.newrelic.instrumentation.lettuce-4.3'
}
}

verifyInstrumentation {
passesOnly 'biz.paluch.redis:lettuce:[4.3.0,4.4.4.Final]'
excludeRegex '.*SNAPSHOT'
}

site {
title 'Lettuce 4.3'
type 'Framework'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/
package com.lambdaworks.redis;

import com.lambdaworks.redis.api.StatefulConnection;
import com.lambdaworks.redis.protocol.AsyncCommand;
import com.lambdaworks.redis.protocol.ProtocolKeyword;
import com.lambdaworks.redis.protocol.RedisCommand;
import com.newrelic.api.agent.DatastoreParameters;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Segment;
import com.newrelic.api.agent.Trace;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;
import com.nr.lettuce43.instrumentation.NRBiConsumer;

@Weave(originalName = "com.lambdaworks.redis.AbstractRedisAsyncCommands")
public abstract class AbstractRedisAsyncCommands_Instrumentation<K, V> {

public abstract StatefulConnection<K, V> getConnection();

@SuppressWarnings("unchecked")
@Trace
public <T> AsyncCommand<K, V, T> dispatch(RedisCommand<K, V, T> cmd) {
AsyncCommand<K, V, T> acmd = Weaver.callOriginal();
String collName = "?";
RedisURI uri = null;

StatefulConnection<K, V> conn = getConnection();
if (StatefulRedisConnectionImpl_Instrumentation.class.isInstance(conn)) {
StatefulRedisConnectionImpl_Instrumentation<K, V> connImpl = (StatefulRedisConnectionImpl_Instrumentation<K, V>) conn;
if (connImpl.redisURI != null) {
uri = connImpl.redisURI;
}
}
String operation = "UnknownOp";
ProtocolKeyword t = cmd.getType();
if (t != null && t.name() != null && !t.name().isEmpty()) {
operation = t.name();
}
DatastoreParameters params = null;
if (uri != null) {

params = DatastoreParameters.product("Redis").collection(collName).operation(operation)
.instance(uri.getHost(), uri.getPort()).noDatabaseName().build();
} else {
params = DatastoreParameters.product("Redis").collection(collName).operation("").noInstance()
.noDatabaseName().noSlowQuery().build();
}
Segment segment = NewRelic.getAgent().getTransaction().startSegment("Lettuce", operation);

NRBiConsumer<T> nrBiConsumer = new NRBiConsumer<T>(segment, params);
acmd.whenComplete(nrBiConsumer);
return acmd;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/
package com.lambdaworks.redis;

import com.lambdaworks.redis.api.StatefulRedisConnection;
import com.lambdaworks.redis.codec.RedisCodec;
import com.lambdaworks.redis.protocol.CommandHandler;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;

import java.util.concurrent.TimeUnit;

@SuppressWarnings("deprecation")
@Weave(originalName = "com.lambdaworks.redis.RedisClient")
public abstract class RedisClient_Instrumentation extends AbstractRedisClient {

private final RedisURI redisURI = Weaver.callOriginal();

public static RedisClient_Instrumentation create(String uri) {
return Weaver.callOriginal();
}

public abstract StatefulRedisConnection<String, String> connect();

protected <K, V> StatefulRedisConnectionImpl_Instrumentation<K, V> newStatefulRedisConnection(CommandHandler<K, V> commandHandler,
RedisCodec<K, V> codec, long timeout, TimeUnit unit) {
StatefulRedisConnectionImpl_Instrumentation<K, V> connection = Weaver.callOriginal();
connection.redisURI = redisURI;
return connection;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/
package com.lambdaworks.redis;

import com.lambdaworks.redis.api.StatefulConnection;
import com.lambdaworks.redis.codec.RedisCodec;
import com.newrelic.api.agent.weaver.NewField;
import com.newrelic.api.agent.weaver.Weave;

import java.util.concurrent.TimeUnit;

@Weave(originalName = "com.lambdaworks.redis.StatefulRedisConnectionImpl")
public abstract class StatefulRedisConnectionImpl_Instrumentation<K, V> implements StatefulConnection<K, V> {

@NewField
public RedisURI redisURI = null;

public StatefulRedisConnectionImpl_Instrumentation(RedisChannelWriter<K, V> writer, RedisCodec<K, V> codec, long timeout,
TimeUnit unit) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/
package com.nr.lettuce43.instrumentation;

import com.newrelic.api.agent.ExternalParameters;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Segment;

import java.util.function.BiConsumer;

public class NRBiConsumer<T> implements BiConsumer<T, Throwable> {

private Segment segment = null;
private ExternalParameters params = null;

public NRBiConsumer(Segment s, ExternalParameters p) {
segment = s;
params = p;
}

@Override
public void accept(T t, Throwable u) {
if (u != null) {
NewRelic.noticeError(u);
}

if (segment != null) {
if (params != null) {
segment.reportAsExternal(params);
}
segment.end();
segment = null;
} else {
if (params != null) {
NewRelic.getAgent().getTracedMethod().reportAsExternal(params);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/
package com.nr.lettuce43.instrumentation;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class StringUtils {

public static String getCSS(List<?> list) {
StringBuffer sb = new StringBuffer();
int i = 0;
int size = list.size();
for (Object obj : list) {
sb.append(obj);
if (i < size - 1) {
sb.append(',');
}
}
return sb.toString();
}

public static String getKeysAsCSS(Map<?, ?> map) {
StringBuffer sb = new StringBuffer();
int i = 0;
Set<?> keys = map.keySet();
int size = keys.size();
for (Object obj : keys) {
sb.append(obj);
if (i < size - 1) {
sb.append(',');
}
}
return sb.toString();

}

public static String getCSSFromArray(Object[] objects) {
if (objects == null || objects.length == 0) {
return " ";
}
List<Object> objectList = Arrays.asList(objects);
return getCSS(objectList);
}
}
Loading

0 comments on commit 5de5c4f

Please sign in to comment.