How to collect requestIn activity trace information? - asp.net-core

<PackageReference Include="OpenTelemetry" Version="1.2.0-rc3" />
<PackageReference Include="OpenTelemetry.Api" Version="1.2.0-rc3" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.2.0-rc3" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9" />
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System.Diagnostics;
var builder = WebApplication.CreateBuilder(args);
var serviceName = "Demo";
var serviceVersion = "1.0.0";
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddOpenTelemetryTracing(b =>
{
b
.SetSampler(new AlwaysOnSampler())
.AddSource(serviceName)
.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService(serviceName: serviceName, serviceVersion: serviceVersion))
.AddConsoleExporter()
;
});
builder.Services.AddSingleton(TracerProvider.Default.GetTracer(serviceName));
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapGet("/hello", (Tracer tracer) =>
{
var current = Activity.Current;
using var span = tracer.StartActiveSpan("hello-span");
});
app.Run();
“hello-span” is output. I debug the variable current, and there is an operation name "Microsoft. Aspnetcore. Hosting. Httprequestin".
However, this activity is not output to the console. I want to output it. What should I do.
I've found a solution
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9" />
This package creates a new activity based on the built-in requestin activity and provides a way to enrich this activity.
So I can add some tags and filters in the request process.
Filter the data according to the active tag, and then export it.

I have test the code you provided, and it works for me. Please check the result first, does it you want?
My test steps:
Choose IIS Express first, then run it.
Open output window, and choose Project_Name-Asp.Net Core Web Server.
Then you can check the message here when access the url http://xxx:port/hello.

Related

Localhost not found after migrating application from .net 5 to .net 6

I am unable to run my web application project in IIS Express after migrating from .net 5 to .net 6. I get an error stating that "the localhost page can't be found."
From what I've been able to learn, I have gone through the proper steps to migrate between target frameworks. I have changed the target framework to .net 6 in application properties, and I have updated all affected NuGet packages using the NuGet Pkg Manager. No errors are shown when I build the project, and I have ensured Visual Studio is updated to the latest version, 17.2.5 as of this writing.
The IIS Settings in my launchsettings.json file are as follows:
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:35317",
"sslPort": 44352
}
}
and I do have an Index view return in my home controller. Yet I still get the error that
No webpage was found for the web address: https://localhost:44352/
when I run the application.
I am retaining the "old" hosting model of Startup.cs and Program.cs from the .Net 5 build to save time, as I understand per Microsoft that this is acceptable. No changes have been made to the code in either of those files.
What am I missing?
EDIT: My Program.cs code is below, by request of #adrianMoskal
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
EDIT 2:
Per #adrianMoskal, I updated my Program.cs file to .NET 6 standard. It now looks like this:
var builder = WebApplication.CreateBuilder(args);
// Add DB Contexts and Services here
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Unfortunately, the error persists.
I created a new project to migrate from .net 5 to .net 6, but no problem, it works fine, I will show you the complete steps:
.csproj:
change Version like below:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<!--<TargetFramework>net5.0</TargetFramework>-->
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>_2022070802</RootNamespace>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
</PropertyGroup>
<ItemGroup>
<!--<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.16" />-->
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.1" />
</ItemGroup>
</Project>
Then I delete Startup.cs and change Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add DB Contexts and Services here
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
More detail information, see this link.
You can create a new empty project to migrate from .net 5 to .net 6 and see if the problem still exists.I think this may be the problem caused by some of your configuration, maybe you can check whether your path is correct?
Chen's answer is the correct migration step.
I tried to reproduce your issue. When I comment Index page method in HomeController. And I face the same issue.
So you probably deleted your "Index" action in HomeController.
The answer appears to be "screw you, start over."
I've tried copying my project code to a new .net 6 project -- that is, copying all the views, controllers, and whatnot to the new project and updating the new Program.cs file accordingly -- and I still got the same error. I then tried converting a different .net 5 project to .net 6, and got the same error again. So apparently, converting just doesn't work.
So be it. I quit. I've got too much else to do to worry about this. Thanks to those who responded to this post for trying to help resolve it, anyway.

