Application Insights - Custom TrackRequest is creating duplicate messages - c#

I want to be able to add custom properties to a request telemetry. I am able to do this with code such as:
public void LogRequest(IDictionary<string,string> properties)
{
var client = new TelemetryClient();
var request = new RequestTelemetry();
foreach(var prop in properties)
{
request.Properties.Add(prop );
}
client.TrackRequest(request);
}
This code works in the sense that it creates a request telemetry with all of the custom properties I wanted, however the application insights SDK is also creating a duplicate request telemetry (without my custom properties). So it's sending its own request telemetry and the one that I created.
While trying to do some research I found this:
https://github.com/Azure/Azure-Functions/wiki/App-Insights-Early-Preview
Custom telemetry
You can bring the .NET App Insights SDK in and create your own TelemetryClient. There isn’t any conflicts, but there is some advice:
Don’t create TrackRequest or use the StartOperation methods if you don’t want duplicate requests – we do this automatically.
So my question is, is there anyway to send in my own custom request telemetry without the sdk automatically creating a duplicate message?
Also I would like to avoid using TrackEvent. Most of the information I need is already in the request object so I would prefer to use TrackRequest.
This is what I have in my application insights config in the track request section:
<TelemetryModules>
<Add Type="Microsoft.ApplicationInsights.Web.RequestTrackingTelemetryModule, Microsoft.AI.Web">
<Handlers>
<Add>System.Web.Handlers.TransferRequestHandler</Add>
<Add>Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.RequestDataHttpHandler</Add>
<Add>System.Web.StaticFileHandler</Add>
<Add>System.Web.Handlers.AssemblyResourceLoader</Add>
<Add>System.Web.Optimization.BundleHandler</Add>
<Add>System.Web.Script.Services.ScriptHandlerFactory</Add>
<Add>System.Web.Handlers.TraceHandler</Add>
<Add>System.Web.Services.Discovery.DiscoveryRequestHandler</Add>
<Add>System.Web.HttpDebugHandler</Add>
</Handlers>
</Add>
</TelemetryModules>

The reason is that AI SDK automatically track requests for you, and therefore you get dups (the one w/o your properties is the one created automatically).
As PeterBons suggested, using Telemetry Initializer you can add the properties to the auto-generated request.

Related

How to use ITelemetryInitializer for each class respectively on dependency telemetry

I want to add customer properties for dependency telemetry(outgoing request).
Saw the answer here
How to extend Dependency tracking for outgoing http requests in Application Insights
Right now I use one telemetry initializer for the whole project via config file.
But what I want to do is add different custom property value based on which outgoing request based on which class send out.
Ex: A.cs and B.cs, for all outgoing request send out by A.cs, add customer property like {"sender", "A.cs"}, and also some value should be captured on run time.
So how do I
Setup telemetry initializer for each different class, not whole project?
Is it possible that I can pass parameter to the telemetry initializer so I can set customer property based on the parameter?
Thanks

ASP.NET Core 2.1 get current web hostname and port in Startup.cs

