I have implemented a custom authentication scheme in a web service based on the ASP.NET Core webhost. I want to add Application Insights to this service.
When I successfully authenticate the user, I do something like this
telemetry.Context.User.Id = authenticatedUserName;
the telemetry object is the TelemetryClient I get from dependency injection.
Now, the problem is that the user ID does not show up among the requests, and I am not sure why.
This works
customEvents | where user_Id != "" and name == "MyCustomEvent"
but not this
request | where user_Id != ""
or this
dependencies | where user_Id != ""
Is there somewhere else where I should set the user ID for the request? I'd rather not create a custom event just for this.
I also tried setting the User property on the HttpContext object, but it does not seem to have any effect.
You should use ITelemetryInitializer for your purpose.
The following is my test steps(asp.net core 2.1):
Step 1:Add the Aplication Insights telemetry by right click your project -> Add -> Application Insights telemetry. The screenshot as below:
Step 2:Add a new class which implements the ITelemetryInitializer:
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
namespace WebApplication33netcore
{
public class MyTelemetryInitializer: ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var request = telemetry as RequestTelemetry;
if (request != null)
{
//set the user id here with your custom value
request.Context.User.Id = "ivan111";
}
}
}
}
Step 3:Register your telemetry initializer in ConfigureServices method in Startup.cs. For details, refer to here:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//Add the following code to register your telemetry initializer
services.AddSingleton<ITelemetryInitializer>(new MyTelemetryInitializer());
}
Step 4:Check the test result:
In visual studio Application Insights Search:
Then check it in Analytics:
Actually, the answer was surprisingly simple.
HttpContext ctx = ...
var requestTelemetry = ctx.Features.Get<RequestTelemetry>()
requestTelemetry.Context.User.Id = authenticationResult.UserName;
Related
I have a .Net core Web App API which accept request from front-end and then send HTTP POST request to Azure search to get search results.
I just use the build-in application insights logging log basic info in request and dependencies sources with zero logging code.
Now I want to extend the default Application Insights dependencies table to add the request body to Azure search.
What is the easiest way with minimum code?
As per your requirement, you can try the code below:
public class RequestBodyInitializer : ITelemetryInitializer
{
readonly IHttpContextAccessor httpContextAccessor;
public RequestBodyInitializer(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry telemetry)
{
if (telemetry is RequestTelemetry requestTelemetry)
{
if ((httpContextAccessor.HttpContext.Request.Method == HttpMethods.Post ||
httpContextAccessor.HttpContext.Request.Method == HttpMethods.Put) &&
httpContextAccessor.HttpContext.Request.Body.CanRead)
{
const string jsonBody = "JsonBody";
if (requestTelemetry.Properties.ContainsKey(jsonBody))
{
return;
}
//Allows re-usage of the stream
httpContextAccessor.HttpContext.Request.EnableRewind();
var stream = new StreamReader(httpContextAccessor.HttpContext.Request.Body);
var body = stream.ReadToEnd();
//Reset the stream so data is not lost
httpContextAccessor.HttpContext.Request.Body.Position = 0;
requestTelemetry.Properties.Add(jsonBody, body);
}
}
}
and add this to the Startup > ConfigureServices
services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();
I have URL /users/myuser#gmail.com. I don't want to log the actual email in the logs. What can I do?
I really like to be logged like /users/m***r#gmail.com but I just don't know if it is possible and how.
Update
It is a c# on ASP.net core 3.1 framework.
You can use ITelemetryInitializer to modify it.
I write a sample code snippet for asp.net core. For testing purpose, I just replace the information(like the email) in the url. Please feel free to change it to meet your need.
1.Add a new class, and implement it with the following code:
public class MyTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null) return;
if (requestTelemetry.Url.ToString().Contains("Home"))
{
string str1 = requestTelemetry.Url.ToString();
//for test purpose, it is just replaced here.
Uri uri = new Uri(requestTelemetry.Url.ToString().Replace("Home", "myhome222"));
requestTelemetry.Url = uri;
//it will also be shown in the Name and Operation_name, you should write your logic to do that.
//requestTelemetry.Name = "";
//requestTelemetry.Context.Operation.Name = "";
}
}
}
2.in the Startup.cs -> ConfigureServices method,register the ITelemetryInitializer:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddApplicationInsightsTelemetry();
//use this line of code to register.
services.AddSingleton<ITelemetryInitializer, MyTelemetryInitializer>();
}
I'm trying to get some additional information about the message we received (basically its application-level outcome) into the Application Insights RequestTelemetry object for a WCF service.
Application Insights is logging request telemetry already. I created an ITelemetryInitializer that is being run, but at the time it runs I have no way that I can find to access information about the request, much less application-specific data from the request's context.
Is there somewhere I can put data that will be accessible by the ITelemetryInitializer at the time it runs?
public class WcfServiceTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
if (telemetry is RequestTelemetry rTelemetry)
{
// HttpContext.Current is populated at this point, but doesn't seem to be available within my application code.
// So is System.ServiceModel.OperationContext.Current
}
}
}
I had to face similar issue as the author described. Tried by implementing ITelemetryInitializer/ITelemetryProcessor but did not work.
Ended up writing my own MessageTraceTelemetryModule class implementing IWcfTelemetryModule and IWcfMessageTrace.
In the OnTraceResponse method, I added my custom property to the request telemetry by extracting value from OperationContext (which is accessible here!):
internal class MessageTraceTelemetryModule : IWcfTelemetryModule, IWcfMessageTrace
{
public void OnTraceResponse(IOperationContext operation, ref Message response)
{
if (OperationContext.Current.IncomingMessageProperties.TryGetValue("clientID", out object value))
{
operation.Request.Properties.Add("clientID", value.ToString());
}
}
}
New custom property visible in Application Insights telemetry - ClientID custom property Pic.
Note that the clientID property is being set in the OperationContext in Message Inspector:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if(!OperationContext.Current.IncomingMessageProperties.ContainsKey("clientID"))
OperationContext.Current.IncomingMessageProperties.Add("clientID", clientID);
}
Brief Context:
I implemented AAD Token based Authentication in a SOAP based WCF Service.
I needed to store the clientID from the token (which is validated in message inspector) and add the same as a custom property in the application insights request telemetry.
References:
Message Inspectors Documentation
Application Insights for WCF Documentation
In case it helps anyone, Application Insights automatically adds custom dimensions from data you store in System.Diagnostics.Activity.Current.AddBaggage(), or at least it does in asp.net 5. That might be available at the right place for you in WCF land.
e.g.
var currentActivity = System.Diagnostics.Activity.Current;
if (currentActivity != null)
{
currentActivity.AddBaggage("MyPropertyName", someData);
}
To log custom property, you can try like this...
public class CustomTelemetry : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null) return;
requestTelemetry.Properties.Add("CustomProperty", "DummyValue");
}
}
And register CustomTelemetry at start of the application
TelemetryConfiguration.Active.TelemetryInitializers.Add(new CustomTelemetry());
Here is the Original Answer
MS Doc - Application Insights API for custom events and metrics
Recently we encountered an issue where our servers times out because of huge traffic surge, and those request telemetries were logged into AI as success with response code zero. Is there any way to configure response code zero to be termed as failure. Since request telemetries are captured automatically by AI so we dont have any handle on that
You can do it by using ITelemetryInitializer in your .NET core project.
To be termed as failure when response code is zero, you can set the Success property of request telemetry data as false. The sample code as below(using .NET core 2.2 for this test). And please make sure you're using the latest version of Microsoft.ApplicationInsights.AspNetCore 2.13.1.
Here is the custom ITelemetryInitializer:
public class MyTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
if (telemetry is RequestTelemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
//you can change the ResponseCode to "0" in your project
if (requestTelemetry.ResponseCode == "200")
{
// set Success property to false
requestTelemetry.Success = false;
}
}
}
}
then register it in the Startup.cs -> ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
//your other code
//here, register the custom ITelemetryInitializer
services.AddSingleton<ITelemetryInitializer, MyTelemetryInitializer>();
}
After executing the code, in azure portal -> your application insights -> Logs, you can see the Success property of request are made as false:
I work on .NET Core 2.2 console application that uses Microsoft.Extensions.Logging and is configured to send logs to Azure Application Insights using Microsoft.ApplicationInsights.Extensibility by:
services.AddSingleton(x =>
new TelemetryClient(
new TelemetryConfiguration
{
InstrumentationKey = "xxxx"
}));
...
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
loggerFactory.AddApplicationInsights(serviceProvider, logLevel);
It works ok: I can read logs in Application Insights. But the application can be started simultanously in few instances (in different Docker containers). How can I distinguish traces from different instances? I can use source FileName, but I don't know how I should inject it.
I tried to use Scope:
var logger = loggerFactory.CreateLogger<Worker>();
logger.BeginScope(dto.FileName);
logger.LogInformation($"Start logging.");
It's interesting that my configuration is almost identical as in example: https://github.com/MicrosoftDocs/azure-docs/issues/12673
But in my case I can't see the property "FileName" in Application Insights.
For console project, if you want to use the custom ITelemetryInitializer, you should use this format: .TelemetryInitializers.Add(new CustomInitializer());
Official doc is here.
I test it at my side, and it works. The role name can be set.
Sample code is below:
static void Main(string[] args)
{
TelemetryConfiguration configuration = TelemetryConfiguration.CreateDefault();
configuration.InstrumentationKey = "xxxxx";
configuration.TelemetryInitializers.Add(new CustomInitializer());
var client = new TelemetryClient(configuration);
ServiceCollection services = new ServiceCollection();
services.AddSingleton(x => client);
var provider = services.BuildServiceProvider();
var loggerFactory = new LoggerFactory();
loggerFactory.AddApplicationInsights(provider, LogLevel.Information);
var logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("a test message 111...");
Console.WriteLine("Hello World!");
Console.ReadLine();
}
Check the role name in azure portal:
If you really have no way to distinguish them you can use a custom telemetry initializer like this:
public class CustomInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = Environment.MachineName;
}
}
and/or you can add a custom property:
public class CustomInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
if(telemetry is ISupportProperties)
{
((ISupportProperties)telemetry).Properties["MyIdentifier"] = Environment.MachineName;
}
}
}
In this example I used Environment.MachineName but you can of course use something else if needed. Like this work Id parameter of yours.
the wire it up using:
services.AddSingleton<ITelemetryInitializer, CustomInitializer>();