.NetCore: How to include folder in production?

So I have this code to send email. The code tries to find the .cshtml for the email template.
This is my folder hierarchy:
Project
-- Email
---- EmailConfirmation.cshtml
---- ResetPassword.cshtml
This is the code to find the template:
public static string GetEmailTemplate(string templateName)
{
string path = Path.Combine(Config.Env.ContentRootPath, "Email", templateName);
string content = "";
// This text is added only once to the file.
if (File.Exists(path))
{
// Create a file to write to.
content = File.ReadAllText(path);
}
else
{
throw new Exception("The email template could not be found");
}
return content;
}
In Debug, this runs perfectly, but in production, the code cannot find the template.
How can I include the template in publish package?
I tried this in my .csproj:
<ItemGroup>
<Folder Include="ClientApp\" />
<Folder Include="Migrations\" />
<Views Include="Email\**" />
</ItemGroup>
It is not working.
Edit
I tried to set this:
But still not working.
So my problem was the Email folder was not copied to the published package. Add this code to .csproj:
<ItemGroup>
<None Include="Email\EmailConfirmation.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Email\ResetPassword.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Or you can set it in the project explorer like this:
For some reason, the build action has to be set to None, because .NetCore will treat Content differently and the folder will not be copied to the publishing package (maybe the content will be merge into .dll, I do not know).
Could you try this ?
string workingDirectory = Environment.CurrentDirectory;
string projectDirectory=Directory.GetParent(workingDirectory).Parent.Parent.FullName;
It will give you the Path to the project directory. Then you complete with the subdirectory which leads to your file.

gRPC code from non-Razor file in Blazor client-side never completes

