How can I trace Database operations with opentelemetry in ASP.NET Core? - asp.net-core

I have an ASP.NET Core Web API project, let's call it projectA.
ProjectA connects to a MySQL database.
Project A simply does this:
get a request
then make a query to the connected MySQL database
then return response to request
I want to measure time spent for database operation and send result to Jaeger.
For http requests opentelemetry automatically measure time spent for request and send results to jaeger.
How can I do the same thing with opentelemetry for database operations?

You need to enable SQL Client instrumentation when configuring your tracer provider.
using OpenTelemetry.Trace;
public class Program
{
public static void Main(string[] args)
{
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSqlClientInstrumentation()
.AddConsoleExporter()
.Build();
}
}
Here's the official documentation: https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry.Instrumentation.SqlClient#readme

Related

Random ASP.NET Core Identity Logouts in production

I have an ASP.NET Core MVC web application that uses Identity to handle user account Authentication and Authorization. When running the app on my local IIS express everything was working properly. After deploying the app to a shared web server I started to notice that the logged-in user accounts would get logged out at seemingly random intervals. Through experimentation, I was able to determine that the log-outs were occurring whether the account was active or idle. They were occuring at no recuring time interval and completely unrelated to any expiry time that I set on my cookies. The logouts were occuring on every view in the web app so I couldn't pin the issue to any particular controller. Also I use the same Database for the published and the local testing version of the app and therefore the same user accounts. I anyone has an idea where to start looking for a solution it would be greatly appreciated.
I posted this question because there is a great answer that 90% solves the issue Here however of the multiple forums that I have been scouring over the last few days there are none with an accepted answer. I am posting this answer to address this. The underlying cause of the issue is that IIS application pool is being reset or recyling and on a shared host with multiple applications using it this can be happening fairly frequently. As is suggested in the above link Data Protection has to be used to persist the keys if IIS application pool recycles. Here is the code offered in the original answer.
services.AddDataProtection()
.PersistKeysToFileSystem(new System.IO.DirectoryInfo("SOME WHERE IN STORAGE"))
//.ProtectKeysWithCertificate(new X509Certificate2());
.SetDefaultKeyLifetime(TimeSpan.FromDays(90));
This code is to be added in ConfigureServices in Startup.cs
As my application is being hosted on a shared server using .PersistKeysToFileSystem was not an option so instead I persisted the keys using DbContext like this:
services.AddDataProtection().PersistKeysToDbContext<MyKeysContext>()
.SetDefaultKeyLifetime(TimeSpan.FromDays(90));
Based on This article here I build MyKeysContext as follows.
// Add a DbContext to store your Database Keys
services.AddDbContext<MyKeysContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("MyKeysConnection")));
In ConfigureServices in Startup.cs and then created a class called MyKeysContext as follows:
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;
namespace WebApp1
{
public class MyKeysContext : DbContext, IDataProtectionKeyContext
{
// A recommended constructor overload when using EF Core
// with dependency injection.
public MyKeysContext(DbContextOptions<MyKeysContext> options)
: base(options) { }
// This maps to the table that stores keys.
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
}
}
I created the database on my Host this will probably be different so I have omitted this step. then I applied the migrations to the database like this.
Add-Migration AddDataProtectionKeys -Context MyKeysContext
Update-Database -Context MyKeysContext

Logging HTTP requests and responses in quarkus resteasy

I am running into a problem with calling a REST endpoint on a server, using a REST client in Quarkus, built with the org.eclipse.microprofile.rest.client.RestClientBuilder. I would very much like to debug the service, by writing the HTTP requests and responses to the log, so I might see what is actually being sent to the server. The hunt for a guide for that particular issue has eluded me however.
I managed to build a logging filter but that only logs out the URL and the Entitys toString value, which is not at all the same as the HTTP request and response being sent.
Help me by pointing me to a solution to log the actual HTTP request and response.
You can try to enable the logging for all the traffic including wire or just some components, here in the logging configuration guide you can find how enable just some components logging.
Add this to your application.properties and you should be able to see some logging information quarkus.log.category."org.apache.http".level=DEBUG
If you need to log everything you can always put quarkus in debug level ALL, and the pick the components you need to constraint the logging, whit this you see all the traces at wire level.
You can also enable the http access log with this guide
Good luck.
If you are using resteasy reactive you can turn on logging with:
quarkus.rest-client.logging.scope=request-response
quarkus.rest-client.logging.body-limit=1024
quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG
Check this: https://quarkus.io/guides/resteasy-reactive#the-jax-rs-way
Simply implement that class and you will start logging. Like this:
#Provider
class LoggingFilter implements ContainerRequestFilter,
ContainerResponseFilter {
/* Useful stuff for later development purposes.
#Context
UriInfo info;
#Context
HttpServerRequest request;
*/
#Override
public void filter(ContainerRequestContext requestContext) {
Log.info(requestContext);
}
#Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) {
Log.info(responseContext);
}
}
Then you may use UriInfo, HttpServerRequest and ContainerRequestContext to get any data you want and add to your custom log.

Getting the Base URL of the application inside of the ASP.NET Core HostedService

