|
4 | 4 |
|
5 | 5 | using System;
|
6 | 6 | using System.Collections.Generic;
|
| 7 | +using System.Diagnostics; |
7 | 8 | using System.Linq;
|
8 | 9 | using System.Runtime.CompilerServices;
|
9 | 10 | using System.Text.Json;
|
@@ -165,11 +166,11 @@ private ValueTask<TResponse> DoRequestCoreAsync<TRequest, TResponse, TRequestPar
|
165 | 166 | ValueTask<TResponse> SendRequest()
|
166 | 167 | {
|
167 | 168 | var (endpointPath, resolvedRouteValues, postData) = PrepareRequest<TRequest, TRequestParameters>(request);
|
168 |
| - var openTelemetryData = PrepareOpenTelemetryData<TRequest, TRequestParameters>(request, resolvedRouteValues); |
| 169 | + var openTelemetryDataMutator = GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(request, resolvedRouteValues); |
169 | 170 |
|
170 | 171 | return isAsync
|
171 |
| - ? new ValueTask<TResponse>(_transport.RequestAsync<TResponse>(endpointPath, postData, in openTelemetryData, request.RequestConfig, cancellationToken)) |
172 |
| - : new ValueTask<TResponse>(_transport.Request<TResponse>(endpointPath, postData, in openTelemetryData, request.RequestConfig)); |
| 172 | + ? new ValueTask<TResponse>(_transport.RequestAsync<TResponse>(endpointPath, postData, openTelemetryDataMutator, request.RequestConfig, cancellationToken)) |
| 173 | + : new ValueTask<TResponse>(_transport.Request<TResponse>(endpointPath, postData, openTelemetryDataMutator, request.RequestConfig)); |
173 | 174 | }
|
174 | 175 |
|
175 | 176 | async ValueTask<TResponse> SendRequestWithProductCheck()
|
@@ -211,19 +212,19 @@ async ValueTask<TResponse> SendRequestWithProductCheckCore()
|
211 | 212 | // Send request
|
212 | 213 |
|
213 | 214 | var (endpointPath, resolvedRouteValues, postData) = PrepareRequest<TRequest, TRequestParameters>(request);
|
214 |
| - var openTelemetryData = PrepareOpenTelemetryData<TRequest, TRequestParameters>(request, resolvedRouteValues); |
| 215 | + var openTelemetryDataMutator = GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(request, resolvedRouteValues); |
215 | 216 |
|
216 | 217 | TResponse response;
|
217 | 218 |
|
218 | 219 | if (isAsync)
|
219 | 220 | {
|
220 | 221 | response = await _transport
|
221 |
| - .RequestAsync<TResponse>(endpointPath, postData, in openTelemetryData, requestConfig, cancellationToken) |
| 222 | + .RequestAsync<TResponse>(endpointPath, postData, openTelemetryDataMutator, requestConfig, cancellationToken) |
222 | 223 | .ConfigureAwait(false);
|
223 | 224 | }
|
224 | 225 | else
|
225 | 226 | {
|
226 |
| - response = _transport.Request<TResponse>(endpointPath, postData, in openTelemetryData, requestConfig); |
| 227 | + response = _transport.Request<TResponse>(endpointPath, postData, openTelemetryDataMutator, requestConfig); |
227 | 228 | }
|
228 | 229 |
|
229 | 230 | // Evaluate product check result
|
@@ -252,39 +253,41 @@ async ValueTask<TResponse> SendRequestWithProductCheckCore()
|
252 | 253 | }
|
253 | 254 | }
|
254 | 255 |
|
255 |
| - private static OpenTelemetryData PrepareOpenTelemetryData<TRequest, TRequestParameters>(TRequest request, Dictionary<string, string> resolvedRouteValues) |
| 256 | + private static Action<Activity>? GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(TRequest request, Dictionary<string, string>? resolvedRouteValues) |
256 | 257 | where TRequest : Request<TRequestParameters>
|
257 | 258 | where TRequestParameters : RequestParameters, new()
|
258 | 259 | {
|
259 | 260 | // If there are no subscribed listeners, we avoid some work and allocations
|
260 | 261 | if (!Elastic.Transport.Diagnostics.OpenTelemetry.ElasticTransportActivitySourceHasListeners)
|
261 |
| - return default; |
| 262 | + return null; |
262 | 263 |
|
263 |
| - // We fall back to a general operation name in cases where the derived request fails to override the property |
264 |
| - var operationName = !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : request.HttpMethod.GetStringValue(); |
| 264 | + return OpenTelemetryDataMutator; |
265 | 265 |
|
266 |
| - // TODO: Optimisation: We should consider caching these, either for cases where resolvedRouteValues is null, or |
267 |
| - // caching per combination of route values. |
268 |
| - // We should benchmark this first to assess the impact for common workloads. |
269 |
| - // The former is likely going to save some short-lived allocations, but only for requests to endpoints without required path parts. |
270 |
| - // The latter may bloat the cache as some combinations of path parts may rarely re-occur. |
271 |
| - var attributes = new Dictionary<string, object> |
| 266 | + void OpenTelemetryDataMutator(Activity activity) |
272 | 267 | {
|
273 |
| - [OpenTelemetry.SemanticConventions.DbOperation] = !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : "unknown", |
274 |
| - [$"{OpenTelemetrySpanAttributePrefix}schema_url"] = OpenTelemetrySchemaVersion |
275 |
| - }; |
| 268 | + // We fall back to a general operation name in cases where the derived request fails to override the property |
| 269 | + var operationName = !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : request.HttpMethod.GetStringValue(); |
| 270 | + |
| 271 | + // TODO: Optimisation: We should consider caching these, either for cases where resolvedRouteValues is null, or |
| 272 | + // caching per combination of route values. |
| 273 | + // We should benchmark this first to assess the impact for common workloads. |
| 274 | + // The former is likely going to save some short-lived allocations, but only for requests to endpoints without required path parts. |
| 275 | + // The latter may bloat the cache as some combinations of path parts may rarely re-occur. |
| 276 | + |
| 277 | + activity.DisplayName = operationName; |
| 278 | + |
| 279 | + activity.SetTag(OpenTelemetry.SemanticConventions.DbOperation, !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : "unknown"); |
| 280 | + activity.SetTag($"{OpenTelemetrySpanAttributePrefix}schema_url", OpenTelemetrySchemaVersion); |
| 281 | + |
| 282 | + if (resolvedRouteValues is null) |
| 283 | + return; |
276 | 284 |
|
277 |
| - if (resolvedRouteValues is not null) |
278 |
| - { |
279 | 285 | foreach (var value in resolvedRouteValues)
|
280 | 286 | {
|
281 | 287 | if (!string.IsNullOrEmpty(value.Key) && !string.IsNullOrEmpty(value.Value))
|
282 |
| - attributes.Add($"{OpenTelemetrySpanAttributePrefix}path_parts.{value.Key}", value.Value); |
| 288 | + activity.SetTag($"{OpenTelemetrySpanAttributePrefix}path_parts.{value.Key}", value.Value); |
283 | 289 | }
|
284 | 290 | }
|
285 |
| - |
286 |
| - var openTelemetryData = new OpenTelemetryData { SpanName = operationName, SpanAttributes = attributes }; |
287 |
| - return openTelemetryData; |
288 | 291 | }
|
289 | 292 |
|
290 | 293 | private (EndpointPath endpointPath, Dictionary<string, string>? resolvedRouteValues, PostData data) PrepareRequest<TRequest, TRequestParameters>(TRequest request)
|
|
0 commit comments