diff --git a/.github/workflows/CHANGELOG.tpl.md b/.github/workflows/CHANGELOG.tpl.md new file mode 100644 index 0000000..d8f4f1e --- /dev/null +++ b/.github/workflows/CHANGELOG.tpl.md @@ -0,0 +1,3 @@ + +{{.SECTION}}### $title{{.SECTION}} +{{.COMMITS}}- $commit{{.COMMITS}} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7da90bb --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,220 @@ +name: Build and Release (Manual Run) v1.3 + +on: + workflow_dispatch: # This event allows manual triggering + +env: + THIRD_PARTY_GIT_AUTHOR_EMAIL: opensource+bot@newrelic.com + THIRD_PARTY_GIT_AUTHOR_NAME: nr-opensource-bot + +jobs: + build-and-release: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: 8 + distribution: 'temurin' + + + - name: Set Extensions Dir + id: set_ext_dir + run: | + echo "Setting Extensions Dir..." + mkdir ${HOME}/release + mkdir /tmp/to + echo "NEW_RELIC_EXTENSIONS_DIR=${HOME}/release" >> $GITHUB_ENV + + - name: Build with Gradle and verifyInstrumentation + run: | + . ./newrelic-dependencies.sh + ./gradlew clean build install verifyInstrumentation + + - name: Identify Release Type + id: define_release_type + run: | + echo "Generating changelog to check type of release..." + old_tag=$(git describe --abbrev=0 --tags 2>/dev/null) || true + if [[ -n "$old_tag" ]]; then + changelog=$(git log --pretty=format:"- %s (%h)" $old_tag..HEAD) + fi + if echo "$changelog" | grep -iqE '\bBREAKING CHANGE\b'; then + echo "RELEASE_TYPE=major" >> $GITHUB_ENV + elif echo "$changelog" | grep -iqE '\bfeat\b'; then + echo "RELEASE_TYPE=minor" >> $GITHUB_ENV + else + echo "RELEASE_TYPE=patch" >> $GITHUB_ENV + fi + + - name: Set release version + id: set_release_version + run: | + major_version=2 + minor_version=0 + patch_revision=1 + + # Retrieve the latest release tag + latest_tag=$(git describe --abbrev=0 --tags 2>/dev/null) || true + echo "LATEST_TAG=${latest_tag}" >> $GITHUB_ENV + + if [[ -n "$latest_tag" && $latest_tag == v* ]]; then + # Extract the major and minor versions from the latest tag + current_major_version=$(echo $latest_tag | cut -d'.' -f1 | sed 's/v//') + current_minor_version=$(echo $latest_tag | cut -d'.' -f2) + current_patch_revision=$(echo $latest_tag | cut -d'.' -f3) + + if [ "${{ env.RELEASE_TYPE }}" = "major" ]; then + major_version=$((current_major_version +1 )) + elif [ "${{ env.RELEASE_TYPE }}" = "minor" ]; then + minor_version=$((current_minor_version + 1)) + major_version=$((current_major_version)) + else + patch_revision=$((current_patch_revision + 1)) + minor_version=$((current_minor_version)) + major_version=$((current_major_version)) + fi + + fi + + # Set the release version environment variable + release_version="v${major_version}.${minor_version}.${patch_revision}" + echo "RELEASE_VERSION=${release_version}" >> $GITHUB_ENV + + - name: Set Tag + id: set_tag + run: echo "::set-output name=tag::${{ env.RELEASE_VERSION }}" + + - name: Set release name + id: set_release_name + run: | + repo_name="${{ github.repository }}" + sanitized_repo_name=$(echo "$repo_name" | awk -F 'newrelic-java-' '{print $2}') + echo "RELEASE_NAME=${sanitized_repo_name}-instrumentation-" >> $GITHUB_ENV + previous_tag=$(git describe --abbrev=0 --tags HEAD^ 2>/dev/null || git rev-list --max-parents=0 HEAD) + + - name: Create Archive + run: | + echo "CURRENT=${PWD}" >> $GITHUB_ENV + cd ${HOME}/release + zip -r /tmp/to/${{ env.RELEASE_NAME}}${{ steps.set_tag.outputs.tag }}.zip *.jar + cd ${{env.CURRENT}} + + + + - name: Create Release + id: create_release + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.OPENSOURCE_BOT_TOKEN }} + script: | + try { + var changelog = ``; + var tag = '' + `${{ steps.set_tag.outputs.tag }}`; + const archivePath = '/tmp/to/${{ env.RELEASE_NAME}}${{ steps.set_tag.outputs.tag }}.zip'; + var response = await github.rest.repos.createRelease({ + draft:false, + generate_release_notes:true, + name:tag, + owner:context.repo.owner, + prerelease:false, + repo:context.repo.repo, + tag_name:tag, + body:changelog + }); + + core.exportVariable('RELEASE_ID', response.data.id); + core.exportVariable('RELEASE_URL', response.data.html_url); + core.exportVariable('RELEASE_UPLOAD_URL', response.data.upload_url); + } catch (error) { + core.setFailed(error.message); + } + - name: Upload Release Artifacts + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.OPENSOURCE_BOT_TOKEN }} + with: + asset_path: /tmp/to/${{ env.RELEASE_NAME}}${{ steps.set_tag.outputs.tag }}.zip + asset_name: ${{ env.RELEASE_NAME}}${{ steps.set_tag.outputs.tag }}.zip + upload_url: ${{ env.RELEASE_UPLOAD_URL }} + asset_content_type: application/zip + + - name: "Generate release changelog" + id: github_changelog + uses: Helmisek/conventional-changelog-generator@v1.0.6-release + with: + repo-token: ${{ secrets.OPENSOURCE_BOT_TOKEN }} + commit-types: "fix:Bug Fixes,feat:Features,doc:Documentation,build:Build Upgrades,BREAKING CHANGE:Enhancements" + template-path: ".github/workflows/CHANGELOG.tpl.md" + + + - name: update CHANGELOG.md + run: | + # Content to add at the top + # Get the current date in YYYY-MM-DD format + release_date=$(date +"%Y-%m-%d") + version="## Version: [${{env.RELEASE_VERSION}}](${{ env.RELEASE_URL }}) | Created: $release_date" + content="$version${{steps.github_changelog.outputs.changelog}}" + + # Existing file + file="CHANGELOG.md" + + # Create a temporary file with the content at the top and existing content below + + echo "$content" > temp_file.txt + cat "$file" >> temp_file.txt + + # Overwrite the original file with the updated content + mv temp_file.txt "$file" + + # Commit the updated CHANGELOG.md file + git add CHANGELOG.md + git config user.email "${{ env.THIRD_PARTY_GIT_AUTHOR_EMAIL }}" + git config user.name "${{ env.THIRD_PARTY_GIT_AUTHOR_NAME }}" + git commit -m "Update Changelog for Release [skip ci]" + + # Push the changes to the remote repository + git push --quiet --set-upstream origin HEAD + + - name: Get Compare URL + run: | + compare=$(echo ${{ env.RELEASE_URL }} | sed 's/releases\/tag.*/compare/') + compareurl=$(echo "\nFull Changelog: ($compare/${{ env.LATEST_TAG }}...${{ steps.set_tag.outputs.tag }})") + echo "COMPAREURL=${compareurl}" >> $GITHUB_ENV + + - name: Update Release + id: update_release + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.OPENSOURCE_BOT_TOKEN }} + script: | + try { + + + var changelog = `${{steps.github_changelog.outputs.changelog}}` + `${{env.COMPAREURL}}` ; + var release_id = `${{env.RELEASE_ID}}`; + var tag = '' + `${{ steps.set_tag.outputs.tag }}`; + const archivePath = '/tmp/to/${{ env.RELEASE_NAME}}${{ steps.set_tag.outputs.tag }}.zip'; + var _response = await github.rest.repos.updateRelease({ + draft:false, + generate_release_notes:true, + owner:context.repo.owner, + repo: context.repo.repo, + prerelease:false, + release_id:release_id, + body:changelog + }); + + core.exportVariable('RELEASE_ID', _response.data.id); + core.exportVariable('RELEASE_URL', _response.data.html_url); + core.exportVariable('RELEASE_UPLOAD_URL', _response.data.upload_url); + } catch (error) { + core.setFailed(error.message); + } diff --git a/README.md b/README.md index 2ab3ce0..4007bae 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ # New Relic Java Instrumentation for RxJava -Instrumentation for the RxJava Framework (https://github.com/ReactiveX/RxJava). There are two sets of instrumentation, one for RxJava1 and the other for RxJava2. +Instrumentation for the RxJava Framework (https://github.com/ReactiveX/RxJava). There are three sets of instrumentation, one for RxJava1 one for RxJava2 and one for RxJava3. ## Installation @@ -38,15 +38,28 @@ The rxjava1-finder extension will find methods which return an RxJava1 object (C Tracks Completable, Flowable, Maybe, Observable and Single. For objects with onNext, the method call will be linked back to the original transaction. For all objects the link is expired and linked with the object completes or an error is recorded. The rxjava2-finder extension will find methods which return an RxJava1 object (Completable, Flowable, Maybe, Observable, Single) and include them in the transaction trace. Methods in the io.reactivex package (and subpackages) are ignored. #### RxJava2 Segments -The RxJava2 instrumentation uses a New Relic Java Agent segment (https://docs.newrelic.com/docs/agents/java-agent/async-instrumentation/java-agent-api-asynchronous-applications/#segments) to track the time from when the object is subscribed to until the object is completed or throws an error. This feature is turned on by default. To turn this feature off: +The RxJava2 instrumentation uses a New Relic Java Agent segment (https://docs.newrelic.com/docs/agents/java-agent/async-instrumentation/java-agent-api-asynchronous-applications/#segments) to track the time from when the object is subscribed to until the object is completed or throws an error. It is reported as RxJava2/*rxType*/TotalTime/*rx Class simple name* This feature is turned on by default. Additional you can choose not to track certain RxJava Object by configuring a comma separated list of the simple class names that you wish to ignore. Due to the logic used with the From methods (e.g. fromCallable) it is not possible to use segments to track from and hence they are not tracked via segments. + +To turn this feature off: 1. Edit newrelic.yml 2. Insert the following text into the file. It can be placed anywhere in the file but the preferred place is right before the **labels:** stanza. Please note that spaces matter so first two lines has two space at the beginning and the third has four spaces.   \# Used to toggle the use of segments in RxJava2 objects   RxJava2:     useSegments: false + +To ignore certain Rx objects add the rxType and simple class name to the above configuration. For example to stop using segments to track with the name RxJava2/Flowable/TotalTime/FlowableFlatMap, use the following configuration: +  RxJava2: +    useSegments: true +    Flowable: +      ignores: FlowableFlatMap + ### RxJava3 -Support for RxJava3 is the same as the RxJava2. Requires different extension jars due to changes to package names +Support for RxJava3 is the same as the RxJava2. Requires different extension jars due to changes to package names + +### Finder Extensions +There are three finder extensions included with this instrumentation, one for each RxJava version. The purpose of these extensions to to track methods outside of those in RxJava that return one of the RxJava objects. This allows you to track your methods that return an RxJava object. + ## Building Building the extension requires that Gradle is installed. To build the extension jars from source, follow these steps: diff --git a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyCallable.java b/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyCallable.java deleted file mode 100644 index ccc7e17..0000000 --- a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyCallable.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.newrelic.instrumentation.labs.test.rxjava2; - -import java.util.concurrent.Callable; - -import com.newrelic.api.agent.Trace; - -public class MyCallable implements Callable { - - private String retValue = "Blue"; - - @Override - @Trace - public String call() throws Exception { - return retValue; - } - -} diff --git a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyCompletedAction.java b/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyCompletedAction.java deleted file mode 100644 index 5173230..0000000 --- a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyCompletedAction.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.newrelic.instrumentation.labs.test.rxjava2; - -import java.util.Random; - -import com.newrelic.api.agent.Trace; - -import io.reactivex.functions.Action; - -public class MyCompletedAction implements Action { - private static Random random = new Random(); - private static int MAXUNITS = 15; - - @Override - @Trace(dispatcher=true) - public void run() throws Exception { - pauseRandomUnits(); - System.out.println("Object has completed"); - } - - private void pauseRandomUnits() { - int n = random.nextInt(MAXUNITS); - pause(n*100L); - } - - private void pause(long ms) { - if(ms > 0) { - try { - Thread.sleep(ms); - } catch (InterruptedException e) { - //e.printStackTrace(); - } - } - } - - -} diff --git a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyErrorConsumer.java b/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyErrorConsumer.java deleted file mode 100644 index 4c32145..0000000 --- a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyErrorConsumer.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.newrelic.instrumentation.labs.test.rxjava2; - -import java.util.Random; - -import com.newrelic.api.agent.Trace; - -import io.reactivex.functions.Consumer; - -public class MyErrorConsumer implements Consumer { - - private static Random random = new Random(); - private static int MAXUNITS = 15; - - @Override - @Trace(dispatcher=true) - public void accept(Throwable t) throws Exception { - System.out.println("MyErrorConsumer received error: "+t); - t.printStackTrace(); - pauseRandomUnits(); - } - - private void pauseRandomUnits() { - int n = random.nextInt(MAXUNITS); - pause(n*100L); - } - - private void pause(long ms) { - if(ms > 0) { - try { - Thread.sleep(ms); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } -} diff --git a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyStringConsumer.java b/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyStringConsumer.java deleted file mode 100644 index f0d9b84..0000000 --- a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/MyStringConsumer.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.newrelic.instrumentation.labs.test.rxjava2; - -import java.util.Random; - -import com.newrelic.api.agent.Trace; - -import io.reactivex.functions.Consumer; - -public class MyStringConsumer implements Consumer { - - private static Random random = new Random(); - private static int MAXUNITS = 15; - - @Override - @Trace(dispatcher=true) - public void accept(String t) throws Exception { - System.out.println("MyStringConsumer received: "+t); - pauseRandomUnits(); - } - - private void pauseRandomUnits() { - int n = random.nextInt(MAXUNITS); - pause(n*100L); - } - - private void pause(long ms) { - if(ms > 0) { - try { - Thread.sleep(ms); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } -} diff --git a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/RxJava2Tests.java b/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/RxJava2Tests.java deleted file mode 100644 index 985a32e..0000000 --- a/rxjava2-2.0/src/test/java/com/newrelic/instrumentation/labs/test/rxjava2/RxJava2Tests.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.newrelic.instrumentation.labs.test.rxjava2; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import com.newrelic.agent.introspec.InstrumentationTestConfig; -import com.newrelic.agent.introspec.InstrumentationTestRunner; -import com.newrelic.agent.introspec.Introspector; - -import io.reactivex.Observable; - -@RunWith(InstrumentationTestRunner.class) -//Tell the test harness which classes are part of the instrumentation module -@InstrumentationTestConfig(includePrefixes = { "io.reactivex" }) -public class RxJava2Tests { - - @Test - public void testObservable() { - System.out.println("Call to testObservable"); - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - Observable observable = Observable.fromCallable(new MyCallable()); - observable.subscribe(new MyStringConsumer(), new MyErrorConsumer(), new MyCompletedAction()); - - - - } - - public void doObservable() { - - } -}