问题
We are running an intranet Application on .Net Core 2.0. Application insight is very helpfull to catch exceptions. So when the User comes up to us for support we would like to find his request that caused problems in application insight. I added an ClientIpHeaderTelemetryInitializer like this:
public class CustomTelemetry : ClientIpHeaderTelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor ;
public CustomTelemetry(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// Implements initialization logic.
/// </summary>
/// <param name="platformContext">Http context.</param>
/// <param name="requestTelemetry">Request telemetry object associated with the current request.</param>
/// <param name="telemetry">Telemetry item to initialize.</param>
protected override void OnInitializeTelemetry(HttpContext platformContext, RequestTelemetry requestTelemetry, ITelemetry telemetry)
{
requestTelemetry.Properties.Add("UserName", _httpContextAccessor.HttpContext.User.Identity.Name);
if (string.IsNullOrEmpty(telemetry.Context.Location.Ip))
{
LocationContext location = requestTelemetry.Context.Location;
telemetry.Context.Location.Ip = location.Ip;
requestTelemetry.Properties.Add("InternalIP", location.Ip);
}
}
}
The Initalizer is registered at startup like this:
services.AddSingleton<ITelemetryInitializer, CustomTelemetry>();
When I look at the requests Custom Data still turns up empty.
Did I register it wrong? I found the exact same line to add the service in the Integration Tests of Application Insights (although commented out...)
Has anyone an Idea whats wrong?
回答1:
I finally got it working for my purposes.
public class CustomTelemetry : ClientIpHeaderTelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor ;
public CustomTelemetry(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// Implements initialization logic.
/// </summary>
/// <param name="platformContext">Http context.</param>
/// <param name="requestTelemetry">Request telemetry object associated with the current request.</param>
/// <param name="telemetry">Telemetry item to initialize.</param>
protected override void OnInitializeTelemetry(HttpContext platformContext, RequestTelemetry requestTelemetry, ITelemetry telemetry)
{
var userName = _httpContextAccessor.HttpContext?.User?.Identity?.Name; // Only set when request failed...
var ip = _httpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();
if (ip != null) telemetry.Context.GlobalProperties.TryAdd("InternalIP", ip);
if(userName != null) requestTelemetry.Properties.Add("UserName", userName);
}
}
(Note: ClientIpHeaderTelemetryInitializer is part of AppInsights)
Important here is to get everything from the HttpContext, checking if it is null and adding custom properties. The built in properties for IP can get overwritten otherwise. Migrosoft GDPR Blogpost
In the Startup add this to ConfigureService
services.AddSingleton<ITelemetryInitializer, CustomTelemetry>();
Make sure to add the ContextAccessor beforehand so it can be injected:
services.AddHttpContextAccessor();
In Configure add this at the beginning:
app.UseForwardedHeaders(new ForwardedHeadersOptions{ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto});
This is very important to optain the IP Adress when using reverse proxy like IIS. Otherwise you will always receive localhost. UseForwardedHeaders() without options just does nothing! (Also you will need a test setup with a remote server)
Getting the Username just seems to work when the Request fails (tested with 500 Internal Server Error). Otherwise the HttpContextAccessor doesn't populate the User object. Could be a 2.1 thing, quite annoying maybe someone finds a way to get it for every request.
In the end your information should arrive at ApplicationInsights and look like this:
回答2:
First, you need to add application insights to your asp.net core project via menu option in visual studio:
Second, in your CustomTelemetry
class, you'd better set a breakpoint to make sure it can be hit when code is running.
Third, in your CustomTelemetry
class, please do some logic when UserName
and InternalIP
is null or empty. If these values are null or empty, they will not be added.
My test code as below:
CustomTelemetry class:
public class CustomTelemetry: ITelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomTelemetry(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null) return;
if (!requestTelemetry.Context.Properties.ContainsKey("UserName"))
{
//if UserName is null or empty, add a default value for it
if (string.IsNullOrEmpty(_httpContextAccessor.HttpContext.User.Identity.Name))
{
requestTelemetry.Context.Properties.Add("UserName", "NA");
}
else
{
requestTelemetry.Context.Properties.Add("UserName", _httpContextAccessor.HttpContext.User.Identity.Name);
}
}
if (!requestTelemetry.Context.Properties.ContainsKey("InternalIP"))
{
//update
if (!string.IsNullOrEmpty(requestTelemetry.Context.Location.Ip))
{
requestTelemetry.Context.Properties.Add("InternalIP", requestTelemetry.Context.Location.Ip);
}
}
}
}
Then register in Startup
class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ITelemetryInitializer, CustomTelemetry>();
services.AddApplicationInsightsTelemetry();
}
Test result:
Since I don't have your ClientIpHeaderTelemetryInitializer
class, just write the above test code. If you need more help, please share that class.
来源:https://stackoverflow.com/questions/52854319/log-username-and-ip-with-custom-properties-in-application-insight-net-core-2-1