I want to register my WebAPI to Consul service discovery and for that I should provide URL of my WebAPI (for example: http://service1.com) and health check endpoint (http://service1.com/health/check). How can I get that URL?
I found this piece of code:
var features = app.Properties["server.Features"] as FeatureCollection;
var addresses = features.Get<IServerAddressesFeature>();
var address = addresses.Addresses.First();
var uri = new Uri(address);
It returns 127.0.0.1:16478 instead of localhost:5600. I think first one used by dotnet.exe and second one is IIS which forwards 5600 to 16478. How can I get localhost:5600 in Startup.cs?
Well, there are multiple solutions for this problem. Your address is this:
string myurl = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
It returns 127.0.0.1:16478 instead of localhost:5600
You got this right yes. One is from IIS and one from dotnet. So, you have a problem of trying to get correct url. Ok, so what happens if you service is behind reverse proxy? https://en.wikipedia.org/wiki/Reverse_proxy
Then your service will not be exposed directly to internet, but requests made to specific url will be passed from reverse proxy to your service. Also, you can configure reverse proxy to forward additional headers that are specifying where the original request came from. I think that most reverse proxies are using X-Forwarded-For (Some of them are using X-Original-Host etc).
So if you have proper header set on RP, then you can get url like this:
url = url.RequestContext.HttpContext.Request.Headers["X-Forwarded-For"]
Url is of type UrlHelper. To simplify this method, you can create extension method (GetHostname(this UrlHelper url)) and then us it in your controller or wherever you want. Hope it helps
I don't think it is possible since there is usually a reverse proxy in production that handles public address and the application itself should not be exposed to public and, therefore, be aware of public address. But there can be some workarounds:
Place URL is some kind of config file that can be updated during deploy steps to have the correct URL.
Application can get full URL of the request like this, so after first actual request to the application we can get hostname.
EDIT: I reread your question. You wanted to know how to do this in Startup.cs. You can, but with fewer fallbacks. Your only choices are configuration or raw DNS.GetHostName(), which are less than ideal. Instead, upon any request to your service, lazily register your API. This is when you have context. Prior to that, your service knows nothing Jon Snow. The first request to your API is likely going to be health-checks, so that will kick off your registration with consul.
A solution I've used is a combination of configuration and headers in a fallback scenario.
Rely first on the X-Forwarded-For header. If there are cases where that doesn't apply or you have a need to... you can fallback to configuration.
This works for your use case, discovery. That said, it also works when you want to generate links for any reason, (e.g. for hypermedia for JSON API or your own REST implementation).
The fallback can be useful when there are reconfigurations occuring, and you have a dynamic configuration source that doesn't require a redeployment.
In the ASP.NET Core world, you can create a class and inject it into your controllers and services. This class would have a method that knows to try config first (to see if overriding is needed) and then the X-Forwarded-For header, and if neither is appropriate, fallback further to HttpContext.Request to get relevant URI parts.
What you're doing is enabling your API to be contextless and resiliency (to change) by giving it some contextual awareness of where "it lives".
This happens when you try to get current URL in Startup.cs. I've faced this issue before. What i did as Solution for my problem is. I Just declared Custom Setting in AppSettings in web.config(For Local) file and web.release.config(For Live)
like following
in web.config
<appSettings>
<add key="MyHost" value="http://localhost:5600" />
</appSettings>
in web.release.config
<appSettings>
<add key="MyHost" value="http://myLiveURL.com" />
</appSettings>
in startup.cs
string hostSetting = ConfigurationSettings.AppSettings["MyHost"];
And different host in release file. so what it helped is i can get Localhost URL in local website from web.config and Live URL from web.release.config.
if you are using Azure for live. it will be more easier for live(you would not need to add setting web.release.config file). add app setting in your website application setting https://learn.microsoft.com/en-us/azure/app-service/configure-common
In Case of ASP.NET Core you can use appsettings.json instead of web.config

How to reload AWS options at runtime

I am running an ASP.NET Core MVC app in a docker container, with an AWS credentials file. I have another service that is putting new keys into the file when the old ones expire, but these new keys don't seem to propagate through to my MVC app and my site crashes. I have seen that normally the solution to get strongly typed configuration to reload is to use IOptionsSnapshot, like:
services.AddDefaultAWSOptions(Configuration.GetAWSOptions())
.AddScoped(config => config.GetService<IOptionsSnapshot<AWSOptions>>().Value)
.AddAWSService<IAmazonS3>();
but this gives an exception:
System.InvalidOperationException: Cannot resolve scoped service 'Amazon.Extensions.NETCore.Setup.AWSOptions' from root provider.
Does anyone have a solution to getting ASP to reload the AWS credentials file? I'd like to continue using the AWS dependency injection extension if possible.
By default, AddAWSService registers the client factory in singleton scope, which means it's one and done for the life of the application. However, AddAWSService has a lifetime param you can utilize to customize this. Essentially, you need a shorter lifetime on the client, so that it will be recreated with the new settings. You can choose either "scoped" (request-scoped) or "transient" (new instance every time it's injected).
Obviously with "scoped", you'll get a connection with the updated settings every request. However, if you do any further operations on the same request after the settings have been changed, it will remain the old connection with the old settings (i.e. you'll still have the same issue, at least for the life of the request).
Using "transient" scope, you'll have have a client with the most updated settings, but you'll end up basically with a client for every use, which may not be ideal.

