Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PropertyFetcher API Improvements #1238

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
using System.Diagnostics;
using System.Web;
using System.Web.Routing;
using OpenTelemetry.Context;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;

Expand All @@ -31,8 +30,8 @@ internal class HttpInListener : ListenerHandler
public const string ResponseCustomPropertyName = "OTel.AspNet.Response";
private const string ActivityNameByHttpInListener = "ActivityCreatedByHttpInListener";
private static readonly Func<HttpRequest, string, IEnumerable<string>> HttpRequestHeaderValuesGetter = (request, name) => request.Headers.GetValues(name);
private readonly PropertyFetcher routeFetcher = new PropertyFetcher("Route");
private readonly PropertyFetcher routeTemplateFetcher = new PropertyFetcher("RouteTemplate");
private readonly PropertyFetcher<object> routeFetcher = new PropertyFetcher<object>("Route");
private readonly PropertyFetcher<object> routeTemplateFetcher = new PropertyFetcher<object>("RouteTemplate");
private readonly AspNetInstrumentationOptions options;
private readonly ActivitySourceAdapter activitySource;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using OpenTelemetry.Context;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Instrumentation.GrpcNetClient;
using OpenTelemetry.Trace;
Expand All @@ -35,11 +34,11 @@ internal class HttpInListener : ListenerHandler
private const string UnknownHostName = "UNKNOWN-HOST";
private const string ActivityNameByHttpInListener = "ActivityCreatedByHttpInListener";
private static readonly Func<HttpRequest, string, IEnumerable<string>> HttpRequestHeaderValuesGetter = (request, name) => request.Headers[name];
private readonly PropertyFetcher startContextFetcher = new PropertyFetcher("HttpContext");
private readonly PropertyFetcher stopContextFetcher = new PropertyFetcher("HttpContext");
private readonly PropertyFetcher beforeActionActionDescriptorFetcher = new PropertyFetcher("actionDescriptor");
private readonly PropertyFetcher beforeActionAttributeRouteInfoFetcher = new PropertyFetcher("AttributeRouteInfo");
private readonly PropertyFetcher beforeActionTemplateFetcher = new PropertyFetcher("Template");
private readonly PropertyFetcher<HttpContext> startContextFetcher = new PropertyFetcher<HttpContext>("HttpContext");
private readonly PropertyFetcher<HttpContext> stopContextFetcher = new PropertyFetcher<HttpContext>("HttpContext");
private readonly PropertyFetcher<object> beforeActionActionDescriptorFetcher = new PropertyFetcher<object>("actionDescriptor");
private readonly PropertyFetcher<object> beforeActionAttributeRouteInfoFetcher = new PropertyFetcher<object>("AttributeRouteInfo");
private readonly PropertyFetcher<string> beforeActionTemplateFetcher = new PropertyFetcher<string>("Template");
private readonly bool hostingSupportsW3C;
private readonly AspNetCoreInstrumentationOptions options;
private readonly ActivitySourceAdapter activitySource;
Expand All @@ -55,7 +54,8 @@ public HttpInListener(string name, AspNetCoreInstrumentationOptions options, Act
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The objects should not be disposed.")]
public override void OnStartActivity(Activity activity, object payload)
{
if (!(this.startContextFetcher.Fetch(payload) is HttpContext context))
HttpContext context = this.startContextFetcher.Fetch(payload);
if (context == null)
{
AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStartActivity));
return;
Expand Down Expand Up @@ -138,7 +138,8 @@ public override void OnStopActivity(Activity activity, object payload)
{
if (activity.IsAllDataRequested)
{
if (!(this.stopContextFetcher.Fetch(payload) is HttpContext context))
HttpContext context = this.stopContextFetcher.Fetch(payload);
if (context == null)
{
AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStopActivity));
return;
Expand Down Expand Up @@ -193,7 +194,7 @@ public override void OnCustom(string name, Activity activity, object payload)
// Taking reference on MVC will increase size of deployment for non-MVC apps.
var actionDescriptor = this.beforeActionActionDescriptorFetcher.Fetch(payload);
var attributeRouteInfo = this.beforeActionAttributeRouteInfoFetcher.Fetch(actionDescriptor);
var template = this.beforeActionTemplateFetcher.Fetch(attributeRouteInfo) as string;
var template = this.beforeActionTemplateFetcher.Fetch(attributeRouteInfo);

if (!string.IsNullOrEmpty(template))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal class GrpcClientDiagnosticListener : ListenerHandler
private readonly GrpcClientInstrumentationOptions options;

private readonly ActivitySourceAdapter activitySource;
private readonly PropertyFetcher startRequestFetcher = new PropertyFetcher("Request");
private readonly PropertyFetcher<HttpRequestMessage> startRequestFetcher = new PropertyFetcher<HttpRequestMessage>("Request");

public GrpcClientDiagnosticListener(ActivitySourceAdapter activitySource, GrpcClientInstrumentationOptions options)
: base("Grpc.Net.Client")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand All @@ -22,7 +23,6 @@
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using OpenTelemetry.Context;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;

Expand All @@ -49,10 +49,10 @@ internal class HttpHandlerDiagnosticListener : ListenerHandler
private static readonly Regex CoreAppMajorVersionCheckRegex = new Regex("^\\.NETCoreApp,Version=v(\\d+)\\.", RegexOptions.Compiled | RegexOptions.IgnoreCase);

private readonly ActivitySourceAdapter activitySource;
private readonly PropertyFetcher startRequestFetcher = new PropertyFetcher("Request");
private readonly PropertyFetcher stopResponseFetcher = new PropertyFetcher("Response");
private readonly PropertyFetcher stopExceptionFetcher = new PropertyFetcher("Exception");
private readonly PropertyFetcher stopRequestStatusFetcher = new PropertyFetcher("RequestTaskStatus");
private readonly PropertyFetcher<HttpRequestMessage> startRequestFetcher = new PropertyFetcher<HttpRequestMessage>("Request");
private readonly PropertyFetcher<HttpResponseMessage> stopResponseFetcher = new PropertyFetcher<HttpResponseMessage>("Response");
private readonly PropertyFetcher<Exception> stopExceptionFetcher = new PropertyFetcher<Exception>("Exception");
private readonly PropertyFetcher<TaskStatus> stopRequestStatusFetcher = new PropertyFetcher<TaskStatus>("RequestTaskStatus");
private readonly bool httpClientSupportsW3C;
private readonly HttpClientInstrumentationOptions options;

Expand Down Expand Up @@ -120,21 +120,20 @@ public override void OnStopActivity(Activity activity, object payload)
{
if (activity.IsAllDataRequested)
{
var requestTaskStatus = this.stopRequestStatusFetcher.Fetch(payload) as TaskStatus?;
// https://github.com/dotnet/runtime/blob/master/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs
// requestTaskStatus is not null
var requestTaskStatus = this.stopRequestStatusFetcher.Fetch(payload);

if (requestTaskStatus.HasValue)
if (requestTaskStatus != TaskStatus.RanToCompletion)
{
if (requestTaskStatus != TaskStatus.RanToCompletion)
if (requestTaskStatus == TaskStatus.Canceled)
{
if (requestTaskStatus == TaskStatus.Canceled)
{
activity.SetStatus(Status.Cancelled);
}
else if (requestTaskStatus != TaskStatus.Faulted)
{
// Faults are handled in OnException and should already have a span.Status of Unknown w/ Description.
activity.SetStatus(Status.Unknown);
}
activity.SetStatus(Status.Cancelled);
}
else if (requestTaskStatus != TaskStatus.Faulted)
{
// Faults are handled in OnException and should already have a span.Status of Unknown w/ Description.
activity.SetStatus(Status.Unknown);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ internal class SqlClientDiagnosticListener : ListenerHandler
internal static readonly ActivitySource SqlClientActivitySource = new ActivitySource(ActivitySourceName, Version.ToString());
#pragma warning restore SA1202 // Elements should be ordered by access

private readonly PropertyFetcher commandFetcher = new PropertyFetcher("Command");
private readonly PropertyFetcher connectionFetcher = new PropertyFetcher("Connection");
private readonly PropertyFetcher dataSourceFetcher = new PropertyFetcher("DataSource");
private readonly PropertyFetcher databaseFetcher = new PropertyFetcher("Database");
private readonly PropertyFetcher commandTypeFetcher = new PropertyFetcher("CommandType");
private readonly PropertyFetcher commandTextFetcher = new PropertyFetcher("CommandText");
private readonly PropertyFetcher exceptionFetcher = new PropertyFetcher("Exception");
private readonly PropertyFetcher<object> commandFetcher = new PropertyFetcher<object>("Command");
private readonly PropertyFetcher<object> connectionFetcher = new PropertyFetcher<object>("Connection");
private readonly PropertyFetcher<object> dataSourceFetcher = new PropertyFetcher<object>("DataSource");
private readonly PropertyFetcher<object> databaseFetcher = new PropertyFetcher<object>("Database");
private readonly PropertyFetcher<CommandType> commandTypeFetcher = new PropertyFetcher<CommandType>("CommandType");
private readonly PropertyFetcher<object> commandTextFetcher = new PropertyFetcher<object>("CommandText");
private readonly PropertyFetcher<Exception> exceptionFetcher = new PropertyFetcher<Exception>("Exception");
private readonly SqlClientInstrumentationOptions options;

public SqlClientDiagnosticListener(string sourceName, SqlClientInstrumentationOptions options)
Expand Down
2 changes: 2 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
([#1203](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1203))
* `PropertyFetcher` is now public
([#1232](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1232))
* `PropertyFetcher` changed to `PropertyFetcher<T>`
([#1238](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1238))

## 0.5.0-beta.2

Expand Down
39 changes: 19 additions & 20 deletions src/OpenTelemetry/Instrumentation/PropertyFetcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ namespace OpenTelemetry.Instrumentation
/// <summary>
/// PropertyFetcher fetches a property from an object.
/// </summary>
public class PropertyFetcher
/// <typeparam name="T">The type of the property being fetched.</typeparam>
public class PropertyFetcher<T>
{
private readonly string propertyName;
private PropertyFetch innerFetcher;

/// <summary>
/// Initializes a new instance of the <see cref="PropertyFetcher"/> class.
/// Initializes a new instance of the <see cref="PropertyFetcher{T}"/> class.
/// </summary>
/// <param name="propertyName">Property name to fetch.</param>
public PropertyFetcher(string propertyName)
Expand All @@ -42,7 +43,7 @@ public PropertyFetcher(string propertyName)
/// </summary>
/// <param name="obj">Object to be fetched.</param>
/// <returns>Property fetched.</returns>
public object Fetch(object obj)
public T Fetch(object obj)
{
if (this.innerFetcher == null)
{
Expand All @@ -56,7 +57,7 @@ public object Fetch(object obj)
this.innerFetcher = PropertyFetch.FetcherForProperty(property);
}

return this.innerFetcher?.Fetch(obj);
return this.innerFetcher.Fetch(obj);
}

// see https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs
Expand All @@ -68,43 +69,41 @@ private class PropertyFetch
/// </summary>
public static PropertyFetch FetcherForProperty(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
if (propertyInfo == null || !typeof(T).IsAssignableFrom(propertyInfo.PropertyType))
{
// returns null on any fetch.
return new PropertyFetch();
}

var typedPropertyFetcher = typeof(TypedFetchProperty<,>);
var instantiatedTypedPropertyFetcher = typedPropertyFetcher.GetTypeInfo().MakeGenericType(
propertyInfo.DeclaringType, propertyInfo.PropertyType);
var typedPropertyFetcher = typeof(TypedPropertyFetch<,>);
var instantiatedTypedPropertyFetcher = typedPropertyFetcher.MakeGenericType(
typeof(T), propertyInfo.DeclaringType, propertyInfo.PropertyType);
return (PropertyFetch)Activator.CreateInstance(instantiatedTypedPropertyFetcher, propertyInfo);
}

/// <summary>
/// Given an object, fetch the property that this propertyFetch represents.
/// </summary>
public virtual object Fetch(object obj)
public virtual T Fetch(object obj)
{
return null;
return default;
}

private class TypedFetchProperty<TObject, TProperty> : PropertyFetch
private class TypedPropertyFetch<TDeclaredObject, TDeclaredProperty> : PropertyFetch
where TDeclaredProperty : T
{
private readonly Func<TObject, TProperty> propertyFetch;
private readonly Func<TDeclaredObject, TDeclaredProperty> propertyFetch;

public TypedFetchProperty(PropertyInfo property)
public TypedPropertyFetch(PropertyInfo property)
{
this.propertyFetch = (Func<TObject, TProperty>)property.GetMethod.CreateDelegate(typeof(Func<TObject, TProperty>));
this.propertyFetch = (Func<TDeclaredObject, TDeclaredProperty>)property.GetMethod.CreateDelegate(typeof(Func<TDeclaredObject, TDeclaredProperty>));
}

public override object Fetch(object obj)
public override T Fetch(object obj)
{
if (obj is TObject o)
if (obj is TDeclaredObject o)
{
return this.propertyFetch(o);
}

return null;
return default;
}
}
}
Expand Down
49 changes: 49 additions & 0 deletions test/OpenTelemetry.Tests/Instrumentation/PropertyFetcherTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// <copyright file="PropertyFetcherTest.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System.Diagnostics;
using OpenTelemetry.Instrumentation;
using Xunit;

namespace OpenTelemetry.Tests.Instrumentation
{
public class PropertyFetcherTest
{
[Fact]
public void FetchValidProperty()
{
var activity = new Activity("test");
var fetch = new PropertyFetcher<string>("DisplayName");
var result = fetch.Fetch(activity);

Assert.Equal(activity.DisplayName, result);
}

[Fact]
public void FetchInvalidProperty()
{
var activity = new Activity("test");
var fetch = new PropertyFetcher<string>("DisplayName2");
var result = fetch.Fetch(activity);

var fetchInt = new PropertyFetcher<int>("DisplayName2");
var resultInt = fetchInt.Fetch(activity);

Assert.Equal(default, result);
Assert.Equal(default, resultInt);
}
}
}