Remove AspNet and AspNet.TelemetryHttpModule instrumentation projects (#3397)

This commit is contained in:
Utkarsh Umesan Pillai 2022-06-23 21:15:17 -07:00 committed by GitHub
parent 2e334df8ae
commit 1f4d97e3e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 86 additions and 5065 deletions

View File

@ -2,15 +2,8 @@
<ItemGroup>
<SolutionProjects Include="**\*.csproj" />
<!-- Windows specific projects -->
<SolutionProjects Remove="examples\AspNet\OpenTelemetry.Exporter.AspNet.csproj" Condition="'$(OS)' != 'Windows_NT'" />
<SolutionProjects Remove="src\OpenTelemetry.Instrumentation.AspNet\OpenTelemetry.Instrumentation.AspNet.csproj" Condition="'$(OS)' != 'Windows_NT'" />
<SolutionProjects Remove="test\OpenTelemetry.Instrumentation.AspNet.Tests\OpenTelemetry.Instrumentation.AspNet.Tests.csproj" Condition="'$(OS)' != 'Windows_NT'" />
<PackProjects Include="src\**\*.csproj" />
<!-- Windows specific projects -->
<PackProjects Remove="src\OpenTelemetry.Instrumentation.AspNet\OpenTelemetry.Instrumentation.AspNet.csproj" Condition="'$(OS)' != 'Windows_NT'" />
</ItemGroup>
<Target Name="Build">

View File

@ -75,10 +75,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Cons
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Zipkin.Tests", "test\OpenTelemetry.Exporter.Zipkin.Tests\OpenTelemetry.Exporter.Zipkin.Tests.csproj", "{1D778D2E-9523-450E-A6E0-A36897C7E78E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.AspNet", "src\OpenTelemetry.Instrumentation.AspNet\OpenTelemetry.Instrumentation.AspNet.csproj", "{B9EEACDD-CAFA-4B75-A18D-898E7DE21B17}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.AspNet.Tests", "test\OpenTelemetry.Instrumentation.AspNet.Tests\OpenTelemetry.Instrumentation.AspNet.Tests.csproj", "{55CBAADE-7040-46D6-A845-F207B4F0E281}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests", "test\OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj", "{7C4026CA-6434-4762-8B77-D657EAEE1325}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{F1D0972B-38CF-49C2-9F4B-4C5DE02FB71D}"
@ -129,8 +125,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentati
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.ZPages.Tests", "test\OpenTelemetry.Exporter.ZPages.Tests\OpenTelemetry.Exporter.ZPages.Tests.csproj", "{98F9556B-116F-49B5-9211-BB1D418446FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples.AspNet", "examples\AspNet\Examples.AspNet.csproj", "{9A4E3A68-904B-4835-A3C8-F664B73098DB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Console", "examples\Console\Examples.Console.csproj", "{FF3E6E08-E8E4-4523-B526-847CD989279F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.AspNetCore", "examples\AspNetCore\Examples.AspNetCore.csproj", "{0935622B-9377-4056-8343-AE6ECDC274CF}"
@ -203,10 +197,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "customizing-the-sdk", "docs
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Prometheus", "src\OpenTelemetry.Exporter.Prometheus\OpenTelemetry.Exporter.Prometheus.csproj", "{52158A12-E7EF-45A1-859F-06F9B17410CB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule", "src\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.csproj", "{F38E511B-1877-4E8A-8051-7879FC7DF8A4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Tests", "test\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Tests\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Tests.csproj", "{4D7201BC-7124-4401-AD65-FAB58A053D45}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "learning-more-instruments", "docs\metrics\learning-more-instruments\learning-more-instruments.csproj", "{E7F491CC-C37E-4A56-9CA7-8F77F59E0614}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "getting-started", "docs\metrics\getting-started\getting-started.csproj", "{EA60B549-F712-4ABE-8E44-FCA83B78C06E}"
@ -311,14 +301,6 @@ Global
{1D778D2E-9523-450E-A6E0-A36897C7E78E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D778D2E-9523-450E-A6E0-A36897C7E78E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D778D2E-9523-450E-A6E0-A36897C7E78E}.Release|Any CPU.Build.0 = Release|Any CPU
{B9EEACDD-CAFA-4B75-A18D-898E7DE21B17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9EEACDD-CAFA-4B75-A18D-898E7DE21B17}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9EEACDD-CAFA-4B75-A18D-898E7DE21B17}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9EEACDD-CAFA-4B75-A18D-898E7DE21B17}.Release|Any CPU.Build.0 = Release|Any CPU
{55CBAADE-7040-46D6-A845-F207B4F0E281}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{55CBAADE-7040-46D6-A845-F207B4F0E281}.Debug|Any CPU.Build.0 = Debug|Any CPU
{55CBAADE-7040-46D6-A845-F207B4F0E281}.Release|Any CPU.ActiveCfg = Release|Any CPU
{55CBAADE-7040-46D6-A845-F207B4F0E281}.Release|Any CPU.Build.0 = Release|Any CPU
{7C4026CA-6434-4762-8B77-D657EAEE1325}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C4026CA-6434-4762-8B77-D657EAEE1325}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C4026CA-6434-4762-8B77-D657EAEE1325}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -331,10 +313,6 @@ Global
{98F9556B-116F-49B5-9211-BB1D418446FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98F9556B-116F-49B5-9211-BB1D418446FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{98F9556B-116F-49B5-9211-BB1D418446FF}.Release|Any CPU.Build.0 = Release|Any CPU
{9A4E3A68-904B-4835-A3C8-F664B73098DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A4E3A68-904B-4835-A3C8-F664B73098DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A4E3A68-904B-4835-A3C8-F664B73098DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A4E3A68-904B-4835-A3C8-F664B73098DB}.Release|Any CPU.Build.0 = Release|Any CPU
{FF3E6E08-E8E4-4523-B526-847CD989279F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF3E6E08-E8E4-4523-B526-847CD989279F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF3E6E08-E8E4-4523-B526-847CD989279F}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -419,14 +397,6 @@ Global
{52158A12-E7EF-45A1-859F-06F9B17410CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52158A12-E7EF-45A1-859F-06F9B17410CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52158A12-E7EF-45A1-859F-06F9B17410CB}.Release|Any CPU.Build.0 = Release|Any CPU
{F38E511B-1877-4E8A-8051-7879FC7DF8A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F38E511B-1877-4E8A-8051-7879FC7DF8A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F38E511B-1877-4E8A-8051-7879FC7DF8A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F38E511B-1877-4E8A-8051-7879FC7DF8A4}.Release|Any CPU.Build.0 = Release|Any CPU
{4D7201BC-7124-4401-AD65-FAB58A053D45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D7201BC-7124-4401-AD65-FAB58A053D45}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D7201BC-7124-4401-AD65-FAB58A053D45}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D7201BC-7124-4401-AD65-FAB58A053D45}.Release|Any CPU.Build.0 = Release|Any CPU
{E7F491CC-C37E-4A56-9CA7-8F77F59E0614}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7F491CC-C37E-4A56-9CA7-8F77F59E0614}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7F491CC-C37E-4A56-9CA7-8F77F59E0614}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -497,7 +467,6 @@ Global
{E69578EB-B456-4062-A645-877CD964528B} = {F1D0972B-38CF-49C2-9F4B-4C5DE02FB71D}
{C1542297-8763-4DF4-957C-489ED771C21D} = {7CB2F02E-03FA-4FFF-89A5-C51F107623FD}
{D2E73927-5966-445C-94E9-EFE6F269C8D5} = {7CB2F02E-03FA-4FFF-89A5-C51F107623FD}
{9A4E3A68-904B-4835-A3C8-F664B73098DB} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E}
{FF3E6E08-E8E4-4523-B526-847CD989279F} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E}
{0935622B-9377-4056-8343-AE6ECDC274CF} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E}
{2C7DD1DA-C229-4D9E-9AF0-BCD5CD3E4948} = {7CB2F02E-03FA-4FFF-89A5-C51F107623FD}

View File

@ -53,7 +53,6 @@ Here are the most commonly used components:
Here are the [instrumentation
libraries](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library):
* [ASP.NET](./src/OpenTelemetry.Instrumentation.AspNet/README.md)
* [ASP.NET Core](./src/OpenTelemetry.Instrumentation.AspNetCore/README.md)
* [Grpc.Net.Client](./src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md)
* [HTTP clients](./src/OpenTelemetry.Instrumentation.Http/README.md)

View File

@ -37,9 +37,6 @@
<GrpcAspNetCorePkgVer>[2.43.0,3.0)</GrpcAspNetCorePkgVer>
<GrpcAspNetCoreServerPkgVer>[2.43.0, 3.0)</GrpcAspNetCoreServerPkgVer>
<GrpcToolsPkgVer>[2.44.0,3.0)</GrpcToolsPkgVer>
<MicrosoftAspNetMvcPkgVer>[5.2.7,6.0)</MicrosoftAspNetMvcPkgVer>
<MicrosoftAspNetWebApiWebHostPkgVer>[5.2.7,6.0)</MicrosoftAspNetWebApiWebHostPkgVer>
<MicrosoftAspNetWebPagesPkgVer>[3.2.7,4.0)</MicrosoftAspNetWebPagesPkgVer>
<MicrosoftExtensionsHostingPkgVer>[3.1.6,5.0)</MicrosoftExtensionsHostingPkgVer>
<MicrosoftExtensionsLoggingPkgVer>[6.0.0,)</MicrosoftExtensionsLoggingPkgVer>
<MicrosoftExtensionsLoggingAbstractionsPkgVer>[6.0.0,)</MicrosoftExtensionsLoggingAbstractionsPkgVer>

View File

@ -35,9 +35,10 @@ In a typical application, a single `MeterProvider` is created at application
startup and disposed at application shutdown. It is important to ensure that the
provider is not disposed too early. Actual mechanism depends on the application
type. For example, in a typical ASP.NET application, `MeterProvider` is created
in `Application_Start`, and disposed in `Application_End` (both methods part of
Global.asax.cs file) as shown [here](../../../examples/AspNet/Global.asax.cs). In
a typical ASP.NET Core application, `MeterProvider` lifetime is managed by
in `Application_Start`, and disposed in `Application_End` (both methods are a
part of the Global.asax.cs file) as shown
[here](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/main/examples/AspNet/Global.asax.cs).
In a typical ASP.NET Core application, `MeterProvider` lifetime is managed by
leveraging the built-in Dependency Injection container as shown
[here](../../../examples/AspNetCore/Program.cs).

View File

@ -15,9 +15,9 @@ processors, etc. Naturally, almost all the customizations must be done on the
Building a `TracerProvider` is done using `TracerProviderBuilder` which must be
obtained by calling `Sdk.CreateTracerProviderBuilder()`. `TracerProviderBuilder`
exposes various methods which configures the provider it is going to build. These
includes methods like `SetSampler`, `AddProcessor` etc, and are explained in
subsequent sections of this document. Once configuration is done, calling
exposes various methods which configures the provider it is going to build.
These includes methods like `SetSampler`, `AddProcessor` etc, and are explained
in subsequent sections of this document. Once configuration is done, calling
`Build()` on the `TracerProviderBuilder` builds the `TracerProvider` instance.
Once built, changes to its configuration is not allowed, with the exception of
adding more processors. In most cases, a single `TracerProvider` is created at
@ -38,9 +38,10 @@ In a typical application, a single `TracerProvider` is created at application
startup and disposed at application shutdown. It is important to ensure that the
provider is not disposed too early. Actual mechanism depends on the application
type. For example, in a typical ASP.NET application, `TracerProvider` is created
in `Application_Start`, and disposed in `Application_End` (both methods part of
Global.asax.cs file) as shown [here](../../../examples/AspNet/Global.asax.cs). In
a typical ASP.NET Core application, `TracerProvider` lifetime is managed by
in `Application_Start`, and disposed in `Application_End` (both methods are a
part of the Global.asax.cs file) as shown
[here](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/main/examples/AspNet/Global.asax.cs).
In a typical ASP.NET Core application, `TracerProvider` lifetime is managed by
leveraging the built-in Dependency Injection container as shown
[here](../../../examples/AspNetCore/Program.cs).
@ -98,13 +99,12 @@ using var tracerProvider = Sdk.CreateTracerProviderBuilder()
See [Program.cs](./Program.cs) for complete example.
**Note**
A common mistake while configuring `TracerProvider` is forgetting to add
all `ActivitySources` to the provider. It is recommended to leverage the
**Note** A common mistake while configuring `TracerProvider` is forgetting to
add all `ActivitySources` to the provider. It is recommended to leverage the
wild card subscription model where it makes sense. For example, if your
application is expecting to enable tracing from a number of libraries
from a company "Abc", the you can use `AddSource("Abc.*")` to enable
all sources whose name starts with "Abc.".
application is expecting to enable tracing from a number of libraries from a
company "Abc", the you can use `AddSource("Abc.*")` to enable all sources whose
name starts with "Abc.".
### Instrumentation
@ -124,5 +124,5 @@ all sources whose name starts with "Abc.".
## Context Propagation
// TODO: OpenTelemetry Sdk contents about Context.
// TODO: Links to built-in instrumentations doing Propagation.
// TODO: OpenTelemetry Sdk contents about Context. // TODO: Links to built-in
instrumentations doing Propagation.

View File

@ -102,7 +102,6 @@ The [OpenTelemetry .NET Github repo](../../../README.md#getting-started) ships
the following instrumentation libraries. The individual docs for them describes
the library they instrument, and steps for enabling them.
* [ASP.NET](../../../src/OpenTelemetry.Instrumentation.AspNet/README.md)
* [ASP.NET
Core](../../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md)
* [gRPC
@ -200,8 +199,7 @@ activities does not by default runs through the sampler, and will have their
`Kind` set to internal and they'll have empty ActivitySource name associated
with it.
Some common examples of such libraries include
[ASP.NET](../../../src/OpenTelemetry.Instrumentation.AspNet/README.md), [ASP.NET
Some common examples of such libraries include [ASP.NET
Core](../../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md), [HTTP
client .NET Core](../../../src/OpenTelemetry.Instrumentation.Http/README.md) .
Instrumentation libraries for these are already provided in this repo. The

View File

@ -1,36 +0,0 @@
// <copyright file="RouteConfig.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.Web.Mvc;
using System.Web.Routing;
namespace Examples.AspNet
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
}
}

View File

@ -1,40 +0,0 @@
// <copyright file="WebApiConfig.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.Net.Http.Formatting;
using System.Web.Http;
namespace Examples.AspNet
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
}
}
}

View File

@ -1,37 +0,0 @@
// <copyright file="HomeController.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.Web.Mvc;
namespace Examples.AspNet.Controllers
{
public class HomeController : Controller
{
// For testing traditional routing. Ex: https://localhost:XXXX/
public ActionResult Index()
{
return this.View();
}
[Route("about_attr_route/{customerId}")] // For testing attribute routing. Ex: https://localhost:XXXX/about_attr_route
public ActionResult About(int? customerId)
{
this.ViewBag.Message = $"Your application description page for customer {customerId}.";
return this.View();
}
}
}

View File

