Вы можете просто реализовать свой собственный телеметрии Initializer:
Например, ниже реализация, которая извлекает полезные данные и добавляет его в качестве специального параметра запроса телеметрии:
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);
}
}
}
}
Затем добавить его в конфигурацию либо конфигурация file или через код:
TelemetryConfiguration.Active.TelemetryInitializers.Add(new RequestBodyInitializer());
Затем запросить его в аналитике:
requests | limit 1 | project customDimensions.body
Решение, предлагаемое @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);
}
}
}
Затем также будьте уверены, чтобы добавить его в свой стартап -> ConfigureServices
services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();
Редактировать:
Если вы также хотите, чтобы получить тело ответа я нашел его полезным для создания кусок промежуточного (.Объем ядра, не уверен насчет рамки). Сначала я принял выше подхода, когда вы входите ответ и запрос, но большую часть времени вы хотите, чтобы эти вместе:
public async Task Invoke(HttpContext context)
{
var reqBody = await this.GetRequestBodyForTelemetry(context.Request);
var respBody = await this.GetResponseBodyForTelemetry(context);
this.SendDataToTelemetryLog(reqBody, respBody, context);
}
Это ждет и запрос, и ответ. `GetRequestBodyForTelemetry почти идентичен коду из телеметрии инициализатора, за исключением использования "задачей". Для организма ответ я использовал код ниже, я также исключил 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;
}
}
Несколько дней назад, я получил аналогичное требование для входа в тело запроса в выводы приложение с фильтрация входных данных пользователя от полезной нагрузки. Итак, делюсь своим решением. Ниже решение разработано для ASP.NET ядро веб-API 2.0.
ActionFilterAttribute
Я'вэ использовал ActionFilterAttribute
с (`Майкрософт.AspNetCore.В MVC.Пространство имен фильтров), который обеспечивает модели через ActionArgument так что, поразмыслив, эти свойства могут быть извлечены, помеченные как конфиденциальные.
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();
}
}
В 'LogActionFilterAttribute' вводят в трубопровод MVC как фильтр.
services.AddMvc(options =>
{
options.Filters.Add<LogActionFilterAttribute>();
});
NoLogAttribute
В приведенном выше коде, атрибут NoLogAttribute `используется, который должен быть применен на модели/модели's свойства или параметра метода, чтобы указать, что значение не должно быть зарегистрировано.
public class NoLogAttribute : Attribute
{
}
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)
{
}
}
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);
}
}
}
}
В PIITelemetryInitializer
зарегистрирована, как
services.AddSingleton<ITelemetryInitializer, PIITelemetryInitializer>();
Функция тестирования
Следующий код демонстрирует использование кода выше
Создал контроллер
[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)
{
}
}
Где "пользователь" модель определяется как
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; }
}
Поэтому, когда API вызывается с помощью инструмента чванство
В jsonBody регистрируется в запросе без конфиденциальных данных. Все конфиденциальные данные будут заменены на 'данных PII' строковый литерал.
Я реализовал middleware для этого,
Вызвать метод,
if (context.Request.Method == "POST" || context.Request.Method == "PUT")
{
var bodyStr = GetRequestBody(context);
var telemetryClient = new TelemetryClient();
var traceTelemetry = new TraceTelemetry
{
Message = bodyStr,
SeverityLevel = SeverityLevel.Verbose
};
//Send a trace message for display in Diagnostic Search.
telemetryClient.TrackTrace(traceTelemetry);
}
Где, GetRequestBody как,
private static string GetRequestBody(HttpContext context)
{
var bodyStr = "";
var req = context.Request;
//Allows using several time the stream in ASP.Net Core.
req.EnableRewind();
//Important: keep stream opened to read when handling the request.
using (var reader = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
{
bodyStr = reader.ReadToEnd();
}
// Rewind, so the core is not lost when it looks the body for the request.
req.Body.Position = 0;
return bodyStr;
}
Решение, предлагаемое yonisha чистый, но это не работает для меня .Чистая Ядра 2.0. Это работает, если у вас есть тело JSON:
public IActionResult MyAction ([FromBody] PayloadObject payloadObject)
{
//create a dictionary to store the json string
var customDataDict = new Dictionary<string, string>();
//convert the object to a json string
string activationRequestJson = JsonConvert.SerializeObject(
new
{
payloadObject = payloadObject
});
customDataDict.Add("body", activationRequestJson);
//Track this event, with the json string, in Application Insights
telemetryClient.TrackEvent("MyAction", customDataDict);
return Ok();
}
Я могу войти в тело запроса сообщение в Выводы приложения с помощью метода @yonisha но я могу'т уметь записывать ответ текст сообщения. Меня интересует запись ответного сообщения. Я уже лесозаготовок пост, поставить, удалить запрос тело сообщения с помощью метода @yonisha.
Когда я пытался получить доступ к телу ответа в TelemetryInitializer я постоянно получаю исключение с сообщением об ошибке, говорящее, что "поток не читается. Когда я исследовал еще я обнаружил, что AzureInitializer работает как часть модуль httpmodule(ApplicationInsightsWebTracking), поэтому к тому времени, он получает объект управления ответ будет удален.
У меня есть идея от @Оскар ответа. Почему бы не делегировать обработчик и запись ответа, так как ответ объект не удаляется на стадии обработки сообщений. Обработчик сообщений является частью веб-жизненный цикл по API, т. е. похожие на модуль HTTP, но ограничивается веб-API. Когда я разрабатывал и проверял эту идею, к счастью, он работал, я записал ответ на сообщение-запрос с помощью обработчика сообщений и извлекать его в AzureInitializer (модуля http, выполнение которых происходит позднее, чем обработчик сообщений). Вот пример кода.
public class AzureRequestResponseInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry != null && HttpContext.Current != null && HttpContext.Current.Request != null)
{
if ((HttpContext.Current.Request.HttpMethod == HttpMethod.Post.ToString()
|| HttpContext.Current.Request.HttpMethod == HttpMethod.Put.ToString()) &&
HttpContext.Current.Request.Url.AbsoluteUri.Contains("api"))
using (var reader = new StreamReader(HttpContext.Current.Request.InputStream))
{
HttpContext.Current.Request.InputStream.Position = 0;
string requestBody = reader.ReadToEnd();
if (requestTelemetry.Properties.Keys.Contains("requestbody"))
{
requestTelemetry.Properties["requestbody"] = requestBody;
}
else
{
requestTelemetry.Properties.Add("requestbody", requestBody);
}
}
else if (HttpContext.Current.Request.HttpMethod == HttpMethod.Get.ToString()
&& HttpContext.Current.Response.ContentType.Contains("application/json"))
{
var netHttpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
if (netHttpRequestMessage.Properties.Keys.Contains("responsejson"))
{
var responseJson = netHttpRequestMessage.Properties["responsejson"].ToString();
if (requestTelemetry.Properties.Keys.Contains("responsebody"))
{
requestTelemetry.Properties["responsebody"] = responseJson;
}
else
{
requestTelemetry.Properties.Add("responsebody", responseJson);
}
}
}
}
}
}
конфиг.MessageHandlers.Добавить(новый LoggingHandler());
public class LoggingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
StoreResponse(response);
return response;
});
}
private void StoreResponse(HttpResponseMessage response)
{
var request = response.RequestMessage;
(response.Content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
var ctx = request.Properties["MS_HttpContext"] as HttpContextWrapper;
if (request.Properties.ContainsKey("responseJson"))
{
request.Properties["responsejson"] = x.Result;
}
else
{
request.Properties.Add("responsejson", x.Result);
}
});
}
}
Извините, @yonisha'ы решение не похоже на работу в .Чистая 4.7. Приложение выводы часть работает нормально, но нет простой способ, чтобы получить тело запроса внутри инициализатора в телеметрии .Чистая 4.7. .Чистая 4.7 использует GetBufferlessInputStream (), чтобы получить поток, и этот поток является "когда-то читал, что". Один потенциальный код такой:
private static void LogRequestBody(ISupportProperties requestTelemetry)
{
var requestStream = HttpContext.Current?.Request?.GetBufferlessInputStream();
if (requestStream?.Length > 0)
using (var reader = new StreamReader(requestStream))
{
string body = reader.ReadToEnd();
requestTelemetry.Properties["body"] = body.Substring(0, Math.Min(body.Length, 8192));
}
}
Но возвращение из GetBufferlessInputStream() уже употребляют, и не поддерживает поиск. Таким образом, тело всегда будет пустая строка.
Я никогда не получал @yonisha'ы ответ работал, так что я использовал вместо DelegatingHandler
:
public class MessageTracingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Trace the request
await TraceRequest(request);
// Execute the request
var response = await base.SendAsync(request, cancellationToken);
// Trace the response
await TraceResponse(response);
return response;
}
private async Task TraceRequest(HttpRequestMessage request)
{
try
{
var requestTelemetry = HttpContext.Current?.GetRequestTelemetry();
var requestTraceInfo = request.Content != null ? await request.Content.ReadAsByteArrayAsync() : null;
var body = requestTraceInfo.ToString();
if (!string.IsNullOrWhiteSpace(body) && requestTelemetry != null)
{
requestTelemetry.Properties.Add("Request Body", body);
}
}
catch (Exception exception)
{
// Log exception
}
}
private async Task TraceResponse(HttpResponseMessage response)
{
try
{
var requestTelemetry = HttpContext.Current?.GetRequestTelemetry();
var responseTraceInfo = response.Content != null ? await response.Content.ReadAsByteArrayAsync() : null;
var body = responseTraceInfo.ToString();
if (!string.IsNullOrWhiteSpace(body) && requestTelemetry != null)
{
requestTelemetry.Properties.Add("Response Body", body);
}
}
catch (Exception exception)
{
// Log exception
}
}
}
.GetRequestTelemetry()
метод расширения от Microsoft.ApplicationInsights.Web.