diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/Instrumentation.xml b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/Instrumentation.xml
index ff57986f1d..c4568da3ca 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/Instrumentation.xml
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/Instrumentation.xml
@@ -12,6 +12,9 @@ SPDX-License-Identifier: Apache-2.0
+
+
+
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/README.md b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/README.md
new file mode 100644
index 0000000000..ced3c1a943
--- /dev/null
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/README.md
@@ -0,0 +1,5 @@
+This is the wrapper and instrumentation project for version 8.0.0 and higher of [MassTransit](https://masstransit.io/).
+
+The code in this project is nearly identical to that in the `MassTransitLegacy` project, which is for MassTransit v7. However, different MassTransit types are referenced
+in the two projects. The team decided at the time this instrumentation was introduced that eliminating the duplicate code would make things far less readable,
+and that code duplication was the lesser of two evils.
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/TransportConfigWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/TransportConfigWrapper.cs
index 34ffa7252b..3c65cffe08 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/TransportConfigWrapper.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransit/TransportConfigWrapper.cs
@@ -22,6 +22,8 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
{
// This will be run for each bus. Each bus gets one transport.
// We can support more than one transport with this setup.
+ // Note that there are two instrumentation points that can get us here, and the first parameter is different for both,
+ // but they both implement IBusFactoryConfigurator, which is what we need
var configurator = instrumentedMethodCall.MethodCall.MethodArguments.ExtractNotNullAs(0);
var spec = new NewRelicPipeSpecification(agent);
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/Instrumentation.xml b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/Instrumentation.xml
index f3ebecb3ec..cf00b0a35c 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/Instrumentation.xml
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/Instrumentation.xml
@@ -9,9 +9,18 @@ SPDX-License-Identifier: Apache-2.0
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/README.md b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/README.md
new file mode 100644
index 0000000000..96a3b02c57
--- /dev/null
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/README.md
@@ -0,0 +1,5 @@
+This is the wrapper and instrumentation project for version 7.x of [MassTransit](https://masstransit.io/).
+
+The code in this project is nearly identical to that in the `MassTransit` project, which is for MassTransit v8. However, different MassTransit types are referenced
+in the two projects. The team decided at the time this instrumentation was introduced that eliminating the duplicate code would make things far less readable,
+and that code duplication was the lesser of two evils.
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/TransportConfigLegacyWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/TransportConfigLegacyWrapper.cs
index df42f021ce..845d574fa3 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/TransportConfigLegacyWrapper.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/MassTransitLegacy/TransportConfigLegacyWrapper.cs
@@ -22,6 +22,8 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
{
// This will be run for each bus. Each bus gets one transport.
// We can support more than one transport with this setup.
+ // Note that there are two instrumentation points that can get us here, and the first parameter is different for both,
+ // but they both implement IBusFactoryConfigurator, which is what we need
var configurator = instrumentedMethodCall.MethodCall.MethodArguments.ExtractNotNullAs(0);
var spec = new NewRelicPipeSpecification(agent);
diff --git a/tests/Agent/IntegrationTests/IntegrationTestHelpers/Assertions.cs b/tests/Agent/IntegrationTests/IntegrationTestHelpers/Assertions.cs
index fb8adc6963..0b3496cc2b 100644
--- a/tests/Agent/IntegrationTests/IntegrationTestHelpers/Assertions.cs
+++ b/tests/Agent/IntegrationTests/IntegrationTestHelpers/Assertions.cs
@@ -573,7 +573,9 @@ public static Metric TryFindMetric(ExpectedMetric expectedMetric, IEnumerable TryFindMetrics(ExpectedMetric expectedMetric, IEnumer
var foundMetrics = actualMetrics
.Where(actualMetric => (expectedMetric.IsRegexName && Regex.IsMatch(actualMetric.MetricSpec.Name, expectedMetric.metricName)) ||
(!expectedMetric.IsRegexName && expectedMetric.metricName == actualMetric.MetricSpec.Name))
- .Where(actualMetric => expectedMetric.metricScope == actualMetric.MetricSpec.Scope)
+ .Where(actualMetric => (expectedMetric.IsRegexScope && Regex.IsMatch(actualMetric.MetricSpec.Scope, expectedMetric.metricScope)) ||
+ (!expectedMetric.IsRegexScope && expectedMetric.metricScope == actualMetric.MetricSpec.Scope))
.ToList();
return foundMetrics;
@@ -1113,10 +1116,11 @@ public class ExpectedMetric
public decimal? callCount = null;
public decimal? CallCountAllHarvests = null;
public bool IsRegexName = false;
+ public bool IsRegexScope = false;
public override string ToString()
{
- return $"{{ metricName: {metricName} metricScope: {metricScope}, IsRegexName: {IsRegexName}, callCount: {callCount}, CallCountAllHarvests: {CallCountAllHarvests} }}";
+ return $"{{ metricName: {metricName} metricScope: {metricScope}, IsRegexName: {IsRegexName}, IsRegexScope: {IsRegexScope}, callCount: {callCount}, CallCountAllHarvests: {CallCountAllHarvests} }}";
}
}
diff --git a/tests/Agent/IntegrationTests/IntegrationTests/MassTransit/MassTransitTests.cs b/tests/Agent/IntegrationTests/IntegrationTests/MassTransit/MassTransitTests.cs
new file mode 100644
index 0000000000..1e1075b3b4
--- /dev/null
+++ b/tests/Agent/IntegrationTests/IntegrationTests/MassTransit/MassTransitTests.cs
@@ -0,0 +1,161 @@
+// Copyright 2020 New Relic, Inc. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using System.Collections.Generic;
+using System.Linq;
+using NewRelic.Agent.IntegrationTestHelpers;
+using Xunit;
+using Xunit.Abstractions;
+using System;
+using NewRelic.Agent.IntegrationTestHelpers.RemoteServiceFixtures;
+
+namespace NewRelic.Agent.IntegrationTests.MassTransit
+{
+ public abstract class MassTransitTestsBase : NewRelicIntegrationTest
+ where TFixture : ConsoleDynamicMethodFixture
+ {
+ private readonly TFixture _fixture;
+
+ public MassTransitTestsBase(TFixture fixture, ITestOutputHelper output, bool useStartBus) : base(fixture)
+ {
+ _fixture = fixture;
+ _fixture.SetTimeout(TimeSpan.FromMinutes(2));
+ _fixture.TestLogger = output;
+
+ if (useStartBus)
+ {
+ _fixture.AddCommand($"MassTransitExerciser StartBus");
+ }
+ else
+ {
+ _fixture.AddCommand("MassTransitExerciser StartHost");
+ }
+ _fixture.AddCommand("MassTransitExerciser Publish publishedMessageOne");
+ _fixture.AddCommand("MassTransitExerciser Publish publishedMessageTwo");
+ _fixture.AddCommand("MassTransitExerciser Send sentMessageOne");
+ _fixture.AddCommand("MassTransitExerciser Send sentMessageTwo");
+
+ if (useStartBus)
+ {
+ _fixture.AddCommand($"MassTransitExerciser StopBus");
+ }
+ else
+ {
+ _fixture.AddCommand("MassTransitExerciser StopHost");
+ }
+
+ _fixture.AddActions
+ (
+ setupConfiguration: () =>
+ {
+ var configModifier = new NewRelicConfigModifier(fixture.DestinationNewRelicConfigFilePath);
+ configModifier.ForceTransactionTraces();
+ configModifier.SetLogLevel("finest");
+ },
+ exerciseApplication: () =>
+ {
+ _fixture.AgentLog.WaitForLogLine(AgentLogBase.MetricDataLogLineRegex, TimeSpan.FromMinutes(2));
+ }
+ );
+
+ _fixture.Initialize();
+ }
+
+ [Fact]
+ public void Test()
+ {
+
+ var massTransitMetricNameRegexBase = @"MessageBroker\/MassTransit\/Queue\/";
+ var queueNameRegex = @"Named\/(.{26})"; // The auto-generated in-memory queue names have 26 chars
+ var massTransitProduceMetricNameRegex = massTransitMetricNameRegexBase + @"Produce\/" + queueNameRegex;
+ var massTransitConsumeMetricNameRegex = massTransitMetricNameRegexBase + @"Consume\/" + queueNameRegex;
+
+ var metrics = _fixture.AgentLog.GetMetrics().ToList();
+
+ var expectedMetrics = new List
+ {
+ new Assertions.ExpectedMetric { metricName = massTransitConsumeMetricNameRegex, callCount = 4, IsRegexName = true},
+ new Assertions.ExpectedMetric { metricName = massTransitProduceMetricNameRegex, callCount = 4, IsRegexName = true},
+
+ new Assertions.ExpectedMetric { metricName = massTransitConsumeMetricNameRegex, callCount = 4, IsRegexName = true, metricScope = @"OtherTransaction\/Message\/MassTransit\/Queue\/" + queueNameRegex, IsRegexScope = true},
+ new Assertions.ExpectedMetric { metricName = massTransitProduceMetricNameRegex, callCount = 2, IsRegexName = true, metricScope = "OtherTransaction/Custom/MultiFunctionApplicationHelpers.NetStandardLibraries.MassTransitExerciser/Publish"},
+ new Assertions.ExpectedMetric { metricName = massTransitProduceMetricNameRegex, callCount = 2, IsRegexName = true, metricScope = "OtherTransaction/Custom/MultiFunctionApplicationHelpers.NetStandardLibraries.MassTransitExerciser/Send"},
+ };
+
+ Assertions.MetricsExist(expectedMetrics, metrics);
+
+ var transactionEvent = _fixture.AgentLog.TryGetTransactionEvent($"OtherTransaction/Custom/MultiFunctionApplicationHelpers.NetStandardLibraries.MassTransitExerciser/Publish");
+
+ Assert.NotNull( transactionEvent );
+ }
+ }
+
+ // Tests using StartHost (hosted service configuration method)
+ [NetFrameworkTest]
+ public class MassTransitTests_StartHost_FW462 : MassTransitTestsBase
+ {
+ public MassTransitTests_StartHost_FW462(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output)
+ : base(fixture, output, false)
+ {
+ }
+ }
+ [NetFrameworkTest]
+ public class MassTransitTests_StartHost_FWLatest : MassTransitTestsBase
+ {
+ public MassTransitTests_StartHost_FWLatest(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output)
+ : base(fixture, output, false)
+ {
+ }
+ }
+ [NetCoreTest]
+ public class MassTransitTests_StartHost_Core60 : MassTransitTestsBase
+ {
+ public MassTransitTests_StartHost_Core60(ConsoleDynamicMethodFixtureCore60 fixture, ITestOutputHelper output)
+ : base(fixture, output, false)
+ {
+ }
+ }
+ [NetCoreTest]
+ public class MassTransitTests_StartHost_CoreLatest : MassTransitTestsBase
+ {
+ public MassTransitTests_StartHost_CoreLatest(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output)
+ : base(fixture, output, false)
+ {
+ }
+ }
+
+ // Tests using StartBus (bus factory configuration method)
+ [NetFrameworkTest]
+ public class MassTransitTests_StartBus_FW462 : MassTransitTestsBase
+ {
+ public MassTransitTests_StartBus_FW462(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output)
+ : base(fixture, output, true)
+ {
+ }
+ }
+ [NetFrameworkTest]
+ public class MassTransitTests_StartBus_FWLatest : MassTransitTestsBase
+ {
+ public MassTransitTests_StartBus_FWLatest(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output)
+ : base(fixture, output, true)
+ {
+ }
+ }
+ [NetCoreTest]
+ public class MassTransitTests_StartBus_Core60 : MassTransitTestsBase
+ {
+ public MassTransitTests_StartBus_Core60(ConsoleDynamicMethodFixtureCore60 fixture, ITestOutputHelper output)
+ : base(fixture, output, true)
+ {
+ }
+ }
+ [NetCoreTest]
+ public class MassTransitTests_StartBus_CoreLatest : MassTransitTestsBase
+ {
+ public MassTransitTests_StartBus_CoreLatest(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output)
+ : base(fixture, output, true)
+ {
+ }
+ }
+
+}
diff --git a/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/MultiFunctionApplicationHelpers.csproj b/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/MultiFunctionApplicationHelpers.csproj
index f3564ea268..691dddac80 100644
--- a/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/MultiFunctionApplicationHelpers.csproj
+++ b/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/MultiFunctionApplicationHelpers.csproj
@@ -262,6 +262,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+