@ -1,217 +0,0 @@
// <copyright file="WeatherForecastController.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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Web.Http;
using Examples.AspNet.Models;
using OpenTelemetry;
namespace Examples.AspNet.Controllers
{
public class WeatherForecastController : ApiController
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching",
};
[HttpGet] // For testing traditional routing. Ex: https://localhost:XXXX/api/weatherforecast
public async Task<IEnumerable<WeatherForecast>> Get()
{
// Build some dependency spans.
await RequestGoogleHomPageViaHttpClient().ConfigureAwait(false);
await this.RequestInvalidViaHttpClient().ConfigureAwait(false);
await this.RequestValidThatReturnsFailedViaHttpClient().ConfigureAwait(false);
await this.RequestValidThatSpawnsSubSpansViaHttpClient().ConfigureAwait(false);
return GetWeatherForecast();
}
[Route("subroute/{customerId}")] // For testing attribute routing. Ex: https://localhost:XXXX/subroute/10
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get(int customerId)
{
if (customerId < 0)
{
throw new ArgumentException();
}
// Making http calls here to serve as an example of
// how dependency calls will be captured and treated
// automatically as child of incoming request.
RequestGoogleHomPageViaHttpWebRequestLegacySync();
await RequestGoogleHomPageViaHttpWebRequestLegacyAsync().ConfigureAwait(false);
RequestGoogleHomPageViaHttpWebRequestLegacyAsyncResult();
return GetWeatherForecast();
}
/// <summary>
/// For testing large async operation which causes IIS to jump threads and results in lost AsyncLocals.
/// </summary>
[Route("data")]
[HttpGet]
public async Task<string> GetData()
{
Baggage.SetBaggage("key1", "value1");
using var rng = RandomNumberGenerator.Create();
var requestData = new byte[1024 * 1024 * 100];
rng.GetBytes(requestData);
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, this.Url.Content("~/data"));
request.Content = new ByteArrayContent(requestData);
using var response = await client.SendAsync(request).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var responseData = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
return responseData.SequenceEqual(responseData) ? "match" : "mismatch";
}
[Route("data")]
[HttpPost]
public async Task<HttpResponseMessage> PostData()
{
string value1 = Baggage.GetBaggage("key1");
if (string.IsNullOrEmpty(value1))
{
throw new InvalidOperationException("Key1 was not found on Baggage.");
}
var stream = await this.Request.Content.ReadAsStreamAsync().ConfigureAwait(false);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(stream),
};
result.Content.Headers.ContentType = this.Request.Content.Headers.ContentType;
return result;
}
private static IEnumerable<WeatherForecast> GetWeatherForecast()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)],
})
.ToArray();
}
// Test successful dependency collection via HttpClient.
private static async Task RequestGoogleHomPageViaHttpClient()
{
using var request = new HttpClient();
using var response = await request.GetAsync("http://www.google.com").ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
// Test dependency collection via legacy HttpWebRequest sync.
private static void RequestGoogleHomPageViaHttpWebRequestLegacySync()
{
var request = WebRequest.Create("http://www.google.com/?sync");
using var response = request.GetResponse();
}
// Test dependency collection via legacy HttpWebRequest async.
private static async Task RequestGoogleHomPageViaHttpWebRequestLegacyAsync()
{
var request = (HttpWebRequest)WebRequest.Create($"http://www.google.com/?async");
using var response = await request.GetResponseAsync().ConfigureAwait(false);
}
// Test dependency collection via legacy HttpWebRequest IAsyncResult.
private static void RequestGoogleHomPageViaHttpWebRequestLegacyAsyncResult()
{
var request = (HttpWebRequest)WebRequest.Create($"http://www.google.com/?async");
var asyncResult = request.BeginGetResponse(null, null);
using var response = request.EndGetResponse(asyncResult);
}
// Test exception dependency collection via HttpClient.
private async Task RequestInvalidViaHttpClient()
{
try
{
using var request = new HttpClient();
// This request is not available over SSL and will throw a handshake exception.
using var response = await request.GetAsync(this.Url.Content("~/subroute/10").Replace("http", "https")).ConfigureAwait(false);
Debug.Fail("Unreachable");
}
catch
{
}
}
// Test exception dependency collection via HttpClient.
private async Task RequestValidThatReturnsFailedViaHttpClient()
{
using var request = new HttpClient();
// This request will return a 500 error because customerId should be >= 0;
using var response = await request.GetAsync(this.Url.Content("~/subroute/-1")).ConfigureAwait(false);
Debug.Assert(response.StatusCode == HttpStatusCode.InternalServerError, "response.StatusCode is InternalServerError");
}
// Test successful dependency collection via HttpClient.
private async Task RequestValidThatSpawnsSubSpansViaHttpClient()
{
using var request = new HttpClient();
// This request will return successfully and cause a bunch of sub-spans;
using var response = await request.GetAsync(this.Url.Content("~/subroute/10")).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
}
}

View File

@ -1,156 +0,0 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{9A4E3A68-904B-4835-A3C8-F664B73098DB}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenTelemetry.Exporter.AspNet</RootNamespace>
<AssemblyName>OpenTelemetry.Exporter.AspNet</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<UseIISExpress>true</UseIISExpress>
<Use64BitIISExpress />
<IISExpressSSLPort>
</IISExpressSSLPort>
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<Content Include="Global.asax" />
<Content Include="Web.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\RouteConfig.cs" />
<Compile Include="App_Start\WebApiConfig.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\WeatherForecastController.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Models\WeatherForecast.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SuppressInstrumentationHttpModule.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\_ViewStart.cshtml" />
<Content Include="Views\Home\Index.cshtml" />
<Content Include="Views\Shared\_Layout.cshtml" />
<Content Include="Views\Web.config" />
<Content Include="Views\Home\About.cshtml" />
<None Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</None>
<None Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.WebHost" Version="$(MicrosoftAspNetWebApiWebHostPkgVer)" />
<PackageReference Include="Microsoft.AspNet.Mvc" Version="$(MicrosoftAspNetMvcPkgVer)" />
<PackageReference Include="Microsoft.AspNet.WebPages" Version="$(MicrosoftAspNetWebPagesPkgVer)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Api\OpenTelemetry.Api.csproj">
<Project>{99f8a331-05e9-45a5-89ba-4c54e825e5b2}</Project>
<Name>OpenTelemetry.Api</Name>
</ProjectReference>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNet\OpenTelemetry.Instrumentation.AspNet.csproj">
<Project>{b9eeacdd-cafa-4b75-a18d-898e7de21b17}</Project>
<Name>OpenTelemetry.Instrumentation.AspNet</Name>
</ProjectReference>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Jaeger\OpenTelemetry.Exporter.Jaeger.csproj">
<Project>{8d47e3cf-9ae3-42fe-9084-feb72d9ad769}</Project>
<Name>OpenTelemetry.Exporter.Jaeger</Name>
</ProjectReference>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.Http\OpenTelemetry.Instrumentation.Http.csproj">
<Project>{412c64d1-43d6-4e4c-8ad8-e20e63b415bd}</Project>
<Name>OpenTelemetry.Instrumentation.Http</Name>
</ProjectReference>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj">
<Project>{ae3e3df5-4083-4c6e-a840-8271b0acde7e}</Project>
<Name>OpenTelemetry</Name>
</ProjectReference>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj">
<Project>{1afff251-3b0c-47ca-be94-937083732c0a}</Project>
<Name>OpenTelemetry.Exporter.Console</Name>
</ProjectReference>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj">
<Project>{7edae7fa-b44e-42ca-80fa-7df2faa2c5dd}</Project>
<Name>OpenTelemetry.Exporter.Zipkin</Name>
</ProjectReference>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.OpenTelemetryProtocol\OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj">
<Project>{a38ac295-2745-4b85-8b6b-dca864cedd5b}</Project>
<Name>OpenTelemetry.Exporter.OpenTelemetryProtocol</Name>
</ProjectReference>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus\OpenTelemetry.Exporter.Prometheus.csproj">
<Project>{52158a12-e7ef-45a1-859f-06f9b17410cb}</Project>
<Name>OpenTelemetry.Exporter.Prometheus</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="Exists('$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets')" />
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
<Target Name="SkipBuildWithoutVisualStudio">
<Message Text="Skipping build because Visual Studio is not available." Condition="!Exists('$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets')" />
<CallTarget Targets="$(BuildDependsOnOriginalValue)" Condition="Exists('$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets')" />
</Target>
<PropertyGroup>
<BuildDependsOnOriginalValue>$(BuildDependsOn)</BuildDependsOnOriginalValue>
<BuildDependsOn>SkipBuildWithoutVisualStudio</BuildDependsOn>
</PropertyGroup>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>0</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:56171/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -1 +0,0 @@
<%@ Application Codebehind="Global.asax.cs" Inherits="Examples.AspNet.WebApiApplication" Language="C#" %>

View File

@ -1,111 +0,0 @@
// <copyright file="Global.asax.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;
using System.Configuration;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
namespace Examples.AspNet
{
#pragma warning disable SA1649 // File name should match first type name
public class WebApiApplication : HttpApplication
#pragma warning restore SA1649 // File name should match first type name
{
private IDisposable tracerProvider;
private IDisposable meterProvider;
protected void Application_Start()
{
var builder = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation()
.AddHttpClientInstrumentation();
switch (ConfigurationManager.AppSettings["UseExporter"].ToLowerInvariant())
{
case "jaeger":
builder.AddJaegerExporter(jaegerOptions =>
{
jaegerOptions.AgentHost = ConfigurationManager.AppSettings["JaegerHost"];
jaegerOptions.AgentPort = int.Parse(ConfigurationManager.AppSettings["JaegerPort"]);
});
break;
case "zipkin":
builder.AddZipkinExporter(zipkinOptions =>
{
zipkinOptions.Endpoint = new Uri(ConfigurationManager.AppSettings["ZipkinEndpoint"]);
});
break;
case "otlp":
builder.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri(ConfigurationManager.AppSettings["OtlpEndpoint"]);
});
break;
default:
builder.AddConsoleExporter(options => options.Targets = ConsoleExporterOutputTargets.Debug);
break;
}
this.tracerProvider = builder.Build();
// Metrics
// Note: Tracerprovider is needed for metrics to work
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/2994
var meterBuilder = Sdk.CreateMeterProviderBuilder()
.AddAspNetInstrumentation();
switch (ConfigurationManager.AppSettings["UseMetricsExporter"].ToLowerInvariant())
{
case "otlp":
meterBuilder.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri(ConfigurationManager.AppSettings["OtlpEndpoint"]);
});
break;
case "prometheus":
meterBuilder.AddPrometheusExporter();
break;
default:
meterBuilder.AddConsoleExporter((exporterOptions, metricReaderOptions) =>
{
exporterOptions.Targets = ConsoleExporterOutputTargets.Debug;
});
break;
}
this.meterProvider = meterBuilder.Build();
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
protected void Application_End()
{
this.tracerProvider?.Dispose();
this.meterProvider?.Dispose();
}
}
}

View File

@ -1,31 +0,0 @@
// <copyright file="WeatherForecast.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;
namespace Examples.AspNet.Models
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(this.TemperatureC / 0.5556);
public string Summary { get; set; }
}
}

View File