I have a Blazor Client hosted in ASP.Net Core. In the Server project, I have a gRPC service. Protos are stored in a Shared project (referenced by both Server and Client).
This works successfully if I call it from a .razor file. I set up the client in the startup.ConfigureServices -
services.AddSingleton(services =>
{
// Create a gRPC-Web channel pointing to the backend server
var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));
var baseUri = services.GetRequiredService<NavigationManager>().BaseUri;
var channel = GrpcChannel.ForAddress(baseUri, new GrpcChannelOptions { HttpClient = httpClient });
// Now we can instantiate gRPC clients for this channel
return new Abilities.AbilitiesClient(channel);
});
Inject it into the razor page -
#inject Abilities.AbilitiesClient AbilitiesClient
And load the collection on init -
private IList<Ability> abilities;
protected override async Task OnInitializedAsync()
{
abilities = (await AbilitiesClient.GetAbilitiesAsync(new Empty())).Abilities;
}
Works no problem! But I don't want to cram all of this logic into the view as I have over a dozen different services, so I've moved it out to a helper class. Now instead of adding the client to the services at startup, I add the helper -
services.AddSingleton(services =>
{
return new Helpers.gRPCHelper(services.GetRequiredService<NavigationManager>().BaseUri);
});
In the helper's constructor, I throw the base URI into a private variable, so I can spin up the channel on demand. In the function that calls the gRPC service, I have the below -
var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));
var channel = GrpcChannel.ForAddress(_BaseURI, new GrpcChannelOptions { HttpClient = httpClient });
var abilityClient = new Abilities.AbilitiesClient(channel);
Console.WriteLine("Calling gRPC");
var abilities = abilityClient.GetAbilities(new Empty(), null).Abilities;
Console.WriteLine("Call Complete");
In the browsers console, I see the "Calling gRPC", but the "Call Complete" is never logged. The browser just sits, waiting, I gave up after 5 minutes.
On the server side, I can see the gRPC method being called, I can even hit a break point in it and verify that it is successfully completing with results.
I don't see the traffic in the web browser's network tab (neither Chrome nor Edge).
I tried setting a deadline of 10 seconds, but it never popped.
This is all off the preview releases. So I'm not sure if I'm running into a current limitation, or if I'm doing something wrong -
<PackageReference Include="Grpc.Net.Client.Web" Version="2.27.0-dev202001100801" />
<PackageReference Include="Microsoft.AspNetCore.Blazor" Version="3.1.0-preview4.19579.2" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="3.1.0-preview4.19579.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.HttpClient" Version="3.1.0-preview4.19579.2" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.DevServer" Version="3.1.0-preview4.19579.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Blazor.Mono" Version="3.2.0-preview1.20052.1" />
Sever output -
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2.0 POST https://localhost:44340/Ability.Abilities/GetAbilities application/grpc-web 5
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'gRPC - /Ability.Abilities/GetAbilities'
********************************************************************
GetAbilities Called!
********************************************************************
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.1.1 initialized 'Context' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (143ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [a].[AbilityID], [a].[Icon], [a].[LongText], [a].[ShortText], [a].[Source], [a].[Title]
FROM [Abilities] AS [a]
ORDER BY [a].[Title]
********************************************************************
GetAbilities Complete!
********************************************************************
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'gRPC - /Ability.Abilities/GetAbilities'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 3555.6284ms 200 application/grpc-web
Poked around at this a bit more last night and I'm seeing another oddity. As soon as the client makes the gRPC call, the browser starts sucking up CPU, 25-35%. Edge will eventually pop a 'long running script' error. Chrome effectively hangs and has to be forcibly closed. I'm starting to think the project may be borked, I'll try building a fresh simplified implementation to see if I can get it working in a slimmed down code base.

Controller not being discovered in asp.net core F#

I am trying to setup a unit test in F# with asp.net core. with the code below. but the code to the server fails with a 404 Not found.
Is there a reason why the controller is being discovered by the framework ?
module Tests
open System
open Xunit
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.DependencyInjection
type Startup () =
member __.ConfigureServices(services: IServiceCollection) =
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2) |> ignore
member __.Configure(app: IApplicationBuilder, env: IHostingEnvironment) =
app.UseMvc() |> ignore
[<Route("api")>]
[<ApiController>]
type TestController() =
inherit ControllerBase()
[<HttpGet("testGet")>]
member __.TestGet() =
121221
[<HttpPost("testPost")>]
member __.TestPost() =
String.Empty
let buildHost(baseAdress:string) =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.UseUrls(baseAdress)
.Build()
[<Fact>]
let ``My test`` () =
let baseAdress = "https://localhost:9000"
use host = buildHost(baseAdress)
host.RunAsync() |> ignore
let client = new System.Net.Http.HttpClient(BaseAddress = new System.Uri(baseAdress))
let result = client.GetAsync("api/testGet").Result
Assert.Equal(System.Net.HttpStatusCode.OK, result.StatusCode)
Assert.True(result.IsSuccessStatusCode, result.Content.ReadAsStringAsync().Result )
Here is the content of the fsproj File
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="Tests.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Microsoft.NETCore.App" Version="2.2.2" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
</Project>
Except the reasons commented above by #Kirk Larkin, you need put your controller into a namespace instead of module directly.
namespace App //<------------- put your controller into a namespace
// ... open
type Startup () =
...
[<Route("api")>]
[<ApiController>]
type TestController() =
...
module Tests=
// ... open
let buildHost(baseAdress:string) =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
.UseUrls(baseAdress)
.Build()
[<Fact>]
let ``My test`` () =
...
Also note you're starting the server by an asynchronous method host.RunAsync() |> ignore. Although the above test code works fine for me, there's a chance your HttpClient will send requests before the server is ready. A good way is to add a reference to the Microsoft.AspNetCore.TestHost package and using the built-in TestHost instead:
let hostBuilder =
Microsoft.AspNetCore.WebHost
.CreateDefaultBuilder()
.UseStartup<Startup>()
use server = new Microsoft.AspNetCore.TestHost.TestServer(hostBuilder)
use client = server.CreateClient()
let result = client.GetAsync("api/testGet").Result // a quick & dirty test
Assert.Equal(System.Net.HttpStatusCode.OK, result.StatusCode)
Assert.True(result.IsSuccessStatusCode, result.Content.ReadAsStringAsync().Result )
[Edit]:
I'm now sure it is a behavior by design. See source code on GitHub:
// We only consider public top-level classes as controllers. IsPublic returns false for nested
// classes, regardless of visibility modifiers
if (!typeInfo.IsPublic)
{
return false;
}
Since the class within a F# module will be compiled into a nested class, it won't be considered as a Controller at all.
On top of the answer that #itminus provided. the issue can be resolved by configuring the application manager in configureServices like so
member this.ConfigureServices(services: IServiceCollection) =
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApplicationPartManager(fun m ->
m.FeatureProviders.Add
{
new Microsoft.AspNetCore.Mvc.Controllers.ControllerFeatureProvider() with
member __.IsController (typeInfo:System.Reflection.TypeInfo) =
(typeof<ControllerBase>.IsAssignableFrom(typeInfo)) || base. IsController(typeInfo)
}
) |> ignore
services.AddRouteAnalyzer() |> ignore