NLog inject context information c#

I'm using ASP.NET WebApi and NLog.
I want to add per-request information like a correlationId to my log messages. In the best case, the user of NLog shouldn't know anything about this. The Logger itself should be able to get the information from the http request.
With Unity i can use the "PerRequestLifetimeManager" to inject those information, but it isn't recommenden. I should rather use HttpContext.Items, but i'm not happy with System.Web and HttpContext.
Is there a possibility to set the information on the server and get them in my logger every time i want to log something, based on the request scope?
NLog has the NLog.MappedDiagnosticContext that you can use to create session type of logging variables. In ASP.Net and WebAPI, using async contexts, you might need to use NLog.MappedDiagnosticsLogicalContext
You'll also need to update the Targets' layouts to include this information:
<target layout="${longdate}|${level:uppercase=true}|${logger}|[${mdc:item=SomeVariable}]${message}" >
Here's how you'd use it:
try
{
NLog.MappedDiagnosticsLogicalContext.Set("SomeVariable", Guid.NewGuid().ToString());
//do your work
Log.Info("Some message here.");
//do more work
Log.Info("Finished!");
}
finally
{
NLog.MappedDiagnosticsLogicalContext.Remove("SomeVariable");
}
Of note, I'd like to see a better way to do this utilizing the C# using statement, like log4net supported.

How do I log the raw HTTP request in ASP.NET Web API whether or not it routes to a controller?

Is it possible to log every HTTP request made to an ASP.NET Web API, even if the request is malformed, or for some other reason fails to route to one of the controllers.
For example, if a POST method has an Order model as it's parameter, an incorrect request will prevent it from ever reaching the controller's POST method. I'd like to alert someone so that actions can be taken to prevent future failures.
Is there a way to capture these requests further upstream from the controller?
Either use Tracing, you need to implement ITraceWriter as shown below
http://www.asp.net/web-api/overview/testing-and-debugging/tracing-in-aspnet-web-api
Or implement message handlers
http://www.strathweb.com/2012/05/implementing-message-handlers-to-track-your-asp-net-web-api-usage/
http://www.asp.net/web-api/overview/working-with-http/http-message-handlers
Message handlers allow you to change message before it comes to HttpControllerDispatcher, and therefore you can handle common problems with routing and action method selection.
But as the last do not hesitate to use AppFabric logging, when you are hosting on IIS, because it can give you information when something is going wrong before requests come to your Web Application. It handles scenarions with global errors in web application, for example errors with web.config.
If you enable tracing in the ASP.NET Web API per Tracing in ASP.NET Web API, the built-in tracing infrastructure will log the information you are after.
In the case of a malformed request that fails content negotiation, you will see an HttpError occur in the DefaultContentNegotiator.
Here is an example of the simple trace for this type of error:
DefaultContentNegotiator;Negotiate;Type='HttpError',
formatters=[JsonMediaTypeFormatterTracer, XmlMediaTypeFormatterTracer,
FormUrlEncodedMediaTypeFormatterTracer,
FormUrlEncodedMediaTypeFormatterTracer]
The trace writer is given a TraceRecord as its input, which will contain the request information as well as optionally any custom information you might want to use.
The Web API will use the trace writer you configure to trace information throughout the lifecycle of requests. You can use trace writer to trace both the lifecycle events as well as your own controller code.

Categories