@ -1,50 +0,0 @@
// <copyright file="AssemblyInfo.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.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Examples.AspNet")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Examples.AspNet")]
[assembly: AssemblyCopyright("Copyright @ 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("9a4e3a68-904b-4835-a3c8-f664b73098db")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -1,58 +0,0 @@
// <copyright file="SuppressInstrumentationHttpModule.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;
using System.Web;
using OpenTelemetry;
namespace Examples.AspNet
{
/// <summary>
/// A demo <see cref="IHttpModule"/> which will suppress ASP.NET
/// instrumentation if a request contains "suppress=true" on the query
/// string. Suppressed spans will not be processed/exported by the
/// OpenTelemetry SDK.
/// </summary>
public class SuppressInstrumentationHttpModule : IHttpModule
{
private IDisposable suppressionScope;
public void Init(HttpApplication context)
{
context.BeginRequest += this.Application_BeginRequest;
context.EndRequest += this.Application_EndRequest;
}
public void Dispose()
{
}
private void Application_BeginRequest(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
if (context.Request.QueryString["suppress"] == "true")
{
this.suppressionScope = SuppressInstrumentationScope.Begin();
}
}
private void Application_EndRequest(object sender, EventArgs e)
{
this.suppressionScope?.Dispose();
}
}
}

View File

@ -1,7 +0,0 @@
@{
ViewBag.Title = "About";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>
<p>Use this area to provide additional information.</p>

View File

@ -1,31 +0,0 @@
@{
ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>ASP.NET</h1>
<p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
<p><a href="https://asp.net" class="btn btn-primary btn-lg">Learn more &raquo;</a></p>
</div>
<div class="row">
<div class="col-md-4">
<h2>Getting started</h2>
<p>
ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that
enables a clean separation of concerns and gives you full control over markup
for enjoyable, agile development.
</p>
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301865">Learn more &raquo;</a></p>
</div>
<div class="col-md-4">
<h2>Get more libraries</h2>
<p>NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.</p>
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301866">Learn more &raquo;</a></p>
</div>
<div class="col-md-4">
<h2>Web Hosting</h2>
<p>You can easily find a web hosting company that offers the right mix of features and price for your applications.</p>
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301867">Learn more &raquo;</a></p>
</div>
</div>

View File

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - My ASP.NET Application</title>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
</ul>
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
</footer>
</div>
@RenderSection("scripts", required: false)
</body>
</html>

View File

@ -1,42 +0,0 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
<system.web>
<compilation>
<assemblies>
<add assembly="System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
</assemblies>
</compilation>
</system.web>
</configuration>

View File

@ -1,3 +0,0 @@
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

View File

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0"/>
<add key="webpages:Enabled" value="false"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
<add key="UseExporter" value="console"/>
<add key="UseMetricsExporter" value="console"/>
<add key="JaegerHost" value="localhost"/>
<add key="JaegerPort" value="6831"/>
<add key="ZipkinEndpoint" value="http://localhost:9411/api/v2/spans"/>
<add key="OtlpEndpoint" value="http://localhost:4317"/>
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.8"/>
<httpRuntime targetFramework="4.8"
maxRequestLength="2147483647"
executionTimeout="300" />
</system.web>
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
<remove name="OPTIONSVerbHandler"/>
<remove name="TRACEVerbHandler"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>
<modules>
<add name="SuppressInstrumentationHttpModule" type="Examples.AspNet.SuppressInstrumentationHttpModule" preCondition="integratedMode,managedHandler"/>
<add name="TelemetryHttpModule" type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" preCondition="integratedMode,managedHandler"/>
</modules>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="4294967295" />
</requestFiltering>
</security>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -153,8 +153,7 @@ required only for the following scenarios:
[Propagators](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md),
to inject and extract context data. Some of the most common libraries
requiring this include
[HttpClient](../OpenTelemetry.Instrumentation.Http/README.md),
[ASP.NET](../OpenTelemetry.Instrumentation.AspNet/README.md), [ASP.NET
[HttpClient](../OpenTelemetry.Instrumentation.Http/README.md), [ASP.NET
Core](../OpenTelemetry.Instrumentation.AspNetCore/README.md). This repo
already provides instrumentation for these common libraries. If your library
is not built on top of these, and want to leverage propagators, follow the
@ -394,8 +393,8 @@ OpenTelemetry defines a concept called
[Status](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status)
to be associated with `Activity`. Starting with [DiagnosticSource
6.0](https://www.nuget.org/packages/System.Diagnostics.DiagnosticSource/6.0.0),
`SetStatus` API on `Activity` can be used to set the status and description
as shown below:
`SetStatus` API on `Activity` can be used to set the status and description as
shown below:
```csharp
activity?.SetStatus(ActivityStatusCode.Ok);
@ -467,16 +466,16 @@ runtime itself, as part of the
package. This means, users can instrument their applications/libraries to emit
metrics by simply using the `System.Diagnostics.DiagnosticSource` package. This
package can be used in applications targeting any of the officially supported
versions of [.NET](https://dotnet.microsoft.com/download/dotnet) and
[.NET Framework](https://dotnet.microsoft.com/download/dotnet-framework) (an
older Windows-based .NET implementation).
versions of [.NET](https://dotnet.microsoft.com/download/dotnet) and [.NET
Framework](https://dotnet.microsoft.com/download/dotnet-framework) (an older
Windows-based .NET implementation).
## Instrumenting a library/application with .NET Metrics API
### Basic metric usage
1. Install the `System.Diagnostics.DiagnosticSource` package version
`6.0.0` or above to your application or library.
1. Install the `System.Diagnostics.DiagnosticSource` package version `6.0.0` or
above to your application or library.
```xml
<ItemGroup>
@ -496,9 +495,8 @@ older Windows-based .NET implementation).
The above requires import of the `System.Diagnostics.Metrics` namespace.
**Note:**
It is important to note that `Meter` instances are created by using its
constructor, and *not* by calling a `GetMeter` method on the
**Note:** It is important to note that `Meter` instances are created by
using its constructor, and *not* by calling a `GetMeter` method on the
`MeterProvider`. This is an important distinction from the [OpenTelemetry
specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#get-a-meter),
where `Meter`s are obtained from `MeterProvider`.
@ -528,8 +526,7 @@ describes more kinds of instruments.
This component uses an
[EventSource](https://docs.microsoft.com/dotnet/api/system.diagnostics.tracing.eventsource)
with the name "OpenTelemetry-Api" for its internal logging.
Please refer to [SDK
with the name "OpenTelemetry-Api" for its internal logging. Please refer to [SDK
troubleshooting](../OpenTelemetry/README.md#troubleshooting) for instructions on
seeing these internal logs.

View File

@ -1,16 +0,0 @@
const OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.AspNetActivityName = "Microsoft.AspNet.HttpReqIn" -> string
const OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.AspNetSourceName = "OpenTelemetry.Instrumentation.AspNet.Telemetry" -> string
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Dispose() -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Init(System.Web.HttpApplication context) -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.TelemetryHttpModule() -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnExceptionCallback.get -> System.Action<System.Diagnostics.Activity, System.Web.HttpContext, System.Exception>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnExceptionCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnRequestStartedCallback.get -> System.Action<System.Diagnostics.Activity, System.Web.HttpContext>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnRequestStartedCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnRequestStoppedCallback.get -> System.Action<System.Diagnostics.Activity, System.Web.HttpContext>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnRequestStoppedCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.TextMapPropagator.get -> OpenTelemetry.Context.Propagation.TextMapPropagator
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.TextMapPropagator.set -> void
static OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Options.get -> OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions

View File

@ -1,224 +0,0 @@
// <copyright file="ActivityHelper.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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Web;
using OpenTelemetry.Context;
using OpenTelemetry.Context.Propagation;
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Activity helper class.
/// </summary>
internal static class ActivityHelper
{
/// <summary>
/// Key to store the state in HttpContext.
/// </summary>
internal const string ContextKey = "__AspnetInstrumentationContext__";
internal static readonly object StartedButNotSampledObj = new();
private const string BaggageSlotName = "otel.baggage";
private static readonly Func<HttpRequest, string, IEnumerable<string>> HttpRequestHeaderValuesGetter = (request, name) => request.Headers.GetValues(name);
private static readonly ActivitySource AspNetSource = new(
TelemetryHttpModule.AspNetSourceName,
typeof(ActivityHelper).Assembly.GetName().Version.ToString());
/// <summary>
/// Try to get the started <see cref="Activity"/> for the running <see
/// cref="HttpContext"/>.
/// </summary>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="aspNetActivity">Started <see cref="Activity"/> or <see
/// langword="null"/> if 1) start has not been called or 2) start was
/// called but sampling decided not to create an instance.</param>
/// <returns><see langword="true"/> if start has been called.</returns>
public static bool HasStarted(HttpContext context, out Activity aspNetActivity)
{
Debug.Assert(context != null, "Context is null.");
object itemValue = context.Items[ContextKey];
if (itemValue is ContextHolder contextHolder)
{
aspNetActivity = contextHolder.Activity;
return true;
}
aspNetActivity = null;
return itemValue == StartedButNotSampledObj;
}
/// <summary>
/// Creates root (first level) activity that describes incoming request.
/// </summary>
/// <param name="textMapPropagator"><see cref="TextMapPropagator"/>.</param>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="onRequestStartedCallback">Callback action.</param>
/// <returns>New root activity.</returns>
public static Activity StartAspNetActivity(TextMapPropagator textMapPropagator, HttpContext context, Action<Activity, HttpContext> onRequestStartedCallback)
{
Debug.Assert(context != null, "Context is null.");
PropagationContext propagationContext = textMapPropagator.Extract(default, context.Request, HttpRequestHeaderValuesGetter);
Activity activity = AspNetSource.StartActivity(TelemetryHttpModule.AspNetActivityName, ActivityKind.Server, propagationContext.ActivityContext);
if (activity != null)
{
if (textMapPropagator is not TraceContextPropagator)
{
Baggage.Current = propagationContext.Baggage;
context.Items[ContextKey] = new ContextHolder { Activity = activity, Baggage = RuntimeContext.GetValue(BaggageSlotName) };
}
else
{
context.Items[ContextKey] = new ContextHolder { Activity = activity };
}
try
{
onRequestStartedCallback?.Invoke(activity, context);
}
catch (Exception callbackEx)
{
AspNetTelemetryEventSource.Log.CallbackException(activity, "OnStarted", callbackEx);
}
AspNetTelemetryEventSource.Log.ActivityStarted(activity);
}
else
{
context.Items[ContextKey] = StartedButNotSampledObj;
}
return activity;
}
/// <summary>
/// Stops the activity and notifies listeners about it.
/// </summary>
/// <param name="textMapPropagator"><see cref="TextMapPropagator"/>.</param>
/// <param name="aspNetActivity"><see cref="Activity"/>.</param>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="onRequestStoppedCallback">Callback action.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StopAspNetActivity(TextMapPropagator textMapPropagator, Activity aspNetActivity, HttpContext context, Action<Activity, HttpContext> onRequestStoppedCallback)
{
Debug.Assert(context != null, "Context is null.");
if (aspNetActivity == null)
{
Debug.Assert(context.Items[ContextKey] == StartedButNotSampledObj, "Context item is not StartedButNotSampledObj.");
// This is the case where a start was called but no activity was
// created due to a sampling decision.
context.Items[ContextKey] = null;
return;
}
Debug.Assert(context.Items[ContextKey] is ContextHolder, "Context item is not an ContextHolder instance.");
var currentActivity = Activity.Current;
aspNetActivity.Stop();
context.Items[ContextKey] = null;
try
{
onRequestStoppedCallback?.Invoke(aspNetActivity, context);
}
catch (Exception callbackEx)
{
AspNetTelemetryEventSource.Log.CallbackException(aspNetActivity, "OnStopped", callbackEx);
}
AspNetTelemetryEventSource.Log.ActivityStopped(currentActivity);
if (textMapPropagator is not TraceContextPropagator)
{
Baggage.Current = default;
}
if (currentActivity != aspNetActivity)
{
Activity.Current = currentActivity;
}
}
/// <summary>
/// Notifies listeners about an unhandled exception thrown on the <see cref="HttpContext"/>.
/// </summary>
/// <param name="aspNetActivity"><see cref="Activity"/>.</param>
/// <param name="context"><see cref="HttpContext"/>.</param>
/// <param name="exception"><see cref="Exception"/>.</param>
/// <param name="onExceptionCallback">Callback action.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteActivityException(Activity aspNetActivity, HttpContext context, Exception exception, Action<Activity, HttpContext, Exception> onExceptionCallback)
{
Debug.Assert(context != null, "Context is null.");
Debug.Assert(exception != null, "Exception is null.");
if (aspNetActivity != null)
{
try
{
onExceptionCallback?.Invoke(aspNetActivity, context, exception);
}
catch (Exception callbackEx)
{
AspNetTelemetryEventSource.Log.CallbackException(aspNetActivity, "OnException", callbackEx);
}
AspNetTelemetryEventSource.Log.ActivityException(aspNetActivity, exception);
}
}
/// <summary>
/// It's possible that a request is executed in both native threads and managed threads,
/// in such case Activity.Current will be lost during native thread and managed thread switch.
/// This method is intended to restore the current activity in order to correlate the child
/// activities with the root activity of the request.
/// </summary>
/// <param name="context"><see cref="HttpContext"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void RestoreContextIfNeeded(HttpContext context)
{
Debug.Assert(context != null, "Context is null.");
if (context.Items[ContextKey] is ContextHolder contextHolder && Activity.Current != contextHolder.Activity)
{
Activity.Current = contextHolder.Activity;
if (contextHolder.Baggage != null)
{
RuntimeContext.SetValue(BaggageSlotName, contextHolder.Baggage);
}
AspNetTelemetryEventSource.Log.ActivityRestored(contextHolder.Activity);
}
}
internal class ContextHolder
{
public Activity Activity;
public object Baggage;
}
}
}

View File

@ -1,122 +0,0 @@
// <copyright file="AspNetTelemetryEventSource.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;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// ETW EventSource tracing class.
/// </summary>
[EventSource(Name = "OpenTelemetry-Instrumentation-AspNet-Telemetry", Guid = "1de158cc-f7ce-4293-bd19-2358c93c8186")]
internal sealed class AspNetTelemetryEventSource : EventSource
{
/// <summary>
/// Instance of the PlatformEventSource class.
/// </summary>
public static readonly AspNetTelemetryEventSource Log = new();
[NonEvent]
public void ActivityStarted(Activity activity)
{
if (this.IsEnabled(EventLevel.Verbose, EventKeywords.All))
{
this.ActivityStarted(activity?.Id);
}
}
[NonEvent]
public void ActivityStopped(Activity activity)
{
if (this.IsEnabled(EventLevel.Verbose, EventKeywords.All))
{
this.ActivityStopped(activity?.Id);
}
}
[NonEvent]
public void ActivityRestored(Activity activity)
{
if (this.IsEnabled(EventLevel.Informational, EventKeywords.All))
{
this.ActivityRestored(activity?.Id);
}
}
[NonEvent]
public void ActivityException(Activity activity, Exception ex)
{
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.ActivityException(activity?.Id, ex.ToInvariantString());
}
}
[NonEvent]
public void CallbackException(Activity activity, string eventName, Exception ex)
{
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.CallbackException(activity?.Id, eventName, ex.ToInvariantString());
}
}
[Event(1, Message = "Callback='{0}'", Level = EventLevel.Verbose)]
public void TraceCallback(string callback)
{
this.WriteEvent(1, callback);
}
[Event(2, Message = "Activity started, Id='{0}'", Level = EventLevel.Verbose)]
public void ActivityStarted(string id)
{
this.WriteEvent(2, id);
}
[Event(3, Message = "Activity stopped, Id='{0}'", Level = EventLevel.Verbose)]
public void ActivityStopped(string id)
{
this.WriteEvent(3, id);
}
[Event(4, Message = "Activity restored, Id='{0}'", Level = EventLevel.Informational)]
public void ActivityRestored(string id)
{
this.WriteEvent(4, id);
}
[Event(5, Message = "Failed to invoke OnExecuteRequestStep, Error='{0}'", Level = EventLevel.Error)]
public void OnExecuteRequestStepInvocationError(string error)
{
this.WriteEvent(5, error);
}
[Event(6, Message = "Activity exception, Id='{0}': {1}", Level = EventLevel.Error)]
public void ActivityException(string id, string ex)
{
this.WriteEvent(6, id, ex);
}
[Event(7, Message = "Callback exception, Id='{0}', Name='{1}': {2}", Level = EventLevel.Error)]
public void CallbackException(string id, string eventName, string ex)
{
this.WriteEvent(7, id, eventName, ex);
}
}
}

View File

@ -1,28 +0,0 @@
// <copyright file="AssemblyInfo.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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
#if SIGNED
[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.AspNet.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
#else
[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Tests")]
[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.AspNet.Tests")]
#endif

View File

@ -1,59 +0,0 @@
# Changelog
## Unreleased
## 1.0.0-rc9.4
Released 2022-Jun-03
## 1.0.0-rc9.3
Released 2022-Apr-15
* Removes .NET Framework 4.6.1. The minimum .NET Framework
version supported is .NET 4.6.2. ([#3190](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3190))
## 1.0.0-rc9.2
Released 2022-Apr-12
## 1.0.0-rc9.1
Released 2022-Mar-30
## 1.0.0-rc10 (broken. use 1.0.0-rc9.1 and newer)
Released 2022-Mar-04
## 1.0.0-rc9
Released 2022-Feb-02
## 1.0.0-rc8
Released 2021-Oct-08
* Adopted the donation
[Microsoft.AspNet.TelemetryCorrelation](https://github.com/aspnet/Microsoft.AspNet.TelemetryCorrelation)
from [.NET Foundation](https://dotnetfoundation.org/)
([#2223](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2223))
* Renamed the module, refactored existing code
([#2224](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2224)
[#2225](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2225)
[#2226](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2226)
[#2229](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2229)
[#2231](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2231)
[#2235](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2235)
[#2238](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2238)
[#2240](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2240))
* Updated to use
[ActivitySource](https://docs.microsoft.com/dotnet/api/system.diagnostics.activitysource)
& OpenTelemetry.API
([#2249](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2249) &
follow-ups (linked to #2249))
* TelemetryHttpModule will now restore Baggage on .NET 4.7.1+ runtimes when IIS
switches threads
([#2314](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2314))

View File

@ -1,26 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- OmniSharp/VS Code requires TargetFrameworks to be in descending order for IntelliSense and analysis. -->
<TargetFrameworks>net462</TargetFrameworks>
<Description>A module that instruments incoming request with System.Diagnostics.Activity and notifies listeners with DiagnosticsSource.</Description>
<PackageTags>$(PackageTags);distributed-tracing;AspNet;MVC;WebAPI</PackageTags>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Includes\ExceptionExtensions.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Api\OpenTelemetry.Api.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="web.config.install.xdt" />
<Content Include="web.config.uninstall.xdt" />
</ItemGroup>
</Project>

View File

@ -1,136 +0,0 @@
# ASP.NET Telemetry HttpModule for OpenTelemetry
[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule/)
The ASP.NET Telemetry HttpModule enables distributed tracing of incoming ASP.NET
requests using the OpenTelemetry API.
## Usage
### Step 1: Install NuGet package
If you are using the traditional `packages.config` reference style, a
`web.config` transform should run automatically and configure the
`TelemetryHttpModule` for you. If you are using the more modern PackageReference
style, this may be needed to be done manually. For more information, see:
[Migrate from packages.config to
PackageReference](https://docs.microsoft.com/nuget/consume-packages/migrate-packages-config-to-package-reference).
To configure your `web.config` manually, add this:
```xml
<system.webServer>
<modules>
<add
name="TelemetryHttpModule"
type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule,
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"
preCondition="integratedMode,managedHandler" />
</modules>
</system.webServer>
```
### Step 2: Register a listener
`TelemetryHttpModule` registers an
[ActivitySource](https://docs.microsoft.com/dotnet/api/system.diagnostics.activitysource)
with the name `OpenTelemetry.Instrumentation.AspNet.Telemetry`. By default, .NET
`ActivitySource` will not generate any `Activity` objects unless there is a
registered listener.
To register a listener automatically using OpenTelemetry, please use the
[OpenTelemetry.Instrumentation.AspNet](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNet/)
NuGet package.
To register a listener manually, use code such as the following:
```csharp
using System.Diagnostics;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using OpenTelemetry.Instrumentation.AspNet;
namespace Examples.AspNet
{
public class WebApiApplication : HttpApplication
{
private ActivityListener aspNetActivityListener;
protected void Application_Start()
{
this.aspNetActivityListener = new ActivityListener
{
ShouldListenTo = (activitySource) =>
{
// Only listen to TelemetryHttpModule's ActivitySource.
return activitySource.Name == TelemetryHttpModule.AspNetSourceName;
},
Sample = (ref ActivityCreationOptions<ActivityContext> options) =>
{
// Sample everything created by TelemetryHttpModule's ActivitySource.
return ActivitySamplingResult.AllDataAndRecorded;
},
};
ActivitySource.AddActivityListener(this.aspNetActivityListener);
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
protected void Application_End()
{
this.aspNetActivityListener?.Dispose();
}
}
}
```
## Options
`TelemetryHttpModule` provides a static options property
(`TelemetryHttpModule.Options`) which can be used to configure the
`TelemetryHttpModule` and listen to events it fires.
### TextMapPropagator
`TextMapPropagator` controls how trace context will be extracted from incoming
Http request messages. By default, [W3C Trace
Context](https://www.w3.org/TR/trace-context/) is enabled.
The OpenTelemetry API ships with a handful of [standard
implementations](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry.Api/Context/Propagation)
which may be used, or you can write your own by deriving from the
`TextMapPropagator` class.
To add support for
[Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/baggage/api.md)
propagation in addition to W3C Trace Context, use:
```csharp
TelemetryHttpModuleOptions.TextMapPropagator = new CompositeTextMapPropagator(
new TextMapPropagator[]
{
new TraceContextPropagator(),
new BaggagePropagator(),
});
```
Note: When using the `OpenTelemetry.Instrumentation.AspNet`
`TelemetryHttpModuleOptions.TextMapPropagator` is automatically initialized to
the SDK default propagator (`Propagators.DefaultTextMapPropagator`) which by
default supports W3C Trace Context & Baggage.
### Events
`OnRequestStartedCallback`, `OnRequestStoppedCallback`, & `OnExceptionCallback`
are provided on `TelemetryHttpModuleOptions` and will be fired by the
`TelemetryHttpModule` as requests are processed.
A typical use case for these events is to add information (tags, events, and/or
links) to the created `Activity` based on the request, response, and/or
exception event being fired.

View File

@ -1,147 +0,0 @@
// <copyright file="TelemetryHttpModule.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;
using System.Diagnostics;
using System.Reflection;
using System.Web;
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Http Module sets ambient state using Activity API from DiagnosticsSource package.
/// </summary>
public class TelemetryHttpModule : IHttpModule
{
/// <summary>
/// OpenTelemetry.Instrumentation.AspNet <see cref="ActivitySource"/> name.
/// </summary>
public const string AspNetSourceName = "OpenTelemetry.Instrumentation.AspNet.Telemetry";
/// <summary>
/// <see cref="Activity.OperationName"/> for OpenTelemetry.Instrumentation.AspNet created <see cref="Activity"/> objects.
/// </summary>
public const string AspNetActivityName = "Microsoft.AspNet.HttpReqIn";
// ServerVariable set only on rewritten HttpContext by URL Rewrite module.
private const string URLRewriteRewrittenRequest = "IIS_WasUrlRewritten";
// ServerVariable set on every request if URL module is registered in HttpModule pipeline.
private const string URLRewriteModuleVersion = "IIS_UrlRewriteModule";
private static readonly MethodInfo OnExecuteRequestStepMethodInfo = typeof(HttpApplication).GetMethod("OnExecuteRequestStep");
/// <summary>
/// Gets the <see cref="TelemetryHttpModuleOptions"/> applied to requests processed by the handler.
/// </summary>
public static TelemetryHttpModuleOptions Options { get; } = new TelemetryHttpModuleOptions();
/// <inheritdoc />
public void Dispose()
{
}
/// <inheritdoc />
public void Init(HttpApplication context)
{
context.BeginRequest += this.Application_BeginRequest;
context.EndRequest += this.Application_EndRequest;
context.Error += this.Application_Error;
if (HttpRuntime.UsingIntegratedPipeline && OnExecuteRequestStepMethodInfo != null)
{
// OnExecuteRequestStep is availabile starting with 4.7.1
try
{
OnExecuteRequestStepMethodInfo.Invoke(context, new object[] { (Action<HttpContextBase, Action>)this.OnExecuteRequestStep });
}
catch (Exception e)
{
AspNetTelemetryEventSource.Log.OnExecuteRequestStepInvocationError(e.Message);
}
}
}
private void Application_BeginRequest(object sender, EventArgs e)
{
AspNetTelemetryEventSource.Log.TraceCallback("Application_BeginRequest");
ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, ((HttpApplication)sender).Context, Options.OnRequestStartedCallback);
}
private void OnExecuteRequestStep(HttpContextBase context, Action step)
{
// Called only on 4.7.1+ runtimes
if (context.CurrentNotification == RequestNotification.ExecuteRequestHandler && !context.IsPostNotification)
{
ActivityHelper.RestoreContextIfNeeded(context.ApplicationInstance.Context);
}
step();
}
private void Application_EndRequest(object sender, EventArgs e)
{
AspNetTelemetryEventSource.Log.TraceCallback("Application_EndRequest");
bool trackActivity = true;
var context = ((HttpApplication)sender).Context;
if (!ActivityHelper.HasStarted(context, out Activity aspNetActivity))
{
// Rewrite: In case of rewrite, a new request context is created, called the child request, and it goes through the entire IIS/ASP.NET integrated pipeline.
// The child request can be mapped to any of the handlers configured in IIS, and it's execution is no different than it would be if it was received via the HTTP stack.
// The parent request jumps ahead in the pipeline to the end request notification, and waits for the child request to complete.
// When the child request completes, the parent request executes the end request notifications and completes itself.
// Do not create activity for parent request. Parent request has IIS_UrlRewriteModule ServerVariable with success response code.
// Child request contains an additional ServerVariable named - IIS_WasUrlRewritten.
// Track failed response activity: Different modules in the pipeline has ability to end the response. For example, authentication module could set HTTP 401 in OnBeginRequest and end the response.
if (context.Request.ServerVariables != null && context.Request.ServerVariables[URLRewriteRewrittenRequest] == null && context.Request.ServerVariables[URLRewriteModuleVersion] != null && context.Response.StatusCode == 200)
{
trackActivity = false;
}
else
{
// Activity has never been started
aspNetActivity = ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, context, Options.OnRequestStartedCallback);
}
}
if (trackActivity)
{
ActivityHelper.StopAspNetActivity(Options.TextMapPropagator, aspNetActivity, context, Options.OnRequestStoppedCallback);
}
}
private void Application_Error(object sender, EventArgs e)
{
AspNetTelemetryEventSource.Log.TraceCallback("Application_Error");
var context = ((HttpApplication)sender).Context;
var exception = context.Error;
if (exception != null)
{
if (!ActivityHelper.HasStarted(context, out Activity aspNetActivity))
{
aspNetActivity = ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, context, Options.OnRequestStartedCallback);
}
ActivityHelper.WriteActivityException(aspNetActivity, context, exception, Options.OnExceptionCallback);
}
}
}
}

View File

@ -1,67 +0,0 @@
// <copyright file="TelemetryHttpModuleOptions.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;
using System.Diagnostics;
using System.Web;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Stores options for the <see cref="TelemetryHttpModule"/>.
/// </summary>
public class TelemetryHttpModuleOptions
{
private TextMapPropagator textMapPropagator = new TraceContextPropagator();
internal TelemetryHttpModuleOptions()
{
}
/// <summary>
/// Gets or sets the <see cref=" Context.Propagation.TextMapPropagator"/> to use to
/// extract <see cref="PropagationContext"/> from incoming requests.
/// </summary>
public TextMapPropagator TextMapPropagator
{
get => this.textMapPropagator;
set
{
Guard.ThrowIfNull(value);
this.textMapPropagator = value;
}
}
/// <summary>
/// Gets or sets a callback action to be fired when a request is started.
/// </summary>
public Action<Activity, HttpContext> OnRequestStartedCallback { get; set; }
/// <summary>
/// Gets or sets a callback action to be fired when a request is stopped.
/// </summary>
public Action<Activity, HttpContext> OnRequestStoppedCallback { get; set; }
/// <summary>
/// Gets or sets a callback action to be fired when an unhandled
/// exception is thrown processing a request.
/// </summary>
public Action<Activity, HttpContext, Exception> OnExceptionCallback { get; set; }
}
}

View File

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!-- Insert global system.web node in any case -->
<system.web xdt:Transform="InsertIfMissing">
</system.web>
<!-- Search for system.web nodes:
- globally
- under location[@path='.']
- under location[count(@path)=0]
If any of above contains httpModules section - it will be reused.
Otherwise it will be created under /configuration/system.web (globally)
-->
<system.web xdt:Locator="XPath(//system.web[(count(parent::location) = 0) or (count(parent::location[@path != '.' and count(@path) != 0]) = 0)])">
<httpModules xdt:Transform="InsertIfMissing">
<add name="TelemetryHttpModule" type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" xdt:Transform="Remove" xdt:Locator="Match(type)"/>
<add name="TelemetryHttpModule" type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" xdt:Transform="InsertIfMissing" xdt:Locator="Match(type)"/>
</httpModules>
</system.web>
<system.webServer xdt:Transform="InsertIfMissing">
</system.webServer>
<!-- Only one validation node is allowed to be deinfed globally or in global location tags. See explanaition above for httpModules on how it works -->
<system.webServer xdt:Locator="XPath(//system.webServer[(count(parent::location) = 0) or (count(parent::location[@path != '.' and count(@path) != 0]) = 0)])">
<validation validateIntegratedModeConfiguration="false" xdt:Transform="InsertIfMissing" />
</system.webServer>
<!-- Search for system.web nodes:
- globally
- under location[@path='.']
- under location[count(@path)=0]
If any of above contains httpModules section - it will be reused.
Otherwise it will be created under /configuration/system.web (globally)
see https://github.com/Microsoft/ApplicationInsights-dotnet-server/blob/develop/Src/Web/Web.Nuget/Resources/web.config.install.xdt
-->
<system.webServer xdt:Locator="XPath(//system.webServer[(count(parent::location) = 0) or (count(parent::location[@path != '.' and count(@path) != 0]) = 0)])">
<modules xdt:Transform="InsertIfMissing">
<remove name="TelemetryHttpModule" xdt:Transform="InsertIfMissing" xdt:Locator="Match(name)"/>
<add name="TelemetryHttpModule" type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"
preCondition="managedHandler" xdt:Transform="Remove" xdt:Locator="Match(type)"/>
<add name="TelemetryHttpModule" type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"
preCondition="managedHandler" xdt:Transform="InsertIfMissing" xdt:Locator="Match(type)"/>
</modules>
</system.webServer>
</configuration>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.web xdt:Locator="XPath(//system.web[(count(parent::location) = 0) or (count(parent::location[@path != '.' and count(@path) != 0]) = 0)])">
<httpModules>
<add name="TelemetryHttpModule" type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" preCondition="managedHandler" xdt:Transform="Remove" xdt:Locator="Match(type)"/>
</httpModules>
</system.web>
<system.webServer>
<modules>
<remove name="TelemetryHttpModule" xdt:Transform="Remove" xdt:Locator="Match(name)"/>
<add name="TelemetryHttpModule"
type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"
preCondition="managedHandler" xdt:Transform="Remove" xdt:Locator="Match(type)"/>
</modules>
</system.webServer>
</configuration>

View File

@ -1,12 +0,0 @@
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.AspNetInstrumentationOptions() -> void
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.Enrich.get -> System.Action<System.Diagnostics.Activity, string, object>
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.Enrich.set -> void
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.Filter.get -> System.Func<System.Web.HttpContext, bool>
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.Filter.set -> void
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.RecordException.get -> bool
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.RecordException.set -> void
OpenTelemetry.Metrics.MeterProviderBuilderExtensions
OpenTelemetry.Trace.TracerProviderBuilderExtensions
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddAspNetInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddAspNetInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action<OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions> configureAspNetInstrumentationOptions = null) -> OpenTelemetry.Trace.TracerProviderBuilder

View File

@ -1,44 +0,0 @@
// <copyright file="AspNetInstrumentation.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;
using OpenTelemetry.Instrumentation.AspNet.Implementation;
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Asp.Net Requests instrumentation.
/// </summary>
internal sealed class AspNetInstrumentation : IDisposable
{
private readonly HttpInListener httpInListener;
/// <summary>
/// Initializes a new instance of the <see cref="AspNetInstrumentation"/> class.
/// </summary>
/// <param name="options">Configuration options for ASP.NET instrumentation.</param>
public AspNetInstrumentation(AspNetInstrumentationOptions options)
{
this.httpInListener = new HttpInListener(options);
}
/// <inheritdoc/>
public void Dispose()
{
this.httpInListener?.Dispose();
}
}
}

View File

@ -1,63 +0,0 @@
// <copyright file="AspNetInstrumentationOptions.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;
using System.Diagnostics;
using System.Web;
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Options for ASP.NET instrumentation.
/// </summary>
public class AspNetInstrumentationOptions
{
/// <summary>
/// Gets or sets a filter callback function that determines on a per
/// request basis whether or not to collect telemetry.
/// </summary>
/// <remarks>
/// The filter callback receives the <see cref="HttpContext"/> for the
/// current request and should return a boolean.
/// <list type="bullet">
/// <item>If filter returns <see langword="true"/> the request is
/// collected.</item>
/// <item>If filter returns <see langword="false"/> or throws an
/// exception the request is filtered out (NOT collected).</item>
/// </list>
/// </remarks>
public Func<HttpContext, bool> Filter { get; set; }
/// <summary>
/// Gets or sets an action to enrich an Activity.
/// </summary>
/// <remarks>
/// <para><see cref="Activity"/>: the activity being enriched.</para>
/// <para>string: the name of the event.</para>
/// <para>object: the raw object from which additional information can be extracted to enrich the activity.
/// The type of this object depends on the event, which is given by the above parameter.</para>
/// </remarks>
public Action<Activity, string, object> Enrich { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the exception will be recorded as ActivityEvent or not.
/// </summary>
/// <remarks>
/// See: <see href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md"/>.
/// </remarks>
public bool RecordException { get; set; }
}
}

View File

@ -1,53 +0,0 @@
// <copyright file="AspNetMetrics.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;
using System.Diagnostics.Metrics;
using System.Reflection;
using OpenTelemetry.Instrumentation.AspNet.Implementation;
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Asp.Net Requests instrumentation.
/// </summary>
internal class AspNetMetrics : IDisposable
{
internal static readonly AssemblyName AssemblyName = typeof(HttpInMetricsListener).Assembly.GetName();
internal static readonly string InstrumentationName = AssemblyName.Name;
internal static readonly string InstrumentationVersion = AssemblyName.Version.ToString();
private readonly Meter meter;
private readonly HttpInMetricsListener httpInMetricsListener;
/// <summary>
/// Initializes a new instance of the <see cref="AspNetMetrics"/> class.
/// </summary>
public AspNetMetrics()
{
this.meter = new Meter(InstrumentationName, InstrumentationVersion);
this.httpInMetricsListener = new HttpInMetricsListener(this.meter);
}
/// <inheritdoc/>
public void Dispose()
{
this.meter?.Dispose();
this.httpInMetricsListener?.Dispose();
}
}
}

View File

@ -1,22 +0,0 @@
// <copyright file="AssemblyInfo.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.Runtime.CompilerServices;
#if SIGNED
[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.AspNet.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
#else
[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.AspNet.Tests")]
#endif

View File

@ -1,162 +0,0 @@
# Changelog
## Unreleased
## 1.0.0-rc9.4
Released 2022-Jun-03
## 1.0.0-rc9.3
Released 2022-Apr-15
* Removes .NET Framework 4.6.1. The minimum .NET Framework
version supported is .NET 4.6.2. ([#3190](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3190))
## 1.0.0-rc9.2
Released 2022-Apr-12
## 1.0.0-rc9.1
Released 2022-Mar-30
* Added ASP.NET metrics instrumentation to collect `http.server.duration`.
([#2985](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2985))
* Fix: Http server span status is now unset for `400`-`499`.
([#2904](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2904))
## 1.0.0-rc10 (broken. use 1.0.0-rc9.1 and newer)
Released 2022-Mar-04
## 1.0.0-rc9
Released 2022-Feb-02
## 1.0.0-rc8
Released 2021-Oct-08
* Removes .NET Framework 4.5.2, .NET 4.6 support. The minimum .NET Framework
version supported is .NET 4.6.1. ([#2138](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2138))
* Replaced `http.path` tag on activity with `http.target`.
([#2266](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2266))
* ASP.NET instrumentation now uses
[OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule/)
instead of
[Microsoft.AspNet.TelemetryCorrelation](https://www.nuget.org/packages/Microsoft.AspNet.TelemetryCorrelation/)
to listen for incoming http requests to the process. Please see the (Step 2:
Modify
Web.config)[https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry.Instrumentation.AspNet#step-2-modify-webconfig]
README section for details on the new HttpModule definition required.
([#2222](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2222))
* Added `RecordException` option. Specify `true` to have unhandled exception
details automatically captured on spans.
([#2256](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2256))
## 1.0.0-rc7
Released 2021-Jul-12
## 1.0.0-rc6
Released 2021-Jun-25
## 1.0.0-rc5
Released 2021-Jun-09
## 1.0.0-rc4
Released 2021-Apr-23
* Sanitize `http.url` attribute. ([#1961](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1961))
## 1.0.0-rc3
Released 2021-Mar-19
* Leverages added AddLegacySource API from OpenTelemetry SDK to trigger Samplers
and ActivityProcessors. Samplers, ActivityProcessor.OnStart will now get the
Activity before any enrichment done by the instrumentation.
([#1836](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1836))
* Performance optimization by leveraging sampling decision and short circuiting
activity enrichment.
([#1903](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1903))
## 1.0.0-rc2
Released 2021-Jan-29
## 1.0.0-rc1.1
Released 2020-Nov-17
* AspNetInstrumentation sets ActivitySource to activities created outside
ActivitySource.
([#1515](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1515/))
## 0.8.0-beta.1
Released 2020-Nov-5
* Renamed TextMapPropagator to TraceContextPropagator, CompositePropagator to
CompositeTextMapPropagator. IPropagator is renamed to TextMapPropagator and
changed from interface to abstract class.
([#1427](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1427))
* Propagators.DefaultTextMapPropagator will be used as the default Propagator.
([#1427](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1428))
* Removed Propagator from Instrumentation Options. Instrumentation now always
respect the Propagator.DefaultTextMapPropagator.
([#1448](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1448))
## 0.7.0-beta.1
Released 2020-Oct-16
* Instrumentation no longer store raw objects like `HttpRequest` in
Activity.CustomProperty. To enrich activity, use the Enrich action on the
instrumentation.
([#1261](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1261))
* Span Status is populated as per new spec
([#1313](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1313))
## 0.6.0-beta.1
Released 2020-Sep-15
## 0.5.0-beta.2
Released 2020-08-28
* Added Filter public API on AspNetInstrumentationOptions to allow filtering of
instrumentation based on HttpContext.
* Asp.Net Instrumentation automatically populates HttpRequest, HttpResponse in
Activity custom property
* Changed the default propagation to support W3C Baggage
([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048))
* The default ITextFormat is now `CompositePropagator(TraceContextFormat,
BaggageFormat)`. Baggage sent via the [W3C
Baggage](https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md)
header will now be parsed and set on incoming Http spans.
* Renamed `ITextPropagator` to `IPropagator`
([#1190](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1190))
## 0.4.0-beta.2
Released 2020-07-24
* First beta release
## 0.3.0-beta
Released 2020-07-23
* Initial release

View File

@ -1,67 +0,0 @@
// <copyright file="AspNetInstrumentationEventSource.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;
using System.Diagnostics.Tracing;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Instrumentation.AspNet.Implementation
{
/// <summary>
/// EventSource events emitted from the project.
/// </summary>
[EventSource(Name = "OpenTelemetry-Instrumentation-AspNet")]
internal sealed class AspNetInstrumentationEventSource : EventSource
{
public static AspNetInstrumentationEventSource Log = new();
[NonEvent]
public void RequestFilterException(string operationName, Exception ex)
{
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.RequestFilterException(operationName, ex.ToInvariantString());
}
}
[NonEvent]
public void EnrichmentException(string eventName, Exception ex)
{
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.EnrichmentException(eventName, ex.ToInvariantString());
}
}
[Event(1, Message = "Request is filtered out and will not be collected. Operation='{0}'", Level = EventLevel.Verbose)]
public void RequestIsFilteredOut(string operationName)
{
this.WriteEvent(1, operationName);
}
[Event(2, Message = "Filter callback threw an exception. Request will not be collected. Operation='{0}': {1}", Level = EventLevel.Error)]
public void RequestFilterException(string operationName, string exception)
{
this.WriteEvent(2, operationName, exception);
}
[Event(3, Message = "Enrich callback threw an exception. Event='{0}': {1}", Level = EventLevel.Error)]
public void EnrichmentException(string eventName, string exception)
{
this.WriteEvent(3, eventName, exception);
}
}
}

View File

@ -1,197 +0,0 @@
// <copyright file="HttpInListener.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;
using System.Diagnostics;
using System.Web;
using System.Web.Routing;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Internal;
using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.AspNet.Implementation
{
internal sealed class HttpInListener : IDisposable
{
private readonly PropertyFetcher<object> routeFetcher = new("Route");
private readonly PropertyFetcher<string> routeTemplateFetcher = new("RouteTemplate");
private readonly AspNetInstrumentationOptions options;
public HttpInListener(AspNetInstrumentationOptions options)
{
Guard.ThrowIfNull(options);
this.options = options;
TelemetryHttpModule.Options.TextMapPropagator = Propagators.DefaultTextMapPropagator;
TelemetryHttpModule.Options.OnRequestStartedCallback += this.OnStartActivity;
TelemetryHttpModule.Options.OnRequestStoppedCallback += this.OnStopActivity;
TelemetryHttpModule.Options.OnExceptionCallback += this.OnException;
}
public void Dispose()
{
TelemetryHttpModule.Options.OnRequestStartedCallback -= this.OnStartActivity;
TelemetryHttpModule.Options.OnRequestStoppedCallback -= this.OnStopActivity;
TelemetryHttpModule.Options.OnExceptionCallback -= this.OnException;
}
/// <summary>
/// Gets the OpenTelemetry standard uri tag value for a span based on its request <see cref="Uri"/>.
/// </summary>
/// <param name="uri"><see cref="Uri"/>.</param>
/// <returns>Span uri value.</returns>
private static string GetUriTagValueFromRequestUri(Uri uri)
{
return string.IsNullOrEmpty(uri.UserInfo) ? uri.ToString() : string.Concat(uri.Scheme, Uri.SchemeDelimiter, uri.Authority, uri.PathAndQuery, uri.Fragment);
}
private void OnStartActivity(Activity activity, HttpContext context)
{
if (activity.IsAllDataRequested)
{
try
{
// todo: Ideally we would also check
// Sdk.SuppressInstrumentation here to prevent tagging a
// span that will not be collected but we can't do that
// without an SDK reference. Need the spec to come around on
// this.
if (this.options.Filter?.Invoke(context) == false)
{
AspNetInstrumentationEventSource.Log.RequestIsFilteredOut(activity.OperationName);
activity.IsAllDataRequested = false;
activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
return;
}
}
catch (Exception ex)
{
AspNetInstrumentationEventSource.Log.RequestFilterException(activity.OperationName, ex);
activity.IsAllDataRequested = false;
activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
return;
}
var request = context.Request;
var requestValues = request.Unvalidated;
// see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md
var path = requestValues.Path;
activity.DisplayName = path;
if (request.Url.Port == 80 || request.Url.Port == 443)
{
activity.SetTag(SemanticConventions.AttributeHttpHost, request.Url.Host);
}
else
{
activity.SetTag(SemanticConventions.AttributeHttpHost, request.Url.Host + ":" + request.Url.Port);
}
activity.SetTag(SemanticConventions.AttributeHttpMethod, request.HttpMethod);
activity.SetTag(SemanticConventions.AttributeHttpTarget, path);
activity.SetTag(SemanticConventions.AttributeHttpUserAgent, request.UserAgent);
activity.SetTag(SemanticConventions.AttributeHttpUrl, GetUriTagValueFromRequestUri(request.Url));
try
{
this.options.Enrich?.Invoke(activity, "OnStartActivity", request);
}
catch (Exception ex)
{
AspNetInstrumentationEventSource.Log.EnrichmentException("OnStartActivity", ex);
}
}
}
private void OnStopActivity(Activity activity, HttpContext context)
{
if (activity.IsAllDataRequested)
{
var response = context.Response;
activity.SetTag(SemanticConventions.AttributeHttpStatusCode, response.StatusCode);
if (activity.GetStatus().StatusCode == StatusCode.Unset)
{
activity.SetStatus(SpanHelper.ResolveSpanStatusForHttpStatusCode(activity.Kind, response.StatusCode));
}
var routeData = context.Request.RequestContext.RouteData;
string template = null;
if (routeData.Values.TryGetValue("MS_SubRoutes", out object msSubRoutes))
{
// WebAPI attribute routing flows here. Use reflection to not take a dependency on microsoft.aspnet.webapi.core\[version]\lib\[framework]\System.Web.Http.
if (msSubRoutes is Array attributeRouting && attributeRouting.Length == 1)
{
var subRouteData = attributeRouting.GetValue(0);
_ = this.routeFetcher.TryFetch(subRouteData, out var route);
_ = this.routeTemplateFetcher.TryFetch(route, out template);
}
}
else if (routeData.Route is Route route)
{
// MVC + WebAPI traditional routing & MVC attribute routing flow here.
template = route.Url;
}
if (!string.IsNullOrEmpty(template))
{
// Override the name that was previously set to the path part of URL.
activity.DisplayName = template;
activity.SetTag(SemanticConventions.AttributeHttpRoute, template);
}
try
{
this.options.Enrich?.Invoke(activity, "OnStopActivity", response);
}
catch (Exception ex)
{
AspNetInstrumentationEventSource.Log.EnrichmentException("OnStopActivity", ex);
}
}
}
private void OnException(Activity activity, HttpContext context, Exception exception)
{
if (activity.IsAllDataRequested)
{
if (this.options.RecordException)
{
activity.RecordException(exception);
}
activity.SetStatus(Status.Error.WithDescription(exception.Message));
try
{
this.options.Enrich?.Invoke(activity, "OnException", exception);
}
catch (Exception ex)
{
AspNetInstrumentationEventSource.Log.EnrichmentException("OnException", ex);
}
}
}
}
}

View File

@ -1,54 +0,0 @@
// <copyright file="HttpInMetricsListener.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;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Web;
using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.AspNet.Implementation
{
internal sealed class HttpInMetricsListener : IDisposable
{
private readonly Histogram<double> httpServerDuration;
public HttpInMetricsListener(Meter meter)
{
this.httpServerDuration = meter.CreateHistogram<double>("http.server.duration", "ms", "measures the duration of the inbound HTTP request");
TelemetryHttpModule.Options.OnRequestStoppedCallback += this.OnStopActivity;
}
public void Dispose()
{
TelemetryHttpModule.Options.OnRequestStoppedCallback -= this.OnStopActivity;
}
private void OnStopActivity(Activity activity, HttpContext context)
{
// TODO: This is just a minimal set of attributes. See the spec for additional attributes:
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#http-server
var tags = new TagList
{
{ SemanticConventions.AttributeHttpMethod, context.Request.HttpMethod },
{ SemanticConventions.AttributeHttpScheme, context.Request.Url.Scheme },
{ SemanticConventions.AttributeHttpStatusCode, context.Response.StatusCode },
};
this.httpServerDuration.Record(activity.Duration.TotalMilliseconds, tags);
}
}
}

View File

@ -1,42 +0,0 @@
// <copyright file="MeterProviderBuilderExtensions.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 OpenTelemetry.Instrumentation.AspNet;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Metrics
{
/// <summary>
/// Extension methods to simplify registering of ASP.NET request instrumentation.
/// </summary>
public static class MeterProviderBuilderExtensions
{
/// <summary>
/// Enables the incoming requests automatic data collection for ASP.NET.
/// </summary>
/// <param name="builder"><see cref="MeterProviderBuilder"/> being configured.</param>
/// <returns>The instance of <see cref="MeterProviderBuilder"/> to chain the calls.</returns>
public static MeterProviderBuilder AddAspNetInstrumentation(
this MeterProviderBuilder builder)
{
Guard.ThrowIfNull(builder);
var instrumentation = new AspNetMetrics();
builder.AddMeter(AspNetMetrics.InstrumentationName);
return builder.AddInstrumentation(() => instrumentation);
}
}
}

View File

@ -1,24 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- OmniSharp/VS Code requires TargetFrameworks to be in descending order for IntelliSense and analysis. -->
<TargetFrameworks>net462</TargetFrameworks>
<Description>ASP.NET instrumentation for OpenTelemetry .NET</Description>
<PackageTags>$(PackageTags);distributed-tracing;AspNet;MVC;WebAPI</PackageTags>
<IncludeInstrumentationHelpers>true</IncludeInstrumentationHelpers>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(RepoRoot)\src\OpenTelemetry\DiagnosticSourceInstrumentation\PropertyFetcher.cs" Link="Includes\PropertyFetcher.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Api\OpenTelemetry.Api.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.csproj" />
</ItemGroup>
</Project>

View File

@ -1,167 +0,0 @@
# ASP.NET Instrumentation for OpenTelemetry
[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.AspNet.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNet)
[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Instrumentation.AspNet.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNet)
This is an [Instrumentation
Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library),
which instruments [ASP.NET](https://docs.microsoft.com/aspnet/overview) and
collect metrics and traces about incoming web requests.
**Note: This component is based on the OpenTelemetry semantic conventions for
[metrics](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/metrics/semantic_conventions)
and
[traces](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions).
These conventions are
[Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/document-status.md),
and hence, this package is a [pre-release](../../VERSIONING.md#pre-releases).
Until a [stable
version](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/telemetry-stability.md)
is released, there can be breaking changes. You can track the progress from
[milestones](https://github.com/open-telemetry/opentelemetry-dotnet/milestone/23).**
## Steps to enable OpenTelemetry.Instrumentation.AspNet
### Step 1: Install Package
Add a reference to the
[`OpenTelemetry.Instrumentation.AspNet`](https://www.nuget.org/packages/opentelemetry.instrumentation.aspnet)
package. Also, add any other instrumentations & exporters you will need.
```shell
dotnet add package OpenTelemetry.Instrumentation.AspNet
```
### Step 2: Modify Web.config
`OpenTelemetry.Instrumentation.AspNet` requires adding an additional HttpModule
to your web server. This additional HttpModule is shipped as part of
[`OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule`](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule/)
which is implicitly brought by `OpenTelemetry.Instrumentation.AspNet`. The
following shows changes required to your `Web.config` when using IIS web server.
```xml
<system.webServer>
<modules>
<add
name="TelemetryHttpModule"
type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule,
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"
preCondition="integratedMode,managedHandler" />
</modules>
</system.webServer>
```
### Step 3: Enable ASP.NET Instrumentation at application startup
ASP.NET instrumentation must be enabled at application startup. This is
typically done in the `Global.asax.cs` as shown below. This example also sets up
the OpenTelemetry Jaeger exporter, which requires adding the package
[`OpenTelemetry.Exporter.Jaeger`](../OpenTelemetry.Exporter.Jaeger/README.md) to
the application.
```csharp
using OpenTelemetry;
using OpenTelemetry.Trace;
public class WebApiApplication : HttpApplication
{
private TracerProvider tracerProvider;
protected void Application_Start()
{
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation()
.AddJaegerExporter()
.Build();
}
protected void Application_End()
{
this.tracerProvider?.Dispose();
}
}
```
## Advanced configuration
This instrumentation can be configured to change the default behavior by using
`AspNetInstrumentationOptions`, which allows configuring `Filter` as explained
below.
### Filter
This instrumentation by default collects all the incoming http requests. It
allows filtering of requests by using the `Filter` function in
`AspNetInstrumentationOptions`. This defines the condition for allowable
requests. The Filter receives the `HttpContext` of the incoming request, and
does not collect telemetry about the request if the Filter returns false or
throws exception.
The following code snippet shows how to use `Filter` to only allow GET requests.
```csharp
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation(
(options) => options.Filter =
(httpContext) =>
{
// only collect telemetry about HTTP GET requests
return httpContext.Request.HttpMethod.Equals("GET");
})
.Build();
```
It is important to note that this `Filter` option is specific to this
instrumentation. OpenTelemetry has a concept of a
[Sampler](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling),
and the `Filter` option does the filtering *before* the Sampler is invoked.
### Enrich
This option allows one to enrich the activity with additional information from
the raw `HttpRequest`, `HttpResponse` objects. The `Enrich` action is called
only when `activity.IsAllDataRequested` is `true`. It contains the activity
itself (which can be enriched), the name of the event, and the actual raw
object. For event name "OnStartActivity", the actual object will be
`HttpRequest`. For event name "OnStopActivity", the actual object will be
`HttpResponse`
The following code snippet shows how to add additional tags using `Enrich`.
```csharp
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation((options) => options.Enrich
= (activity, eventName, rawObject) =>
{
if (eventName.Equals("OnStartActivity"))
{
if (rawObject is HttpRequest httpRequest)
{
activity.SetTag("physicalPath", httpRequest.PhysicalPath);
}
}
else if (eventName.Equals("OnStopActivity"))
{
if (rawObject is HttpResponse httpResponse)
{
activity.SetTag("responseType", httpResponse.ContentType);
}
}
})
.Build();
```
[Processor](../../docs/trace/extending-the-sdk/README.md#processor), is the
general extensibility point to add additional properties to any activity. The
`Enrich` option is specific to this instrumentation, and is provided to get
access to `HttpRequest` and `HttpResponse`.
### RecordException
This instrumentation automatically sets Activity Status to Error if an unhandled
exception is thrown. Additionally, `RecordException` feature may be turned on,
to store the exception to the Activity itself as ActivityEvent.
## References
* [ASP.NET](https://dotnet.microsoft.com/apps/aspnet)
* [OpenTelemetry Project](https://opentelemetry.io/)

View File

@ -1,49 +0,0 @@
// <copyright file="TracerProviderBuilderExtensions.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;
using OpenTelemetry.Instrumentation.AspNet;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Trace
{
/// <summary>
/// Extension methods to simplify registering of ASP.NET request instrumentation.
/// </summary>
public static class TracerProviderBuilderExtensions
{
/// <summary>
/// Enables the incoming requests automatic data collection for ASP.NET.
/// </summary>
/// <param name="builder"><see cref="TracerProviderBuilder"/> being configured.</param>
/// <param name="configureAspNetInstrumentationOptions">ASP.NET Request configuration options.</param>
/// <returns>The instance of <see cref="TracerProviderBuilder"/> to chain the calls.</returns>
public static TracerProviderBuilder AddAspNetInstrumentation(
this TracerProviderBuilder builder,
Action<AspNetInstrumentationOptions> configureAspNetInstrumentationOptions = null)
{
Guard.ThrowIfNull(builder);
var aspnetOptions = new AspNetInstrumentationOptions();
configureAspNetInstrumentationOptions?.Invoke(aspnetOptions);
builder.AddInstrumentation(() => new AspNetInstrumentation(aspnetOptions));
builder.AddSource(TelemetryHttpModule.AspNetSourceName);
return builder;
}
}
}

View File

@ -3,8 +3,8 @@
[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.Http.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Http)
[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Instrumentation.Http.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Http)
This is an
[Instrumentation Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library),
This is an [Instrumentation
Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library),
which instruments
[System.Net.Http.HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient)
and
@ -27,7 +27,8 @@ is released, there can be breaking changes. You can track the progress from
### Step 1: Install Package
Add a reference to the [`OpenTelemetry.Instrumentation.Http`](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Http)
Add a reference to the
[`OpenTelemetry.Instrumentation.Http`](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.Http)
package. Also, add any other instrumentations & exporters you will need.
```shell
@ -38,9 +39,9 @@ dotnet add package OpenTelemetry.Instrumentation.Http
HTTP instrumentation must be enabled at application startup.
The following example demonstrates adding HTTP instrumentation to a
console application. This example also sets up the OpenTelemetry Console
exporter, which requires adding the package
The following example demonstrates adding HTTP instrumentation to a console
application. This example also sets up the OpenTelemetry Console exporter, which
requires adding the package
[`OpenTelemetry.Exporter.Console`](../OpenTelemetry.Exporter.Console/README.md)
to the application.
@ -59,12 +60,13 @@ public class Program
}
```
For an ASP.NET Core application, adding instrumentation is typically done in
the `ConfigureServices` of your `Startup` class. Refer to documentation for
For an ASP.NET Core application, adding instrumentation is typically done in the
`ConfigureServices` of your `Startup` class. Refer to documentation for
[OpenTelemetry.Instrumentation.AspNetCore](../OpenTelemetry.Instrumentation.AspNetCore/README.md).
For an ASP.NET application, adding instrumentation is typically done in the
`Global.asax.cs`. Refer to documentation for [OpenTelemetry.Instrumentation.AspNet](../OpenTelemetry.Instrumentation.AspNet/README.md).
`Global.asax.cs`. Refer to the documentation for
[OpenTelemetry.Instrumentation.AspNet](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/main/src/OpenTelemetry.Instrumentation.AspNet/README.md).
## Advanced configuration
@ -80,9 +82,9 @@ is used.
### SetHttpFlavor
By default, this instrumentation does not add the `http.flavor` attribute. The
`http.flavor` attribute specifies the kind of HTTP protocol used
(e.g., `1.1` for HTTP 1.1). The `SetHttpFlavor` option can be used to include
the `http.flavor` attribute.
`http.flavor` attribute specifies the kind of HTTP protocol used (e.g., `1.1`
for HTTP 1.1). The `SetHttpFlavor` option can be used to include the
`http.flavor` attribute.
The following example shows how to use `SetHttpFlavor`.
@ -104,8 +106,7 @@ the condition for allowable requests. The Filter receives the request object -
representing the outgoing request and does not collect telemetry about the
request if the Filter returns false or throws exception.
The following code snippet shows how to use `Filter` to only allow GET
requests.
The following code snippet shows how to use `Filter` to only allow GET requests.
```csharp
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
@ -142,8 +143,7 @@ For event name "OnStartActivity", the actual object will be
For event name "OnStopActivity", the actual object will be
`HttpResponseMessage`.
For event name "OnException", the actual object will be
`Exception`.
For event name "OnException", the actual object will be `Exception`.
Example:
@ -180,14 +180,11 @@ var tracerProvider = Sdk.CreateTracerProviderBuilder()
#### HttpWebRequestInstrumentationOptions
For event name "OnStartActivity", the actual object will be
`HttpWebRequest`.
For event name "OnStartActivity", the actual object will be `HttpWebRequest`.
For event name "OnStopActivity", the actual object will be
`HttpWebResponse`.
For event name "OnStopActivity", the actual object will be `HttpWebResponse`.
For event name "OnException", the actual object will be
`Exception`.
For event name "OnException", the actual object will be `Exception`.
Example:
@ -222,10 +219,10 @@ var tracerProvider = Sdk.CreateTracerProviderBuilder()
}).Build();
```
[Processor](../../docs/trace/extending-the-sdk/README.md#processor),
is the general extensibility point to add additional properties to any
activity. The `Enrich` option is specific to this instrumentation, and is
provided to get access to raw request, response, and exception objects.
[Processor](../../docs/trace/extending-the-sdk/README.md#processor), is the
general extensibility point to add additional properties to any activity. The
`Enrich` option is specific to this instrumentation, and is provided to get
access to raw request, response, and exception objects.
### RecordException

View File

@ -3,8 +3,8 @@
[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.SqlClient.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.SqlClient)
[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Instrumentation.SqlClient.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.SqlClient)
This is an
[Instrumentation Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library),
This is an [Instrumentation
Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library),
which instruments
[Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient)
and
@ -37,9 +37,9 @@ dotnet add package OpenTelemetry.Instrumentation.SqlClient
SqlClient instrumentation must be enabled at application startup.
The following example demonstrates adding SqlClient instrumentation to a
console application. This example also sets up the OpenTelemetry Console
exporter, which requires adding the package
The following example demonstrates adding SqlClient instrumentation to a console
application. This example also sets up the OpenTelemetry Console exporter, which
requires adding the package
[`OpenTelemetry.Exporter.Console`](../OpenTelemetry.Exporter.Console/README.md)
to the application.
@ -58,12 +58,13 @@ public class Program
}
```
For an ASP.NET Core application, adding instrumentation is typically done in
the `ConfigureServices` of your `Startup` class. Refer to documentation for
For an ASP.NET Core application, adding instrumentation is typically done in the
`ConfigureServices` of your `Startup` class. Refer to documentation for
[OpenTelemetry.Instrumentation.AspNetCore](../OpenTelemetry.Instrumentation.AspNetCore/README.md).
For an ASP.NET application, adding instrumentation is typically done in the
`Global.asax.cs`. Refer to documentation for [OpenTelemetry.Instrumentation.AspNet](../OpenTelemetry.Instrumentation.AspNet/README.md).
`Global.asax.cs`. Refer to the documentation for
[OpenTelemetry.Instrumentation.AspNet](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/blob/main/src/OpenTelemetry.Instrumentation.AspNet/README.md).
## Advanced configuration
@ -72,8 +73,9 @@ This instrumentation can be configured to change the default behavior by using
### Capturing 'db.statement'
The `SqlClientInstrumentationOptions` class exposes several properties that can be
used to configure how the [`db.statement`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md#call-level-attributes)
The `SqlClientInstrumentationOptions` class exposes several properties that can
be used to configure how the
[`db.statement`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md#call-level-attributes)
attribute is captured upon execution of a query.
#### .NET Core - SetDbStatementForStoredProcedure and SetDbStatementForText
@ -88,7 +90,8 @@ attribute to the stored procedure command name.
`SetDbStatementForText` is _false_ by default (to prevent accidental capture of
sensitive data that might be part of the SQL statement text). When set to
`true`, the instrumentation will set [`db.statement`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md#call-level-attributes)
`true`, the instrumentation will set
[`db.statement`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md#call-level-attributes)
attribute to the text of the SQL command being executed.
To disable capturing stored procedure commands use configuration like below.
@ -118,8 +121,8 @@ For .NET Framework, `SetDbStatementForStoredProcedure` and
`SetDbStatementForText` are not available. Instead, a single `SetDbStatement`
property should be used to control whether this instrumentation should set the
[`db.statement`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md#call-level-attributes)
attribute to the text of the `SqlCommand` being executed. This could either be
a name of a stored procedure or a full text of a `CommandType.Text` query.
attribute to the text of the `SqlCommand` being executed. This could either be a
name of a stored procedure or a full text of a `CommandType.Text` query.
On .NET Framework, unlike .NET Core, the instrumentation capabilities for both
[`Microsoft.Data.SqlClient`](https://www.nuget.org/packages/Microsoft.Data.SqlClient/)
@ -133,8 +136,8 @@ and `System.Data.SqlClient` are limited:
query text.
Since `CommandType.Text` might contain sensitive data, all SQL capturing is
_disabled_ by default to protect against accidentally sending full query text
to a telemetry backend. If you are only using stored procedures or have no
_disabled_ by default to protect against accidentally sending full query text to
a telemetry backend. If you are only using stored procedures or have no
sensitive data in your `sqlCommand.CommandText`, you can enable SQL capturing
using the options like below:
@ -169,11 +172,11 @@ using var tracerProvider = Sdk.CreateTracerProviderBuilder()
## Enrich
This option, available on .NET Core only, allows one to enrich the activity
with additional information from the raw `SqlCommand` object. The `Enrich`
action is called only when `activity.IsAllDataRequested` is `true`. It contains
the activity itself (which can be enriched), the name of the event, and the
actual raw object.
This option, available on .NET Core only, allows one to enrich the activity with
additional information from the raw `SqlCommand` object. The `Enrich` action is
called only when `activity.IsAllDataRequested` is `true`. It contains the
activity itself (which can be enriched), the name of the event, and the actual
raw object.
Currently there is only one event name reported, "OnCustom". The actual object
is `Microsoft.Data.SqlClient.SqlCommand` for `Microsoft.Data.SqlClient` and
@ -197,15 +200,16 @@ using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.Build();
```
[Processor](../../docs/trace/extending-the-sdk/README.md#processor),
is the general extensibility point to add additional properties to any activity.
The `Enrich` option is specific to this instrumentation, and is provided to
get access to `SqlCommand` object.
[Processor](../../docs/trace/extending-the-sdk/README.md#processor), is the
general extensibility point to add additional properties to any activity. The
`Enrich` option is specific to this instrumentation, and is provided to get
access to `SqlCommand` object.
### RecordException
This option, available on .NET Core only, can be set to instruct the instrumentation
to record SqlExceptions as Activity [events](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md).
This option, available on .NET Core only, can be set to instruct the
instrumentation to record SqlExceptions as Activity
[events](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/exceptions.md).
The default value is `false` and can be changed by the code like below.
@ -221,4 +225,5 @@ using var tracerProvider = Sdk.CreateTracerProviderBuilder()
* [OpenTelemetry Project](https://opentelemetry.io/)
* [OpenTelemetry semantic conventions for database calls](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md)
* [OpenTelemetry semantic conventions for database
calls](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md)

View File

@ -79,7 +79,7 @@ Once built, changes to its configuration is not allowed, with the exception of
adding more processors. In most cases, a single `TracerProvider` is created at
the application startup, and is disposed when application shuts down.
// TODO: Add Asp.Net Core, Asp.Net notes showing where this code should go.
// TODO: Add Asp.Net Core notes showing where this code should go.
The snippet below shows how to build a basic `TracerProvider`. This will create
a provider with default configuration, and is not particularly useful. The

View File

@ -1,546 +0,0 @@
// <copyright file="ActivityHelperTest.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>
namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using OpenTelemetry.Context.Propagation;
using Xunit;
public class ActivityHelperTest : IDisposable
{
private const string TraceParentHeaderName = "traceparent";
private const string TraceStateHeaderName = "tracestate";
private const string BaggageHeaderName = "baggage";
private const string BaggageInHeader = "TestKey1=123,TestKey2=456,TestKey1=789";
private const string TestActivityName = "Activity.Test";
private readonly TextMapPropagator noopTextMapPropagator = new NoopTextMapPropagator();
private ActivityListener activitySourceListener;
public void Dispose()
{
this.activitySourceListener?.Dispose();
}
[Fact]
public void Has_Started_Returns_Correctly()
{
var context = HttpContextHelper.GetFakeHttpContext();
bool result = ActivityHelper.HasStarted(context, out Activity aspNetActivity);
Assert.False(result);
Assert.Null(aspNetActivity);
context.Items[ActivityHelper.ContextKey] = ActivityHelper.StartedButNotSampledObj;
result = ActivityHelper.HasStarted(context, out aspNetActivity);
Assert.True(result);
Assert.Null(aspNetActivity);
Activity activity = new Activity(TestActivityName);
context.Items[ActivityHelper.ContextKey] = new ActivityHelper.ContextHolder { Activity = activity };
result = ActivityHelper.HasStarted(context, out aspNetActivity);
Assert.True(result);
Assert.NotNull(aspNetActivity);
Assert.Equal(activity, aspNetActivity);
}
[Fact]
public async Task Can_Restore_Activity()
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
using var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
rootActivity.AddTag("k1", "v1");
rootActivity.AddTag("k2", "v2");
Task testTask;
using (ExecutionContext.SuppressFlow())
{
testTask = Task.Run(() =>
{
Task.Yield();
Assert.Null(Activity.Current);
ActivityHelper.RestoreContextIfNeeded(context);
Assert.Same(Activity.Current, rootActivity);
});
}
await testTask.ConfigureAwait(false);
}
[Fact(Skip = "Temporarily disable until stable.")]
public async Task Can_Restore_Baggage()
{
this.EnableListener();
var requestHeaders = new Dictionary<string, string>
{
{ BaggageHeaderName, BaggageInHeader },
};
var context = HttpContextHelper.GetFakeHttpContext(headers: requestHeaders);
using var rootActivity = ActivityHelper.StartAspNetActivity(new CompositeTextMapPropagator(new TextMapPropagator[] { new TraceContextPropagator(), new BaggagePropagator() }), context, null);
rootActivity.AddTag("k1", "v1");
rootActivity.AddTag("k2", "v2");
Task testTask;
using (ExecutionContext.SuppressFlow())
{
testTask = Task.Run(() =>
{
Task.Yield();
Assert.Null(Activity.Current);
Assert.Equal(0, Baggage.Current.Count);
ActivityHelper.RestoreContextIfNeeded(context);
Assert.Same(Activity.Current, rootActivity);
Assert.Empty(rootActivity.Baggage);
Assert.Equal(2, Baggage.Current.Count);
Assert.Equal("789", Baggage.Current.GetBaggage("TestKey1"));
Assert.Equal("456", Baggage.Current.GetBaggage("TestKey2"));
});
}
await testTask.ConfigureAwait(false);
}
[Fact]
public void Can_Stop_Lost_Activity()
{
this.EnableListener(a =>
{
Assert.NotNull(Activity.Current);
Assert.Equal(Activity.Current, a);
Assert.Equal(TelemetryHttpModule.AspNetActivityName, Activity.Current.OperationName);
});
var context = HttpContextHelper.GetFakeHttpContext();
using var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
rootActivity.AddTag("k1", "v1");
rootActivity.AddTag("k2", "v2");
Activity.Current = null;
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(rootActivity.Duration != TimeSpan.Zero);
Assert.Null(Activity.Current);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
}
[Fact]
public void Do_Not_Restore_Activity_When_There_Is_No_Activity_In_Context()
{
this.EnableListener();
ActivityHelper.RestoreContextIfNeeded(HttpContextHelper.GetFakeHttpContext());
Assert.Null(Activity.Current);
}
[Fact]
public void Do_Not_Restore_Activity_When_It_Is_Not_Lost()
{
this.EnableListener();
var root = new Activity("root").Start();
var context = HttpContextHelper.GetFakeHttpContext();
context.Items[ActivityHelper.ContextKey] = new ActivityHelper.ContextHolder { Activity = root };
ActivityHelper.RestoreContextIfNeeded(context);
Assert.Equal(root, Activity.Current);
}
[Fact]
public void Can_Stop_Activity_Without_AspNetListener_Enabled()
{
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = new Activity(TestActivityName);
rootActivity.Start();
context.Items[ActivityHelper.ContextKey] = new ActivityHelper.ContextHolder { Activity = rootActivity };
Thread.Sleep(100);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(rootActivity.Duration != TimeSpan.Zero);
Assert.Null(rootActivity.Parent);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
}
[Fact]
public void Can_Stop_Activity_With_AspNetListener_Enabled()
{
var context = HttpContextHelper.GetFakeHttpContext();
var rootActivity = new Activity(TestActivityName);
rootActivity.Start();
context.Items[ActivityHelper.ContextKey] = new ActivityHelper.ContextHolder { Activity = rootActivity };
Thread.Sleep(100);
this.EnableListener();
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(rootActivity.Duration != TimeSpan.Zero);
Assert.Null(rootActivity.Parent);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
}
[Fact]
public void Can_Stop_Root_Activity_With_All_Children()
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
using var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
var child = new Activity("child").Start();
new Activity("grandchild").Start();
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(rootActivity.Duration != TimeSpan.Zero);
Assert.True(child.Duration == TimeSpan.Zero);
Assert.Null(rootActivity.Parent);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
}
[Fact]
public void Can_Stop_Root_While_Child_Is_Current()
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
using var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
var child = new Activity("child").Start();
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.True(child.Duration == TimeSpan.Zero);
Assert.NotNull(Activity.Current);
Assert.Equal(Activity.Current, child);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
}
[Fact]
public async Task Can_Stop_Root_Activity_If_It_Is_Broken()
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
using var root = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
new Activity("child").Start();
for (int i = 0; i < 2; i++)
{
await Task.Run(() =>
{
// when we enter this method, Current is 'child' activity
Activity.Current.Stop();
// here Current is 'parent', but only in this execution context
});
}
// when we return back here, in the 'parent' execution context
// Current is still 'child' activity - changes in child context (inside Task.Run)
// do not affect 'parent' context in which Task.Run is called.
// But 'child' Activity is stopped, thus consequent calls to Stop will
// not update Current
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, root, context, null);
Assert.True(root.Duration != TimeSpan.Zero);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
Assert.Null(Activity.Current);
}
[Fact]
public void Stop_Root_Activity_With_129_Nesting_Depth()
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
using var root = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
for (int i = 0; i < 129; i++)
{
new Activity("child" + i).Start();
}
// can stop any activity regardless of the stack depth
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, root, context, null);
Assert.True(root.Duration != TimeSpan.Zero);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
Assert.NotNull(Activity.Current);
}
[Fact]
public void Should_Not_Create_RootActivity_If_AspNetListener_Not_Enabled()
{
var context = HttpContextHelper.GetFakeHttpContext();
using var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.Null(rootActivity);
Assert.Equal(ActivityHelper.StartedButNotSampledObj, context.Items[ActivityHelper.ContextKey]);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
}
[Fact]
public void Should_Not_Create_RootActivity_If_AspNetActivity_Not_Enabled()
{
var context = HttpContextHelper.GetFakeHttpContext();
this.EnableListener(onSample: (context) => ActivitySamplingResult.None);
using var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.Null(rootActivity);
Assert.Equal(ActivityHelper.StartedButNotSampledObj, context.Items[ActivityHelper.ContextKey]);
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.Null(context.Items[ActivityHelper.ContextKey]);
}
[Fact]
public void Can_Create_RootActivity_From_W3C_Traceparent()
{
this.EnableListener();
var requestHeaders = new Dictionary<string, string>
{
{ TraceParentHeaderName, "00-0123456789abcdef0123456789abcdef-0123456789abcdef-00" },
};
var context = HttpContextHelper.GetFakeHttpContext(headers: requestHeaders);
using var rootActivity = ActivityHelper.StartAspNetActivity(new TraceContextPropagator(), context, null);
Assert.NotNull(rootActivity);
Assert.Equal(ActivityIdFormat.W3C, rootActivity.IdFormat);
Assert.Equal("00-0123456789abcdef0123456789abcdef-0123456789abcdef-00", rootActivity.ParentId);
Assert.Equal("0123456789abcdef0123456789abcdef", rootActivity.TraceId.ToHexString());
Assert.Equal("0123456789abcdef", rootActivity.ParentSpanId.ToHexString());
Assert.True(rootActivity.Recorded); // note: We're not using a parent-based sampler in this test so the recorded flag of traceparent is ignored.
Assert.Null(rootActivity.TraceStateString);
Assert.Empty(rootActivity.Baggage);
Assert.Equal(0, Baggage.Current.Count);
}
[Fact]
public void Can_Create_RootActivityWithTraceState_From_W3C_TraceContext()
{
this.EnableListener();
var requestHeaders = new Dictionary<string, string>
{
{ TraceParentHeaderName, "00-0123456789abcdef0123456789abcdef-0123456789abcdef-01" },
{ TraceStateHeaderName, "ts1=v1,ts2=v2" },
};
var context = HttpContextHelper.GetFakeHttpContext(headers: requestHeaders);
using var rootActivity = ActivityHelper.StartAspNetActivity(new TraceContextPropagator(), context, null);
Assert.NotNull(rootActivity);
Assert.Equal(ActivityIdFormat.W3C, rootActivity.IdFormat);
Assert.Equal("00-0123456789abcdef0123456789abcdef-0123456789abcdef-01", rootActivity.ParentId);
Assert.Equal("0123456789abcdef0123456789abcdef", rootActivity.TraceId.ToHexString());
Assert.Equal("0123456789abcdef", rootActivity.ParentSpanId.ToHexString());
Assert.True(rootActivity.Recorded);
Assert.Equal("ts1=v1,ts2=v2", rootActivity.TraceStateString);
Assert.Empty(rootActivity.Baggage);
Assert.Equal(0, Baggage.Current.Count);
}
[Fact]
public void Can_Create_RootActivity_From_W3C_Traceparent_With_Baggage()
{
this.EnableListener();
var requestHeaders = new Dictionary<string, string>
{
{ TraceParentHeaderName, "00-0123456789abcdef0123456789abcdef-0123456789abcdef-00" },
{ BaggageHeaderName, BaggageInHeader },
};
var context = HttpContextHelper.GetFakeHttpContext(headers: requestHeaders);
using var rootActivity = ActivityHelper.StartAspNetActivity(new CompositeTextMapPropagator(new TextMapPropagator[] { new TraceContextPropagator(), new BaggagePropagator() }), context, null);
Assert.NotNull(rootActivity);
Assert.Equal(ActivityIdFormat.W3C, rootActivity.IdFormat);
Assert.Equal("00-0123456789abcdef0123456789abcdef-0123456789abcdef-00", rootActivity.ParentId);
Assert.Equal("0123456789abcdef0123456789abcdef", rootActivity.TraceId.ToHexString());
Assert.Equal("0123456789abcdef", rootActivity.ParentSpanId.ToHexString());
Assert.True(rootActivity.Recorded); // note: We're not using a parent-based sampler in this test so the recorded flag of traceparent is ignored.
Assert.Null(rootActivity.TraceStateString);
Assert.Empty(rootActivity.Baggage);
Assert.Equal(2, Baggage.Current.Count);
Assert.Equal("789", Baggage.Current.GetBaggage("TestKey1"));
Assert.Equal("456", Baggage.Current.GetBaggage("TestKey2"));
ActivityHelper.StopAspNetActivity(this.noopTextMapPropagator, rootActivity, context, null);
Assert.Equal(0, Baggage.Current.Count);
}
[Fact]
public void Can_Create_RootActivity_And_Start_Activity()
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
using var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.NotNull(rootActivity);
Assert.True(!string.IsNullOrEmpty(rootActivity.Id));
}
[Fact]
public void Can_Create_RootActivity_And_Saved_In_HttContext()
{
this.EnableListener();
var context = HttpContextHelper.GetFakeHttpContext();
using var rootActivity = ActivityHelper.StartAspNetActivity(this.noopTextMapPropagator, context, null);
Assert.NotNull(rootActivity);
Assert.Same(rootActivity, ((ActivityHelper.ContextHolder)context.Items[ActivityHelper.ContextKey])?.Activity);
}
[Fact]
public void Fire_Exception_Events()
{
int callbacksFired = 0;
var context = HttpContextHelper.GetFakeHttpContext();
Activity activity = new Activity(TestActivityName);
ActivityHelper.WriteActivityException(activity, context, new InvalidOperationException(), (a, c, e) => { callbacksFired++; });
ActivityHelper.WriteActivityException(null, context, new InvalidOperationException(), (a, c, e) => { callbacksFired++; });
// Callback should fire only for non-null activity
Assert.Equal(1, callbacksFired);
}
private void EnableListener(Action<Activity> onStarted = null, Action<Activity> onStopped = null, Func<ActivityContext, ActivitySamplingResult> onSample = null)
{
Debug.Assert(this.activitySourceListener == null, "Cannot attach multiple listeners in tests.");
this.activitySourceListener = new ActivityListener
{
ShouldListenTo = (activitySource) => activitySource.Name == TelemetryHttpModule.AspNetSourceName,
ActivityStarted = (a) => onStarted?.Invoke(a),
ActivityStopped = (a) => onStopped?.Invoke(a),
Sample = (ref ActivityCreationOptions<ActivityContext> options) =>
{
if (onSample != null)
{
return onSample(options.Parent);
}
return ActivitySamplingResult.AllDataAndRecorded;
},
};
ActivitySource.AddActivityListener(this.activitySourceListener);
}
private class TestHttpRequest : HttpRequestBase
{
private readonly NameValueCollection headers = new();
public override NameValueCollection Headers => this.headers;
public override UnvalidatedRequestValuesBase Unvalidated => new TestUnvalidatedRequestValues(this.headers);
}
private class TestUnvalidatedRequestValues : UnvalidatedRequestValuesBase
{
public TestUnvalidatedRequestValues(NameValueCollection headers)
{
this.Headers = headers;
}
public override NameValueCollection Headers { get; }
}
private class TestHttpResponse : HttpResponseBase
{
}
private class TestHttpServerUtility : HttpServerUtilityBase
{
private readonly HttpContextBase context;
public TestHttpServerUtility(HttpContextBase context)
{
this.context = context;
}
public override Exception GetLastError()
{
return this.context.Error;
}
}
private class TestHttpContext : HttpContextBase
{
private readonly Hashtable items;
public TestHttpContext(Exception error = null)
{
this.Server = new TestHttpServerUtility(this);
this.items = new Hashtable();
this.Error = error;
}
public override HttpRequestBase Request { get; } = new TestHttpRequest();
/// <inheritdoc />
public override IDictionary Items => this.items;
public override Exception Error { get; }
public override HttpServerUtilityBase Server { get; }
}
private class NoopTextMapPropagator : TextMapPropagator
{
private static readonly PropagationContext DefaultPropagationContext = default;
public override ISet<string> Fields => null;
public override PropagationContext Extract<T>(PropagationContext context, T carrier, Func<T, string, IEnumerable<string>> getter)
{
return DefaultPropagationContext;
}
public override void Inject<T>(PropagationContext context, T carrier, Action<T, string, string> setter)
{
}
}
}
}

View File

@ -1,103 +0,0 @@
// <copyright file="HttpContextHelper.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>
namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Web;
using System.Web.Hosting;
internal class HttpContextHelper
{
public static HttpContext GetFakeHttpContext(string page = "/page", string query = "", IDictionary<string, string> headers = null)
{
Thread.GetDomain().SetData(".appPath", string.Empty);
Thread.GetDomain().SetData(".appVPath", string.Empty);
var workerRequest = new SimpleWorkerRequestWithHeaders(page, query, new StringWriter(CultureInfo.InvariantCulture), headers);
var context = new HttpContext(workerRequest);
HttpContext.Current = context;
return context;
}
public static HttpContextBase GetFakeHttpContextBase(string page = "/page", string query = "", IDictionary<string, string> headers = null)
{
var context = GetFakeHttpContext(page, query, headers);
return new HttpContextWrapper(context);
}
private class SimpleWorkerRequestWithHeaders : SimpleWorkerRequest
{
private readonly IDictionary<string, string> headers;
public SimpleWorkerRequestWithHeaders(string page, string query, TextWriter output, IDictionary<string, string> headers)
: base(page, query, output)
{
if (headers != null)
{
this.headers = headers;
}
else
{
this.headers = new Dictionary<string, string>();
}
}
public override string[][] GetUnknownRequestHeaders()
{
List<string[]> result = new List<string[]>();
foreach (var header in this.headers)
{
result.Add(new string[] { header.Key, header.Value });
}
var baseResult = base.GetUnknownRequestHeaders();
if (baseResult != null)
{
result.AddRange(baseResult);
}
return result.ToArray();
}
public override string GetUnknownRequestHeader(string name)
{
if (this.headers.ContainsKey(name))
{
return this.headers[name];
}
return base.GetUnknownRequestHeader(name);
}
public override string GetKnownRequestHeader(int index)
{
var name = GetKnownRequestHeaderName(index);
if (this.headers.ContainsKey(name))
{
return this.headers[name];
}
return base.GetKnownRequestHeader(index);
}
}
}
}

View File

@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Unit test project for ASP.NET HttpModule</Description>
<!-- OmniSharp/VS Code requires TargetFrameworks to be in descending order for IntelliSense and analysis. -->
<TargetFrameworks>net462</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Web" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPkgVer)" />
<PackageReference Include="Microsoft.Web.Xdt" Version="3.1.0" />
<PackageReference Include="System.Reactive.Core" Version="3.1.1" />
<PackageReference Include="xunit" Version="$(XUnitPkgVer)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XUnitRunnerVisualStudioPkgVer)">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<DotNetCliToolReference Include="dotnet-xunit" Version="$(DotNetXUnitCliVer)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule\web.config.install.xdt">
<Link>Resources\web.config.install.xdt</Link>
</EmbeddedResource>
<EmbeddedResource Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule\web.config.uninstall.xdt">
<Link>Resources\web.config.uninstall.xdt</Link>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -1,409 +0,0 @@
// <copyright file="WebConfigTransformTest.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>
namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
using System.IO;
using System.Xml.Linq;
using Microsoft.Web.XmlTransform;
using Xunit;
public class WebConfigTransformTest
{
private const string InstallConfigTransformationResourceName = "OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Tests.Resources.web.config.install.xdt";
private const string UninstallConfigTransformationResourceName = "OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Tests.Resources.web.config.uninstall.xdt";
[Fact]
public void VerifyInstallationToBasicWebConfig()
{
const string OriginalWebConfigContent = @"
<configuration>
<system.web>
<httpModules />
</system.web>
<system.webServer>
<modules />
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyUpdateWithTypeRenamingWebConfig()
{
const string OriginalWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModuleSomeOldName"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<modules>
<add name=""TelemetryHttpModuleSomeOldName"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyUpdateNewerVersionWebConfig()
{
const string OriginalWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<add name=""TelemetryHttpModule"" type=""Microsoft.AspNet.TelemetryCorrelation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" preCondition=""managedHandler""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<add name=""TelemetryHttpModule"" type=""Microsoft.AspNet.TelemetryCorrelation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" preCondition=""managedHandler""/>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyUpdateWithIntegratedModeWebConfig()
{
const string OriginalWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration=""false"" />
<modules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""integratedMode,managedHandler""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration=""false"" />
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyUninstallationWithBasicWebConfig()
{
const string OriginalWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""integratedMode,managedHandler""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules></httpModules>
</system.web>
<system.webServer>
<modules></modules>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyUninstallTransformation(OriginalWebConfigContent, UninstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyUninstallWithIntegratedPrecondition()
{
const string OriginalWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""integratedMode,managedHandler""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules></httpModules>
</system.web>
<system.webServer>
<modules></modules>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyUninstallTransformation(OriginalWebConfigContent, UninstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyUninstallationWithUserModules()
{
const string OriginalWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""UserModule"" type=""UserNamespace.WebModuleFoo""/>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests=""true"">
<remove name=""TelemetryHttpModule"" />
<add name=""UserModule"" type=""UserNamespace.WebModuleFoo""/>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""UserModule"" type=""UserNamespace.WebModuleFoo""/>
</httpModules >
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests=""true"">
<add name=""UserModule"" type=""UserNamespace.WebModuleFoo""/>
</modules>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyUninstallTransformation(OriginalWebConfigContent, UninstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToWebConfigWithUserModules()
{
const string OriginalWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""UserModule"" type=""UserNamespace.WebModuleFoo""/>
</httpModules>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests=""true"">
<add name=""UserModule"" type=""UserNamespace.WebModuleFoo""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""UserModule"" type=""UserNamespace.WebModuleFoo""/>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests=""true"">
<add name=""UserModule"" type=""UserNamespace.WebModuleFoo""/>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToEmptyWebConfig()
{
const string OriginalWebConfigContent = @"<configuration/>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration=""false"" />
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToWebConfigWithoutModules()
{
const string OriginalWebConfigContent = @"<configuration><system.webServer/></configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<system.webServer>
<validation validateIntegratedModeConfiguration=""false"" />
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler""/>
</modules>
</system.webServer>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
private XDocument ApplyInstallTransformation(string originalConfiguration, string resourceName)
{
return this.ApplyTransformation(originalConfiguration, resourceName);
}
private XDocument ApplyUninstallTransformation(string originalConfiguration, string resourceName)
{
return this.ApplyTransformation(originalConfiguration, resourceName);
}
private void VerifyTransformation(string expectedConfigContent, XDocument transformedWebConfig)
{
Assert.True(
XNode.DeepEquals(
transformedWebConfig.FirstNode,
XDocument.Parse(expectedConfigContent).FirstNode));
}
private XDocument ApplyTransformation(string originalConfiguration, string transformationResourceName)
{
XDocument result;
Stream stream = null;
try
{
stream = typeof(WebConfigTransformTest).Assembly.GetManifestResourceStream(transformationResourceName);
var document = new XmlTransformableDocument();
using var transformation = new XmlTransformation(stream, null);
stream = null;
document.LoadXml(originalConfiguration);
transformation.Apply(document);
result = XDocument.Parse(document.OuterXml);
}
finally
{
if (stream != null)
{
stream.Dispose();
}
}
return result;
}
}
}

View File

@ -1,439 +0,0 @@
// <copyright file="WebConfigWithLocationTagTransformTest.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>
namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
using System.IO;
using System.Xml.Linq;
using Microsoft.Web.XmlTransform;
using Xunit;
public class WebConfigWithLocationTagTransformTest
{
private const string InstallConfigTransformationResourceName = "OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Tests.Resources.web.config.install.xdt";
[Fact]
public void VerifyInstallationWhenNonGlobalLocationTagExists()
{
const string OriginalWebConfigContent = @"
<configuration>
<location path=""a.aspx"">
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
</modules>
</system.webServer>
</location>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<location path=""a.aspx"">
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
</modules>
</system.webServer>
</location>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration=""false"" />
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler"" />
</modules>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationWhenGlobalAndNonGlobalLocationTagExists()
{
const string OriginalWebConfigContent = @"
<configuration>
<location path=""a.aspx"">
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
</modules>
</system.webServer>
</location>
<location path=""."">
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type""/>
</modules>
</system.webServer>
</location>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<location path=""a.aspx"">
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
</modules>
</system.webServer>
</location>
<location path=""."">
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler"" />
</modules>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</location>
<system.web></system.web>
<system.webServer></system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToLocationTagWithDotPathAndExistingModules()
{
const string OriginalWebConfigContent = @"
<configuration>
<location path=""."">
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type""/>
</modules>
</system.webServer>
</location>
<system.webServer>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<location path=""."">
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler"" />
</modules>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</location>
<system.webServer></system.webServer>
<system.web></system.web>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToLocationTagWithEmptyPathAndExistingModules()
{
const string OriginalWebConfigContent = @"
<configuration>
<location>
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
</httpModules >
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type""/>
</modules>
</system.webServer>
</location>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<location>
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler"" />
</modules>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</location>
<system.web></system.web>
<system.webServer></system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToLocationTagWithDotPathWithNoModules()
{
const string OriginalWebConfigContent = @"
<configuration>
<location path=""."">
<system.web>
</system.web>
<system.webServer>
</system.webServer>
</location>
<system.web>
</system.web>
<system.webServer>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<location path=""."">
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration=""false"" />
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler"" />
</modules>
</system.webServer>
</location>
<system.web>
</system.web>
<system.webServer>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToLocationTagWithEmptyPathWithNoModules()
{
const string OriginalWebConfigContent = @"
<configuration>
<location>
<system.web>
</system.web>
<system.webServer>
</system.webServer>
</location>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<location>
<system.web>
<httpModules>
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration=""false"" />
<modules>
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler"" />
</modules>
</system.webServer>
</location>
<system.web>
</system.web>
<system.webServer>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToLocationTagWithDotPathWithGlobalModules()
{
const string OriginalWebConfigContent = @"
<configuration>
<location path=""."">
<system.web>
</system.web>
<system.webServer>
</system.webServer>
</location>
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
</httpModules>
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<location path=""."">
<system.web>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</location>
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler"" />
</modules>
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
[Fact]
public void VerifyInstallationToLocationTagWithEmptyPathWithGlobalModules()
{
const string OriginalWebConfigContent = @"
<configuration>
<location>
</location>
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
</httpModules>
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type""/>
</modules>
</system.webServer>
</configuration>";
const string ExpectedWebConfigContent = @"
<configuration>
<location>
</location>
<system.web>
<httpModules>
<add name=""abc"" type=""type"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" />
</httpModules>
</system.web>
<system.webServer>
<modules>
<add name=""abc"" type=""type"" />
<remove name=""TelemetryHttpModule"" />
<add name=""TelemetryHttpModule"" type=""OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"" preCondition=""managedHandler"" />
</modules>
<validation validateIntegratedModeConfiguration=""false"" />
</system.webServer>
</configuration>";
var transformedWebConfig = this.ApplyInstallTransformation(OriginalWebConfigContent, InstallConfigTransformationResourceName);
this.VerifyTransformation(ExpectedWebConfigContent, transformedWebConfig);
}
private XDocument ApplyInstallTransformation(string originalConfiguration, string resourceName)
{
return this.ApplyTransformation(originalConfiguration, resourceName);
}
private XDocument ApplyUninstallTransformation(string originalConfiguration, string resourceName)
{
return this.ApplyTransformation(originalConfiguration, resourceName);
}
private void VerifyTransformation(string expectedConfigContent, XDocument transformedWebConfig)
{
Assert.True(
XNode.DeepEquals(
transformedWebConfig.FirstNode,
XDocument.Parse(expectedConfigContent).FirstNode));
}
private XDocument ApplyTransformation(string originalConfiguration, string transformationResourceName)
{
XDocument result;
Stream stream = null;
try
{
stream = typeof(WebConfigTransformTest).Assembly.GetManifestResourceStream(transformationResourceName);
var document = new XmlTransformableDocument();
using var transformation = new XmlTransformation(stream, null);
stream = null;
document.LoadXml(originalConfiguration);
transformation.Apply(document);
result = XDocument.Parse(document.OuterXml);
}
finally
{
if (stream != null)
{
stream.Dispose();
}
}
return result;
}
}
}

View File

@ -1,32 +0,0 @@
// <copyright file="BasicTests.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;
using OpenTelemetry.Trace;
using Xunit;
namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
public class BasicTests
{
[Fact]
public void AddAspNetInstrumentation_BadArgs()
{
TracerProviderBuilder builder = null;
Assert.Throws<ArgumentNullException>(() => builder.AddAspNetInstrumentation());
}
}
}

View File

@ -1,31 +0,0 @@
// <copyright file="EventSourceTest.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 OpenTelemetry.Instrumentation.AspNet.Implementation;
using OpenTelemetry.Tests;
using Xunit;
namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
public class EventSourceTest
{
[Fact]
public void EventSourceTest_AspNetInstrumentationEventSource()
{
EventSourceTestHelper.MethodsAreImplementedConsistentlyWithTheirAttributes(AspNetInstrumentationEventSource.Log);
}
}
}

View File

@ -1,370 +0,0 @@
// <copyright file="HttpInListenerTests.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;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Routing;
using Moq;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Instrumentation.AspNet.Implementation;
using OpenTelemetry.Tests;
using OpenTelemetry.Trace;
using Xunit;
namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
public class HttpInListenerTests
{
[Theory]
[InlineData("http://localhost/", "http://localhost/", 0, null)]
[InlineData("http://localhost/", "http://localhost/", 0, null, true)]
[InlineData("https://localhost/", "https://localhost/", 0, null)]
[InlineData("https://localhost/", "https://user:pass@localhost/", 0, null)] // Test URL sanitization
[InlineData("http://localhost:443/", "http://localhost:443/", 0, null)] // Test http over 443
[InlineData("https://localhost:80/", "https://localhost:80/", 0, null)] // Test https over 80
[InlineData("https://localhost:80/Home/Index.htm?q1=v1&q2=v2#FragmentName", "https://localhost:80/Home/Index.htm?q1=v1&q2=v2#FragmentName", 0, null)] // Test complex URL
[InlineData("https://localhost:80/Home/Index.htm?q1=v1&q2=v2#FragmentName", "https://user:password@localhost:80/Home/Index.htm?q1=v1&q2=v2#FragmentName", 0, null)] // Test complex URL sanitization
[InlineData("http://localhost:80/Index", "http://localhost:80/Index", 1, "{controller}/{action}/{id}")]
[InlineData("https://localhost:443/about_attr_route/10", "https://localhost:443/about_attr_route/10", 2, "about_attr_route/{customerId}")]
[InlineData("http://localhost:1880/api/weatherforecast", "http://localhost:1880/api/weatherforecast", 3, "api/{controller}/{id}")]
[InlineData("https://localhost:1843/subroute/10", "https://localhost:1843/subroute/10", 4, "subroute/{customerId}")]
[InlineData("http://localhost/api/value", "http://localhost/api/value", 0, null, false, "/api/value")] // Request will be filtered
[InlineData("http://localhost/api/value", "http://localhost/api/value", 0, null, false, "{ThrowException}")] // Filter user code will throw an exception
[InlineData("http://localhost/", "http://localhost/", 0, null, false, null, true)] // Test RecordException option
public void AspNetRequestsAreCollectedSuccessfully(
string expectedUrl,
string url,
int routeType,
string routeTemplate,
bool setStatusToErrorInEnrich = false,
string filter = null,
bool recordException = false)
{
IDisposable tracerProvider = null;
RouteData routeData;
switch (routeType)
{
case 0: // WebForm, no route data.
routeData = new RouteData();
break;
case 1: // Traditional MVC.
case 2: // Attribute routing MVC.
case 3: // Traditional WebAPI.
routeData = new RouteData()
{
Route = new Route(routeTemplate, null),
};
break;
case 4: // Attribute routing WebAPI.
routeData = new RouteData();
var value = new[]
{
new
{
Route = new
{
RouteTemplate = routeTemplate,
},
},
};
routeData.Values.Add(
"MS_SubRoutes",
value);
break;
default:
throw new NotSupportedException();
}
var workerRequest = new Mock<HttpWorkerRequest>();
workerRequest.Setup(wr => wr.GetKnownRequestHeader(It.IsAny<int>())).Returns<int>(i =>
{
return i switch
{
39 => "Test", // User-Agent
_ => null,
};
});
HttpContext.Current = new HttpContext(
new HttpRequest(string.Empty, url, string.Empty)
{
RequestContext = new RequestContext()
{
RouteData = routeData,
},
},
new HttpResponse(new StringWriter()));
typeof(HttpRequest).GetField("_wr", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(HttpContext.Current.Request, workerRequest.Object);
List<Activity> exportedItems = new List<Activity>(16);
Sdk.SetDefaultTextMapPropagator(new TraceContextPropagator());
using (tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation((options) =>
{
options.Filter = httpContext =>
{
Assert.True(Activity.Current.IsAllDataRequested);
if (string.IsNullOrEmpty(filter))
{
return true;
}
if (filter == "{ThrowException}")
{
throw new InvalidOperationException();
}
return httpContext.Request.Path != filter;
};
options.Enrich = GetEnrichmentAction(setStatusToErrorInEnrich ? Status.Error : default);
options.RecordException = recordException;
})
.AddInMemoryExporter(exportedItems)
.Build())
{
using var inMemoryEventListener = new InMemoryEventListener(AspNetInstrumentationEventSource.Log);
var activity = ActivityHelper.StartAspNetActivity(Propagators.DefaultTextMapPropagator, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStartedCallback);
if (filter == "{ThrowException}")
{
Assert.Single(inMemoryEventListener.Events.Where((e) => e.EventId == 2));
}
Assert.Equal(TelemetryHttpModule.AspNetActivityName, Activity.Current.OperationName);
if (recordException)
{
ActivityHelper.WriteActivityException(activity, HttpContext.Current, new InvalidOperationException(), TelemetryHttpModule.Options.OnExceptionCallback);
}
ActivityHelper.StopAspNetActivity(Propagators.DefaultTextMapPropagator, activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
}
if (HttpContext.Current.Request.Path == filter || filter == "{ThrowException}")
{
Assert.Empty(exportedItems);
return;
}
Assert.Single(exportedItems);
Activity span = exportedItems[0];
Assert.Equal(TelemetryHttpModule.AspNetActivityName, span.OperationName);
Assert.NotEqual(TimeSpan.Zero, span.Duration);
Assert.Equal(routeTemplate ?? HttpContext.Current.Request.Path, span.DisplayName);
Assert.Equal(ActivityKind.Server, span.Kind);
Assert.True(span.Duration != TimeSpan.Zero);
Assert.Equal(200, span.GetTagValue(SemanticConventions.AttributeHttpStatusCode));
var expectedUri = new Uri(expectedUrl);
var actualUrl = span.GetTagValue(SemanticConventions.AttributeHttpUrl);
Assert.Equal(expectedUri.ToString(), actualUrl);
// Url strips 80 or 443 if the scheme matches.
if ((expectedUri.Port == 80 && expectedUri.Scheme == "http") || (expectedUri.Port == 443 && expectedUri.Scheme == "https"))
{
Assert.DoesNotContain($":{expectedUri.Port}", actualUrl as string);
}
else
{
Assert.Contains($":{expectedUri.Port}", actualUrl as string);
}
// Host includes port if it isn't 80 or 443.
if (expectedUri.Port is 80 or 443)
{
Assert.Equal(
expectedUri.Host,
span.GetTagValue(SemanticConventions.AttributeHttpHost) as string);
}
else
{
Assert.Equal(
$"{expectedUri.Host}:{expectedUri.Port}",
span.GetTagValue(SemanticConventions.AttributeHttpHost) as string);
}
Assert.Equal(HttpContext.Current.Request.HttpMethod, span.GetTagValue(SemanticConventions.AttributeHttpMethod) as string);
Assert.Equal(HttpContext.Current.Request.Path, span.GetTagValue(SemanticConventions.AttributeHttpTarget) as string);
Assert.Equal(HttpContext.Current.Request.UserAgent, span.GetTagValue(SemanticConventions.AttributeHttpUserAgent) as string);
if (recordException)
{
var status = span.GetStatus();
Assert.Equal(Status.Error.StatusCode, status.StatusCode);
Assert.Equal("Operation is not valid due to the current state of the object.", status.Description);
}
else if (setStatusToErrorInEnrich)
{
// This validates that users can override the
// status in Enrich.
Assert.Equal(Status.Error, span.GetStatus());
// Instrumentation is not expected to set status description
// as the reason can be inferred from SemanticConventions.AttributeHttpStatusCode
Assert.True(string.IsNullOrEmpty(span.GetStatus().Description));
}
else
{
Assert.Equal(Status.Unset, span.GetStatus());
// Instrumentation is not expected to set status description
// as the reason can be inferred from SemanticConventions.AttributeHttpStatusCode
Assert.True(string.IsNullOrEmpty(span.GetStatus().Description));
}
}
[Theory]
[InlineData(SamplingDecision.Drop)]
[InlineData(SamplingDecision.RecordOnly)]
[InlineData(SamplingDecision.RecordAndSample)]
public void ExtractContextIrrespectiveOfSamplingDecision(SamplingDecision samplingDecision)
{
HttpContext.Current = new HttpContext(
new HttpRequest(string.Empty, "http://localhost/", string.Empty)
{
RequestContext = new RequestContext()
{
RouteData = new RouteData(),
},
},
new HttpResponse(new StringWriter()));
bool isPropagatorCalled = false;
var propagator = new Mock<TextMapPropagator>();
propagator.Setup(m => m.Extract(It.IsAny<PropagationContext>(), It.IsAny<HttpRequest>(), It.IsAny<Func<HttpRequest, string, IEnumerable<string>>>()))
.Returns(() =>
{
isPropagatorCalled = true;
return default;
});
var activityProcessor = new Mock<BaseProcessor<Activity>>();
Sdk.SetDefaultTextMapPropagator(propagator.Object);
using (var tracerProvider = Sdk.CreateTracerProviderBuilder()
.SetSampler(new TestSampler(samplingDecision))
.AddAspNetInstrumentation()
.AddProcessor(activityProcessor.Object).Build())
{
var activity = ActivityHelper.StartAspNetActivity(Propagators.DefaultTextMapPropagator, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStartedCallback);
ActivityHelper.StopAspNetActivity(Propagators.DefaultTextMapPropagator, activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
}
Assert.True(isPropagatorCalled);
}
[Fact]
public void ExtractContextIrrespectiveOfTheFilterApplied()
{
HttpContext.Current = new HttpContext(
new HttpRequest(string.Empty, "http://localhost/", string.Empty)
{
RequestContext = new RequestContext()
{
RouteData = new RouteData(),
},
},
new HttpResponse(new StringWriter()));
bool isPropagatorCalled = false;
var propagator = new Mock<TextMapPropagator>();
propagator.Setup(m => m.Extract(It.IsAny<PropagationContext>(), It.IsAny<HttpRequest>(), It.IsAny<Func<HttpRequest, string, IEnumerable<string>>>()))
.Returns(() =>
{
isPropagatorCalled = true;
return default;
});
bool isFilterCalled = false;
var activityProcessor = new Mock<BaseProcessor<Activity>>();
Sdk.SetDefaultTextMapPropagator(propagator.Object);
using (var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation(options =>
{
options.Filter = context =>
{
isFilterCalled = true;
return false;
};
})
.AddProcessor(activityProcessor.Object).Build())
{
var activity = ActivityHelper.StartAspNetActivity(Propagators.DefaultTextMapPropagator, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStartedCallback);
ActivityHelper.StopAspNetActivity(Propagators.DefaultTextMapPropagator, activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
}
Assert.True(isFilterCalled);
Assert.True(isPropagatorCalled);
}
private static Action<Activity, string, object> GetEnrichmentAction(Status statusToBeSet)
{
void EnrichAction(Activity activity, string method, object obj)
{
Assert.True(activity.IsAllDataRequested);
switch (method)
{
case "OnStartActivity":
Assert.True(obj is HttpRequest);
break;
case "OnStopActivity":
Assert.True(obj is HttpResponse);
if (statusToBeSet != default)
{
activity.SetStatus(statusToBeSet);
}
break;
default:
break;
}
}
return EnrichAction;
}
private class TestSampler : Sampler
{
private readonly SamplingDecision samplingDecision;
public TestSampler(SamplingDecision samplingDecision)
{
this.samplingDecision = samplingDecision;
}
public override SamplingResult ShouldSample(in SamplingParameters samplingParameters)
{
return new SamplingResult(this.samplingDecision);
}
}
}
}

View File

@ -1,112 +0,0 @@
// <copyright file="HttpInMetricsListenerTests.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.Collections.Generic;
using System.IO;
using System.Web;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Xunit;
namespace OpenTelemetry.Instrumentation.AspNet.Tests
{
public class HttpInMetricsListenerTests
{
[Fact]
public void HttpDurationMetricIsEmitted()
{
string url = "http://localhost/api/value";
double duration = 0;
HttpContext.Current = new HttpContext(
new HttpRequest(string.Empty, url, string.Empty),
new HttpResponse(new StringWriter()));
// This is to enable activity creation
// as it is created using activitysource inside TelemetryHttpModule
// TODO: This should not be needed once the dependency on activity is removed from metrics
using var traceprovider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation(opts => opts.Enrich
= (activity, eventName, rawObject) =>
{
if (eventName.Equals("OnStopActivity"))
{
duration = activity.Duration.TotalMilliseconds;
}
})
.Build();
var exportedItems = new List<Metric>();
using var meterprovider = Sdk.CreateMeterProviderBuilder()
.AddAspNetInstrumentation()
.AddInMemoryExporter(exportedItems)
.Build();
var activity = ActivityHelper.StartAspNetActivity(Propagators.DefaultTextMapPropagator, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStartedCallback);
ActivityHelper.StopAspNetActivity(Propagators.DefaultTextMapPropagator, activity, HttpContext.Current, TelemetryHttpModule.Options.OnRequestStoppedCallback);
meterprovider.ForceFlush();
var metricPoints = new List<MetricPoint>();
foreach (var p in exportedItems[0].GetMetricPoints())
{
metricPoints.Add(p);
}
Assert.Single(metricPoints);
var metricPoint = metricPoints[0];
var count = metricPoint.GetHistogramCount();
var sum = metricPoint.GetHistogramSum();
Assert.Equal(MetricType.Histogram, exportedItems[0].MetricType);
Assert.Equal("http.server.duration", exportedItems[0].Name);
Assert.Equal(1L, count);
Assert.Equal(duration, sum);
Assert.Equal(3, metricPoints[0].Tags.Count);
string httpMethod = null;
int httpStatusCode = 0;
string httpScheme = null;
foreach (var tag in metricPoints[0].Tags)
{
if (tag.Key == SemanticConventions.AttributeHttpMethod)
{
httpMethod = (string)tag.Value;
continue;
}
if (tag.Key == SemanticConventions.AttributeHttpStatusCode)
{
httpStatusCode = (int)tag.Value;
continue;
}
if (tag.Key == SemanticConventions.AttributeHttpScheme)
{
httpScheme = (string)tag.Value;
continue;
}
}
Assert.Equal("GET", httpMethod);
Assert.Equal(200, httpStatusCode);
Assert.Equal("http", httpScheme);
}
}
}

View File

@ -1,34 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Unit test project for OpenTelemetry ASP.NET instrumentation</Description>
<!-- OmniSharp/VS Code requires TargetFrameworks to be in descending order for IntelliSense and analysis. -->
<TargetFrameworks>net462</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPkgVer)" />
<PackageReference Include="Moq" Version="$(MoqPkgVer)" />
<PackageReference Include="xunit" Version="$(XUnitPkgVer)" />
<PackageReference Condition="$([MSBuild]::IsOsPlatform('Windows'))" Include="xunit.runner.visualstudio" Version="$(XUnitRunnerVisualStudioPkgVer)">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<DotNetCliToolReference Include="dotnet-xunit" Version="$(DotNetXUnitCliVer)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNet\OpenTelemetry.Instrumentation.AspNet.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.InMemory\OpenTelemetry.Exporter.InMemory.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\EventSourceTestHelper.cs" Link="EventSourceTestHelper.cs" />
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestEventListener.cs" Link="TestEventListener.cs" />
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\InMemoryEventListener.cs" Link="InMemoryEventListener.cs" />
</ItemGroup>
</Project>