Microsoft.DotNet.Props directory does not exist for building xproj

We are trying to build an xproj project and an error about not being able to find the Microsoft.DotNet.Props file because it seems like its looking at the wrong directory.
Taking a look at the xml MSBuildExtensionsPath32 references C:\Program Files\dotnet\sdk\1.1.4\ where the directory Microsoft\VisualStudio\.. does not exist ... but the normal MSBuild directory C:\Program Files (x86)\MSBuild does have the directory for Microsoft.DotNet.Props file C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\DotNet\Microsoft.DotNet.Props
Here is the part of the XML
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
The error I see while building is:
error MSB4019: The imported project "C:\Program Files\dotnet\sdk\1.1.4\Microsoft\VisualStudio\v14.0\DotNet\Microsoft.DotNet.Props" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
If anyone has any idea what is happening, help would be great
EDIT:
The build is invoked from a Jenkins project on Windows Server 2012 R2.
The VM image is from Azure market place "MicrosoftVisualStudio / VisualStudio / VS-2015-Comm-VSU3-AzureSDK-29-WS2012R2 / 2017.10.12" - which comes with Visual Studio 2015 community edition with update 3.
Azure SDK 2.9. Upgraded Node from old v0.12 to v8.x. Upgraded .NET core from not sure what was installed to 1.1.4.
The xproj itself has no code - except small amount in Startup.cs to serve static files (code at bottom of post).
The application is also used in a Service Fabric project. The error does not come from building the .sln but when packaging up the .sfproj (it might be its not set to build in the sln but packaging will need to build it).
Startup.cs:
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Website
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == 404
&& !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/index.html";
await next();
}
});
app.UseStaticFiles();
}
}
}
Edit: here is the whole xproj xml
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<ProjectGuid>17107df8-0cfa-6946-917a-a9b8765cf9ea</ProjectGuid>
<RootNamespace>Website</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<IsServiceFabricServiceProject>True</IsServiceFabricServiceProject>
</PropertyGroup>
<ItemGroup>
<DnxInvisibleContent Include="bower.json" />
<DnxInvisibleContent Include=".bowerrc" />
</ItemGroup>
<ItemGroup>
<DnxInvisibleFolder Include="wwwroot\Angular\dist\" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b69-4b1e-b82e-3ada8210c987}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
You are trying to use a preview tooling (xproj) with the 1.1.4 version of the .NET Core Sdk. The preview tooling available in VS 2015 does not work with the 1.0+ stable tooling for .NET Core.
Make sure that a preview2 version of the .NET Core SDK is installed both on your development machines and Jenkins server - e.g. 1.0.0-preview2-003156 - and that a global.json file exists your solution directory to tell VS to use this preview version of the SDK:
{
"sdk": {
"version": "1.0.0-preview2-003156"
}
}
As a long-term solution I recommend moving to the stable and supported .NET Core tooling by migrating to VS 2017.