Skip to content

Commit 5f5f941

Browse files
Add subclient property interceptor support (Azure#53111)
* Add subclient property interceptor support * fix * Add test * fix test * Handle both property and method * comment * refactor * fix
1 parent f014523 commit 5f5f941

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
lines changed

sdk/core/Azure.Core.TestFramework/src/Instrumentation/InstrumentResultInterceptor.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@ public void Intercept(IInvocation invocation)
3131
var type = invocation.Method.ReturnType;
3232

3333
// We don't want to instrument generated rest clients.
34-
if ((type.Name.EndsWith("Client") && !type.Name.EndsWith("RestClient") && !type.Name.EndsWith("ExtensionClient")) ||
35-
// Generated ARM clients will have a property containing the sub-client that ends with Operations.
36-
//TODO: remove after all track2 .net mgmt libraries are updated to the new generation
37-
(invocation.Method.Name.StartsWith("get_") && type.Name.EndsWith("Operations")))
34+
if (IsInstrumentableClientType(invocation))
3835
{
3936
if (IsNullResult(invocation))
4037
return;
@@ -100,6 +97,38 @@ public void Intercept(IInvocation invocation)
10097
invocation.Proceed();
10198
}
10299

100+
private static bool IsInstrumentableClientType(IInvocation invocation)
101+
{
102+
var type = invocation.Method.ReturnType;
103+
104+
// Skip system types
105+
if (type.Namespace?.StartsWith("System.") == true)
106+
{
107+
return false;
108+
}
109+
110+
// We don't want to instrument generated rest clients or extension clients
111+
if (type.Name.EndsWith("RestClient") || type.Name.EndsWith("ExtensionClient"))
112+
{
113+
return false;
114+
}
115+
116+
if (type.Name.EndsWith("Client"))
117+
{
118+
return true;
119+
}
120+
121+
//TODO: remove after all track2 .net mgmt libraries are updated to the new generation
122+
if (invocation.Method.Name.StartsWith("get_") && type.Name.EndsWith("Operations"))
123+
{
124+
return true;
125+
}
126+
127+
// Some libraries have subclients that do not end with Client. Instrument any type that has a Pipeline property.
128+
// This is the most expensive check so we do it last.
129+
return type.GetProperty("Pipeline") != null;
130+
}
131+
103132
internal async ValueTask<T> InstrumentOperationInterceptor<T>(IInvocation invocation, Func<ValueTask<T>> innerTask)
104133
{
105134
return (T) _testBase.InstrumentOperation(typeof(T), await innerTask());

sdk/core/Azure.Core.TestFramework/src/Shared/TestClient.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ public virtual TestClient GetAnotherTestClient()
8585
}
8686
public virtual TestClientOperations SubProperty => new TestClientOperations();
8787

88+
public virtual AnotherType SubClientProperty => new AnotherType();
89+
90+
public virtual AnotherType GetAnotherType() => new AnotherType();
91+
8892
public virtual string MethodA()
8993
{
9094
using DiagnosticScope scope = _diagnostics.CreateScope($"{nameof(TestClient)}.{nameof(MethodA)}");
@@ -119,4 +123,21 @@ public virtual async Task<string> MethodBAsync()
119123
return nameof(MethodAAsync);
120124
}
121125
}
126+
127+
#pragma warning disable SA1402
128+
internal class AnotherType
129+
#pragma warning restore SA1402
130+
{
131+
public virtual HttpPipeline Pipeline { get; }
132+
133+
public virtual Task<string> MethodAsync(int i, CancellationToken cancellationToken = default)
134+
{
135+
return Task.FromResult("Async " + i + " " + cancellationToken.CanBeCanceled);
136+
}
137+
138+
public virtual string Method(int i, CancellationToken cancellationToken = default)
139+
{
140+
return "Sync " + i + " " + cancellationToken.CanBeCanceled;
141+
}
142+
}
122143
}

sdk/core/Azure.Core.TestFramework/tests/ClientTestBaseTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,28 @@ public async Task GetClientCallsAreAutoInstrumented()
156156
Assert.AreEqual(IsAsync ? "Async 123 False" : "Sync 123 False", result);
157157
}
158158

159+
[Test]
160+
public async Task SubClientPropertyWithoutClientSuffixIsAutoInstrumented()
161+
{
162+
TestClient client = InstrumentClient(new TestClient());
163+
164+
AnotherType subClient = client.SubClientProperty;
165+
var result = await subClient.MethodAsync(123);
166+
167+
Assert.AreEqual(IsAsync ? "Async 123 False" : "Sync 123 False", result);
168+
}
169+
170+
[Test]
171+
public async Task SubClientWithoutClientSuffixIsAutoInstrumented()
172+
{
173+
TestClient client = InstrumentClient(new TestClient());
174+
175+
AnotherType subClient = client.GetAnotherType();
176+
var result = await subClient.MethodAsync(123);
177+
178+
Assert.AreEqual(IsAsync ? "Async 123 False" : "Sync 123 False", result);
179+
}
180+
159181
[Test]
160182
public async Task SubClientPropertyCallsAreAutoInstrumented()
161183
{

0 commit comments

Comments
 (0)