Я хотел бы увеличить/улучшить свое ведение журнала.
До сих пор у меня был в каждом коде действия контроллера, например
public asyc Task<IActionResult> Action1(int id, [FromBody] RequestModel request) {
string log = $"{nameof(Action1)}(id: {id}, request: {request?.ToJson()})";
_logger.LogInformation(log);
Основная цель состояла в том, чтобы увидеть, что на самом деле достигает действия контроллера.
Я удалил его, так как он сильно загромождал код (например, для методов с большим количеством параметров). Но теперь я недоволен тем, что в логах больше не отображается информация (а они мне были нужны для расследования каких-то необъяснимых багов).
Есть ли способ подключиться к результату связывания модели (например, через сервисный фильтр), чтобы зарегистрировать результат связывания модели?
Работает как шарм: благодаря Шахзаду Хасану
public class MethodCallParameterLogger : IAsyncActionFilter
{
public ILoggerFactory LoggerFactory { get; set; }
public MethodCallParameterLogger(ILoggerFactory loggerFactory)
{
LoggerFactory = loggerFactory;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
LoggerFactory
// the display name contains the controller name space, controller name, method name and the postfix " (ActionLogger)"
.CreateLogger(context.ActionDescriptor.DisplayName.Split(" ")[0])
// MIND THAT THIS LOGS EVEN SENSITIVE DATA (e.g. credentials) !!!
.LogInformation(JsonConvert.SerializeObject(context.ActionArguments));
var resultContext = await next();
}
}
Первое, что пришло мне в голову, это связыватель моделей, поскольку он производит то, что я хочу регистрировать. Любые намеки в направлении. Я в темноте, и, кажется, вы знаете, что я ищу. :-)
Пока ничего не могу показать, есть новый ноутбук, и он еще не настроен. Конечно, регистрация всех параметров — это огромная дыра в безопасности, и я бы не позволил своим разработчикам делать это.
Лучше, чем регистрировать полные тела запросов :-) Да, конечно, в примере кода avove ToJson() позаботился об удалении конфиденциальных данных. Как только я понял, как подключиться к процессу, я подумал о SensitiveAttribute, который мой код регистрации учитывает и очищает эти значения. Но один шаг за другим. И конечно это только для тестовых машин. :-)





Я думаю, что вместо этого вы можете использовать ActionFilter. ActionFilters выполняются после привязки модели, поэтому вы можете получить параметры из файла ActionExecutingContext. Вы можете переопределить метод OnActionExecuting и зарегистрировать все, что требуется:
public class LogParamsFilter : ActionFilterAttribute
{
private readonly ILogger<LogsParamsFilter> _logger;
public LogParamsFilter (ILogger<LogsParamsFilter> logger)
{
_logger = logger;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var id = (int)context.ActionArguments["id"];
var request = context.ActionArguments["request"] as RequestModel;
var action = context.ActionDescriptor.DisplayName;
string log = $"{action)}(id: {id}, request: {request?.ToJson()})";
_logger.LogInformation(log);
base.OnActionExecuting(context);
}
}
Вам нужно будет использовать его как ТипФильтр в действии контроллера, чтобы его зависимости, т.е. ILogger, разрешались через DI.
[TypeFilter(typeof(LogParamsFilter))]
public asyc Task<IActionResult> Action1(int id, [FromBody] RequestModel request)
{
...
}
Либо можно прописать глобально в автозагрузке для всех контроллеров:
services.AddMvc(options => options
.Filters.Add(new TypeFilterAttribute(typeof(LogParamsFilter))));
Чтобы использовать его в качестве общего фильтра для всех действий контроллера, выполните итерацию по свойству context.ActionArguments.Keys и запишите значение для каждого ключа. Вам нужно будет выполнить некоторые проверки типов и вызвать .ToJson(), если тип ActionArgument — RequestModel.
Надеюсь, это поможет.
Связывание модели, вероятно, было бы плохой идеей, но вы можете сделать это с помощью атрибута.