I need to obtain the base URL of the ASP.NET Core application inside of one of the HostedServices.
I need this because it does a request to the same ASP.NET core application in which it is hosted (the purpose is warming up, to improve the first call performance to the User).
For now my solution is to keep the base URL in the config file or just in the hosted service private variable.
https://github.com/BBGONE/JRIApp.Core/blob/master/DEMOS/RIAppDemoMVC/RIAppDemo/Utils/WarmUpService.cs
But i think, there's a way to obtain it from the startup code, but i don't know where it is hidden.
Anybody know how it can be obtained?
P.S. - there are solutions to obtain it from the request information, but the HostedService is started before any request have been done. So it's not suitable in this case.
I have found how to obtain the address of the appllication.
public void Configure(IApplicationBuilder application)
{
var addresses = application.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
}
Although it has an issue https://github.com/aspnet/Hosting/issues/811 and can not be used if the application is hosted in the IIS or IIS Express.
They say:
That's not going to work for IIS or IIS Express. IIS is running as a
reverse proxy. It picks a random port for your process to listen on
and does not flow the public address information to you. The only way
to get the public address information is from incoming requests.
The ASP.NET Core Module generates a dynamic port to assign to the backend process. CreateDefaultBuilder calls the UseIISIntegration method. UseIISIntegration configures Kestrel to listen on the dynamic port at the localhost IP address (127.0.0.1). If the dynamic port is 1234, Kestrel listens at 127.0.0.1:1234. This configuration replaces other URL configurations provided by.
But if you get the feature from the WebHost after it was built, then this can be used to get the local address for warm up.
I tried this way:
public static void Main(string[] args)
{
var builder = CreateWebHostBuilder(args);
var webHost = builder.Build();
var addresses = webHost.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
var address = addresses.FirstOrDefault();
AppDomain.CurrentDomain.SetData("BaseUrl", address?? "");
webHost.Run();
}
and got the local Kestrel address in the WarmUpService like this:
string baseUrl = AppDomain.CurrentDomain.GetData("BaseUrl").ToString();

"transparent" server side proxy for requests to ASP.NET Web API

Have an ASP.NET Web API endpoint that generates JSON responses. But due to two factors can't be consumed directly from a browser.
cross-domain issues
need to provide session ticket for the API that is known only server side
So I need a lightweight server side proxy for client(browser) requests to extend the request with session key. Do not want to impose an overhead deserializing client JSON requests or Web API JSON responses in the proxy code. Would like to pass the payload "as is" and deserialize client requests only Web API side and the Web API responses only client (browser) side. That is the proxy takes json from the browser and passes it directly to Web API. It also passes the JSON response from the Web API to the browser directly without deserialization. Just a dummy proxy that does not know anything about the data it transfers.
Please suggest is it feasible and what is the best way to implement it. The existing web application (the one that is used to generate the client pages) is implemented using ASP.NET MVC 4.
Thanks in advance.
update for 2021:
You should probably be looking at https://microsoft.github.io/reverse-proxy/ if you have found your way here
old answer:
I wrote one for a previous version of WebApi. The code should be fairly easy to update for your purposes.
The basic idea is that you create a WebApi DelegatingHandler that passes the request on to an HttpClient:
public class ForwardProxyMessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-Forwarded-For", request.GetClientIp());
if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Trace) request.Content = null;
request.RequestUri = new Uri(request.RequestUri.ToString().Replace(":3002", "")); //comes through with the port for the proxy, rewrite to port 80
request.Headers.AcceptEncoding.Clear();
var responseMessage = await new HttpClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
responseMessage.Headers.TransferEncodingChunked = null; //throws an error on calls to WebApi results
if (request.Method == HttpMethod.Head) responseMessage.Content = null;
return responseMessage;
}
}

How to host Web API in Windows Service

I have several resources that I'd like to expose using the WCF Web API. I've investigated the Web API using a Web host but our services all run as Windows Services in production so it's time for me to put the tests aside and verify that everything will work as we need it. I've looked as the sample app here: http://webapicontrib.codeplex.com/SourceControl/changeset/view/2d771a4d6f6f#Samples%2fSelfHosted%2fserver%2fProgram.cs but this does not work with the current version (preview 5) because the HttpConfigurableServiceHost class is not accessible from our code.
One of the most appealing aspects of the Web API is the simple startup using MapServiceRoute and the new WebApiConfiguration. I don't see, however, a way to define the base url and port for the services. Obviously, hosting the service in IIS eliminates this because we configure this information in IIS. How can I accomplish this when hosting in a Windows Service?
It's actually pretty simple. In a nutshell you need to instantiate HttpSelfHostServer and HttpSelfHostConfiguration and then call server.OpenAsync().
public void Start()
{
_server.OpenAsync();
}
public void Stop()
{
_server.CloseAsync().Wait();
_server.Dispose();
}
For an example on how to do this using Windows service project template and/or Topshelf library see my blog post: http://www.piotrwalat.net/hosting-web-api-in-windows-service/
The latest version just uses HttpServiceHost. http://webapicontrib.codeplex.com/SourceControl/changeset/view/ddc499585751#Samples%2fSelfHosted%2fserver%2fProgram.cs
Ping me on twitter if you continue to have problems.
This is the basic code using a console app. A Windows Service uses the same basic approach except you use the start and stop methods to start and stop the service and don't need to block.
static void Main(string[] args)
{
var host = new HttpServiceHost(typeof(PeopleService), "http://localhost:8080/people");
host.Open();
foreach (var ep in host.Description.Endpoints)
{
Console.WriteLine("Using {0} at {1}", ep.Binding.Name, ep.Address);
}
Console.ReadLine();
host.Close();
}
See this blog post.