Conversation
…ed distributed tracing in client methods Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
|
@copilot run the Generate.ps1 script |
| } | ||
|
|
||
| // Prepend activity instrumentation statement if enabled | ||
| if (Client.ActivitySourceField != null) |
There was a problem hiding this comment.
@copilot we need to add tests that validate the tracing is added for client methods and for subclient methods. We should also update the paging methods to support tracing.
There was a problem hiding this comment.
Done in 1853adb. Here's what was implemented:
Tests added (ScmMethodProviderCollectionTests.cs):
TracingIsAddedToConvenienceMethod— verifiesusing Activityis prepended to regular convenience methodsTracingIsNotAddedToConvenienceMethodWhenDisabled— verifies no tracing when disabledTracingIsAddedToSubClientConvenienceMethod— verifies sub-clients also get tracingTracingIsAddedToPagingConvenienceMethod— verifies paging methods declare activity (withoutusing)TracingInPagingCollectionDisposesActivityViaFinally— verifies theCollectionResultDefinitionhas_activityfield, accepts it in the constructor, and disposes it via try-finally inGetRawPages
Sub-client fix (ClientProvider.cs): Removed ClientOptions != null check from ActivitySourceField — sub-clients now get their own _activitySource field.
Paging fix (CollectionResultDefinition.cs, ScmMethodProviderCollection.cs): Paging convenience methods now declare Activity activity = ... (no using) and pass it to the CollectionResultDefinition constructor. The collection stores the activity in _activity and disposes it in a try-finally block in GetRawPages/GetRawPagesAsync, so the activity spans the actual HTTP calls rather than just the instantiation.
… instrumentation Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Done in 7b53cd0. |
| /// <exception cref="ClientResultException"> Service returned a non-success status code. </exception> | ||
| public virtual async Task<ClientResult<Thing>> HelloDemo2Async(CancellationToken cancellationToken = default) | ||
| { | ||
| using Activity activity = _activitySource.StartActivity("SampleTypeSpecClient.HelloDemo2", ActivityKind.Client); |
There was a problem hiding this comment.
@copilot is this all we need? We don't need a call for tracking exceptions like we have in Azure?
There was a problem hiding this comment.
The current implementation doesn't track exceptions. When an exception is thrown, the using Activity statement disposes the activity, but no error status is recorded on the span — so failed operations appear indistinguishable from successful ones in tracing backends.
The Azure pattern's scope.Failed(e) marks the span with an error status and message. The equivalent for ActivitySource would be:
using Activity activity = _activitySource.StartActivity("...", ActivityKind.Client);
try
{
return this.Method(options);
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
throw;
}For SCM clients, whether to add this depends on how useful error detection in traces is for users. If we want spans to accurately reflect failures (which I'd recommend), I can add the try-catch with SetStatus. Otherwise the current simpler approach just captures timing. Let me know if you'd like me to add it.
There was a problem hiding this comment.
Done in f719be4. Non-paging convenience methods now match the Azure pattern:
using Activity activity = _activitySource.StartActivity("SampleTypeSpecClient.SayHi", ActivityKind.Client);
try
{
ClientResult result = SayHi(headParameter, queryParameter, optionalQuery, cancellationToken.ToRequestOptions());
return ClientResult.FromValue((Thing)result, result.GetRawResponse());
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
throw;
}Note on paging methods: The GetRawPages iterator methods can only use try-finally (not try-catch). C# CS1626 prohibits yield return inside a try block that has a catch clause, so exception status tracking isn't possible in the iterator body. The activity is still disposed via the finally block when enumeration completes (successfully or with exception).
…tracing - Fix ActivitySourceField for sub-clients by removing ClientOptions != null check - Update CollectionResultDefinition to accept and dispose Activity in GetRawPages via try-finally - Update paging convenience methods to pass activity to CollectionResult (instead of using) - Add enableMethodInstrumentation param to MockHelpers.LoadMockGenerator - Add 5 tests: TracingIsAddedToConvenienceMethod, TracingIsNotAddedToConvenienceMethodWhenDisabled, TracingIsAddedToSubClientConvenienceMethod, TracingIsAddedToPagingConvenienceMethod, TracingInPagingCollectionDisposesActivityViaFinally - Regenerate Local Sample-TypeSpec test project with new tracing behavior Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
I tried running Generate.ps1 but it fails in this environment due to a .NET SDK version mismatch (
The generated files are therefore up to date — Spector stubs wouldn't show any of the instrumentation changes even if regenerated. |
…etStatus) Non-paging convenience methods now wrap the HTTP call in try-catch to mark activity spans as error when exceptions are thrown, matching the Azure SDK pattern. Paging iterator methods can only use try-finally due to C# CS1626 (yield return cannot be inside a try-catch block). Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
| } | ||
| finally | ||
| { | ||
| _activity?.Dispose(); |
There was a problem hiding this comment.
@copilot we need to actually start the activity for each request. The pattern should mirror Azure - https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/devcenter/Azure.Developer.DevCenter/src/Generated/CollectionResults/DeploymentEnvironmentsClientGetAllEnvironmentsCollectionResultOfT.cs#L64-L74
There was a problem hiding this comment.
We might need to add a parameter to the CollectionResultDefinition for the scope name.
There was a problem hiding this comment.
Done in 480bfbe. The pattern now mirrors Azure: each page request starts its own activity via a private ExecutePageRequest/ExecutePageRequestAsync helper on the collection result class.
The helper wraps the HTTP call with:
using Activity activity = _activitySource?.StartActivity("SampleTypeSpecClient.GetWithContinuationToken", ActivityKind.Client);
try
{
return ClientResult.FromResponse(_client.Pipeline.ProcessMessage(message, _options));
}
catch (Exception ex)
{
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
throw;
}The collection stores ActivitySource? (passed from the convenience method) instead of a single Activity?. GetRawPages calls ExecutePageRequest(message) for each page, which starts/stops an activity per HTTP request. This avoids the CS1626 limitation (no yield return in try-catch) since the helper is a non-iterator method.
…ttern) Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Done in 480bfbe. The generator ran successfully and all 20 collection result files in Sample-TypeSpec have been regenerated with the new per-page activity pattern ( |
Adds first-class support for ActivitySource-based distributed tracing in generated client convenience methods, enabled by default via the
generate-method-instrumentationflag.Changes Made
options.ts: Addedgenerate-method-instrumentationoption to TypeScript emitter optionsConfiguration.cs: AddedGenerateMethodInstrumentationproperty (defaults totrue)ClientProvider.cs: AddedActivitySourcestatic field to all clients (root and sub-clients) when instrumentation is enabledScmMethodProviderCollection.cs: Added distributed tracing to convenience methods:using Activity activity = _activitySource.StartActivity(...)wraps the HTTP call in a try-catch that callsactivity?.SetStatus(ActivityStatusCode.Error, ex.Message)on exception before rethrowing — matching the Azure SDK pattern_activitySourceto theCollectionResultDefinition; no activity is started in the convenience method itselfCollectionResultDefinition.cs: StoresActivitySource?(instead of a singleActivity?) and adds a privateExecutePageRequest/ExecutePageRequestAsynchelper method. Each page HTTP request starts its own activity via this helper, which wraps the pipeline call in a try-catch withactivity?.SetStatus(ActivityStatusCode.Error, ex.Message)on failure — matching the Azure DevCenter per-page tracing pattern. This avoids the CS1626 restriction (noyield returninside try-catch) since the helper is a non-iterator method.MockHelpers.cs: AddedenableMethodInstrumentationparameter and disabled instrumentation by default in tests (viaConfiguration.json) to avoid updating all existing test snapshotsScmMethodProviderCollectionTests.csvalidating tracing for regular client methods, sub-client methods, and paging methods (including per-page activity start/stop inExecutePageRequestand try-catch withSetStatus)_activitySourcefield; collection result classes updated with_activitySourcefield andExecutePageRequesthelper; paging convenience methods updated to pass_activitySourceto the collectionTesting
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.