Dha*_*777 49 c# azure-application-insights
是否可以在Application Insights中查看POST请求正文?
我可以看到请求详细信息,但不能查看应用程序洞察中发布的有效负载.我是否需要通过一些编码来跟踪这个?
我正在构建一个MVC核心1.1 Web Api.
yon*_*sha 33
您可以简单地实现自己的遥测初始化器:
例如,在提取有效负载并将其添加为请求遥测的自定义维度的实现下面:
public class RequestBodyInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry != null && (requestTelemetry.HttpMethod == HttpMethod.Post.ToString() || requestTelemetry.HttpMethod == HttpMethod.Put.ToString()))
{
using (var reader = new StreamReader(HttpContext.Current.Request.InputStream))
{
string requestBody = reader.ReadToEnd();
requestTelemetry.Properties.Add("body", requestBody);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后通过配置文件或代码将其添加到配置中:
TelemetryConfiguration.Active.TelemetryInitializers.Add(new RequestBodyInitializer());
Run Code Online (Sandbox Code Playgroud)
然后在Google Analytics中查询:
requests | limit 1 | project customDimensions.body
Run Code Online (Sandbox Code Playgroud)
小智 19
@yonisha提供的解决方案在我看来是最干净的解决方案.但是你仍然需要在那里获得你的httpcontext,为此你需要更多的代码.我还插入了一些基于或取自上面的代码示例的注释.重置您的请求的位置很重要,否则您将丢失它的数据.
这是我测试的解决方案,并给了我jsonbody:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后还要确保将其添加到Startup - > ConfigureServices
services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();
Run Code Online (Sandbox Code Playgroud)
编辑:
如果您还想获得响应体,我发现创建一个中间件(dotnet核心不确定框架)是有用的.起初我采用了上面的方法来记录响应和请求,但大多数时候你想要这些一起.
public async Task Invoke(HttpContext context)
{
var reqBody = await this.GetRequestBodyForTelemetry(context.Request);
var respBody = await this.GetResponseBodyForTelemetry(context);
this.SendDataToTelemetryLog(reqBody, respBody, context);
}
Run Code Online (Sandbox Code Playgroud)
这等待请求和响应,其中请求与上面的请求几乎相同,而不是它是一项任务.
对于我使用下面代码的响应主体,我也排除了204,因为它导致了nullref:
public async Task<string> GetResponseBodyForTelemetry(HttpContext context)
{
var originalBody = context.Response.Body;
try
{
using (var memStream = new MemoryStream())
{
context.Response.Body = memStream;
//await the responsebody
await next(context);
if (context.Response.StatusCode == 204)
{
return null;
}
memStream.Position = 0;
var responseBody = new StreamReader(memStream).ReadToEnd();
//make sure to reset the position so the actual body is still available for the client
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
return responseBody;
}
}
finally
{
context.Response.Body = originalBody;
}
}
Run Code Online (Sandbox Code Playgroud)
use*_*994 15
几天前,我收到了一个类似的要求,即在应用程序洞察中记录请求正文,并从有效负载中过滤掉敏感的输入用户数据。所以分享我的解决方案。以下解决方案是为 ASP.NET Core 2.0 Web API 开发的。
动作过滤器属性
我使用了ActionFilterAttributefrom ( Microsoft.AspNetCore.Mvc.Filtersnamespace) ,它提供了 Model viaActionArgument以便通过反射,可以提取那些标记为敏感的属性。
public class LogActionFilterAttribute : ActionFilterAttribute
{
private readonly IHttpContextAccessor httpContextAccessor;
public LogActionFilterAttribute(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (context.HttpContext.Request.Method == HttpMethods.Post || context.HttpContext.Request.Method == HttpMethods.Put)
{
// Check parameter those are marked for not to log.
var methodInfo = ((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor).MethodInfo;
var noLogParameters = methodInfo.GetParameters().Where(p => p.GetCustomAttributes(true).Any(t => t.GetType() == typeof(NoLogAttribute))).Select(p => p.Name);
StringBuilder logBuilder = new StringBuilder();
foreach (var argument in context.ActionArguments.Where(a => !noLogParameters.Contains(a.Key)))
{
var serializedModel = JsonConvert.SerializeObject(argument.Value, new JsonSerializerSettings() { ContractResolver = new NoPIILogContractResolver() });
logBuilder.AppendLine($"key: {argument.Key}; value : {serializedModel}");
}
var telemetry = this.httpContextAccessor.HttpContext.Items["Telemetry"] as Microsoft.ApplicationInsights.DataContracts.RequestTelemetry;
if (telemetry != null)
{
telemetry.Context.GlobalProperties.Add("jsonBody", logBuilder.ToString());
}
}
await next();
}
}
Run Code Online (Sandbox Code Playgroud)
“LogActionFilterAttribute”作为过滤器注入到 MVC 管道中。
services.AddMvc(options =>
{
options.Filters.Add<LogActionFilterAttribute>();
});
Run Code Online (Sandbox Code Playgroud)
无日志属性
在上面的代码中,NoLogAttribute使用了应应用于模型/模型的属性或方法参数的属性,以指示不应记录该值。
public class NoLogAttribute : Attribute
{
}
Run Code Online (Sandbox Code Playgroud)
NoPIILogContractResolver
此外,NoPIILogContractResolver在使用JsonSerializerSettings期间,序列化过程中
internal class NoPIILogContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = new List<JsonProperty>();
if (!type.GetCustomAttributes(true).Any(t => t.GetType() == typeof(NoLogAttribute)))
{
IList<JsonProperty> retval = base.CreateProperties(type, memberSerialization);
var excludedProperties = type.GetProperties().Where(p => p.GetCustomAttributes(true).Any(t => t.GetType() == typeof(NoLogAttribute))).Select(s => s.Name);
foreach (var property in retval)
{
if (excludedProperties.Contains(property.PropertyName))
{
property.PropertyType = typeof(string);
property.ValueProvider = new PIIValueProvider("PII Data");
}
properties.Add(property);
}
}
return properties;
}
}
internal class PIIValueProvider : IValueProvider
{
private object defaultValue;
public PIIValueProvider(string defaultValue)
{
this.defaultValue = defaultValue;
}
public object GetValue(object target)
{
return this.defaultValue;
}
public void SetValue(object target, object value)
{
}
}
Run Code Online (Sandbox Code Playgroud)
PIITelemetryInitializer
要注入RequestTelemetry对象,我必须使用它ITelemetryInitializer以便RequestTelemetry可以在LogActionFilterAttribute类中检索。
public class PIITelemetryInitializer : ITelemetryInitializer
{
IHttpContextAccessor httpContextAccessor;
public PIITelemetryInitializer(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry telemetry)
{
if (this.httpContextAccessor.HttpContext != null)
{
if (telemetry is Microsoft.ApplicationInsights.DataContracts.RequestTelemetry)
{
this.httpContextAccessor.HttpContext.Items.TryAdd("Telemetry", telemetry);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
该PIITelemetryInitializer注册为
services.AddSingleton<ITelemetryInitializer, PIITelemetryInitializer>();
Run Code Online (Sandbox Code Playgroud)
测试功能
下面的代码演示了上面代码的用法
创建了一个控制器
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly ILogger _logger;
public ValuesController(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<ValuesController>();
}
// POST api/values
[HttpPost]
public void Post([FromBody, NoLog]string value)
{
}
[HttpPost]
[Route("user")]
public void AddUser(string id, [FromBody]User user)
{
}
}
Run Code Online (Sandbox Code Playgroud)
其中User模型定义为
public class User
{
[NoLog]
public string Id { get; set; }
public string Name { get; set; }
public DateTime AnneviseryDate { get; set; }
[NoLog]
public int LinkId { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public string AddressLine { get; set; }
[NoLog]
public string City { get; set; }
[NoLog]
public string Country { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
所以当 API 被 Swagger 工具调用时
jsonBody 在没有敏感数据的情况下登录请求。所有敏感数据都替换为“PII 数据”字符串文字。
我选择自定义中间件路径,因为它HttpContext已经在那里使事情变得更容易。
public class RequestBodyLoggingMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var method = context.Request.Method;
// Ensure the request body can be read multiple times
context.Request.EnableBuffering();
// Only if we are dealing with POST or PUT, GET and others shouldn't have a body
if (context.Request.Body.CanRead && (method == HttpMethods.Post || method == HttpMethods.Put))
{
// Leave stream open so next middleware can read it
using var reader = new StreamReader(
context.Request.Body,
Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 512, leaveOpen: true);
var requestBody = await reader.ReadToEndAsync();
// Reset stream position, so next middleware can read it
context.Request.Body.Position = 0;
// Write request body to App Insights
var requestTelemetry = context.Features.Get<RequestTelemetry>();
requestTelemetry?.Properties.Add("RequestBody", requestBody);
}
// Call next middleware in the pipeline
await next(context);
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我记录响应正文的方式
public class ResponseBodyLoggingMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var originalBodyStream = context.Response.Body;
try
{
// Swap out stream with one that is buffered and suports seeking
using var memoryStream = new MemoryStream();
context.Response.Body = memoryStream;
// hand over to the next middleware and wait for the call to return
await next(context);
// Read response body from memory stream
memoryStream.Position = 0;
var reader = new StreamReader(memoryStream);
var responseBody = await reader.ReadToEndAsync();
// Copy body back to so its available to the user agent
memoryStream.Position = 0;
await memoryStream.CopyToAsync(originalBodyStream);
// Write response body to App Insights
var requestTelemetry = context.Features.Get<RequestTelemetry>();
requestTelemetry?.Properties.Add("ResponseBody", responseBody);
}
finally
{
context.Response.Body = originalBodyStream;
}
}
}
Run Code Online (Sandbox Code Playgroud)
比添加扩展方法...
public static class ApplicationInsightExtensions
{
public static IApplicationBuilder UseRequestBodyLogging(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestBodyLoggingMiddleware>();
}
public static IApplicationBuilder UseResponseBodyLogging(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ResponseBodyLoggingMiddleware>();
}
}
Run Code Online (Sandbox Code Playgroud)
...允许在内部进行干净的集成 Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Enable our custom middleware
app.UseRequestBodyLogging();
app.UseResponseBodyLogging();
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
不要忘记在里面注册自定义中间件组件 ConfigureServices()
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddApplicationInsightsTelemetry(Configuration["APPINSIGHTS_CONNECTIONSTRING"]);
services.AddTransient<RequestBodyLoggingMiddleware>();
services.AddTransient<ResponseBodyLoggingMiddleware>();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
22875 次 |
| 最近记录: |