Asp net core логирование ошибок в файл

Обновлено: 07.07.2024

Ниже представлен Startup.cs, созданный по шаблону

Итак, когда Startup.cs мы настраиваем все параметры, Startup.cs также имеет свойство с именем Configuration

Что я не могу понять, как получить доступ к этой конфигурации в контроллере или где-либо в приложении? MS рекомендует использовать шаблон параметров, но у меня всего 4-5 пар ключ-значение, поэтому я бы не хотел использовать шаблон параметров. Я просто хотел получить доступ к конфигурации в приложении. Как мне вводить его в любом классе?

Чтобы получить доступ к конфигурации в виде словаря из любого места, отметьте этот ответ .

Обновить

Это так же просто, как добавить IConfiguration экземпляр в коллекцию сервисов как одноэлементный объект ConfigureServices :

Где Configuration экземпляр в вашем Startup классе.

Это позволяет вам вводить IConfiguration в любой контроллер или службу:

Моллерна . а как насчет того, чтобы добавить конфигурацию в отдельный проект библиотеки классов в решении? Пробовал как эту частную статическую IConfiguration _configuration общедоступные DatabaseHelpers (конфигурация IConfiguration) но _configuration всегда имеет значение null . он никогда не попадает в конструктор Тем не менее, IConfiguration такое движение очень неплотно. Намного лучше использовать паттерн Параметры . Как получить доступ к значениям из "appsettings.json" непосредственно в настраиваемом классе? Без передачи данных от контроллера? Является ли это возможным? @HenkMollema Не могли бы вы добавить сюда пример? Как бы мне ввести это в любой класс (откуда?). @HenkMollema Вопрос заключался в том, как внедрить в любой класс . а не в том, как внедрить в «любой класс, разрешенный посредством внедрения зависимостей». Я думаю, что здесь недопонимание . его класс, вероятно, не вызывается из цепочки, которая начинается с контроллера или другого объекта, автоматически разрешаемого автоматическим процессом DI.

Правильный способ сделать это:

Теперь, когда вы хотите создать экземпляр своего класса, поскольку ваш класс вводит IConfiguration , вы не сможете просто сделать new MyClass() , потому что ему нужен IConfiguration параметр, введенный в конструктор, поэтому вам нужно будет ввести свой класс как хорошо для инъекционной цепочки, что означает два простых шага:

1) Добавьте свой Class / es - где вы хотите использовать IConfiguration , к методу IServiceCollection at ConfigureServices() в Startup.cs

2) Определите экземпляр - скажем, в Controller , и введите его с помощью конструктора:

Теперь у вас должна быть возможность _myClass.configuration свободно наслаждаться .

Если вы все еще ищете способ сделать его доступным без внедрения классов в контроллер, вы можете сохранить его в a static class , который вы настроите в Startup.cs , например:

И ваш Startup конструктор должен выглядеть так:

Затем используйте MyAppData.Configuration где угодно в вашей программе.

Не спорите со мной, почему первый вариант является правильным, я просто вижу, что опытные разработчики всегда избегают мусора на своем пути, и хорошо понимают, что постоянно держать в памяти множество данных - не лучшая практика, это плохо ни для производительности, ни для разработки, и, возможно, также более безопасно иметь с собой только то, что вам нужно.

Единый интерфейс для записи в лог позволяет приложениям работать с логом в едином стиле, независимо от того, какой провайдер используется.

Настройка инфраструктуры логгирования

Прежде чем начать писать что-то в логи, нужно настроить провайдеров логов. Для этого в метод Configure() класса Startup инжектируется сервис ILoggerFactory . Используя этот сервис можно настроить провайдеров:

Аналогичным образом добавляются и другие провайдеры. Microsoft предоставляет такие провайдеры как Microsoft.Extensions.Logging.Console , Microsoft.Extensions.Logging.Debug , Microsoft.Extensions.Logging.TraceSource , Microsoft.Extensions.Logging.AzureAppServices , Microsoft.Extensions.Logging.EventSource , Microsoft.Extensions.Logging.EventLog . Как нетрудно догадаться из названия, каждый из них имеет собственный источник для хранения логов.

Можно использовать несколько провайдеров одновременно:

Запись в лог

Для того, чтобы начать писать что-то в лог, следует инжектировать интерфейс ILogger в нужный компонент приложения. Причем в качестве generic-параметра мы можем указать категорию логгера. Например, получим доступ к логгеру в компоненте HomeController :

  • LogDebug()
  • LogTrace()
  • LogInformation()
  • LogWarning()
  • LogError()
  • LogCritical()

Попробуем записать что-нибудь в лог:

Теперь при срабатывании этого действия в консоли отладчика обязательно появится запись:

WebApplication8.Controllers.HomeController:Error: Some error occured

Если при настройке провайдеров мы настроили также вывод в консоль ( AddConsole() ), то эта же запись появится и в консоли (если мы запускаем приложение через Kestrel и консоль нам видна).

Логгирование в ASP.NET Core

Записывать в лог можно не только обычные строки, но и структурированные данные. Для этого существуют специальные перегрузки методов LogXXX() . Запишем, например, в лог данные с идентификатором объекта:

В логе появится строка следующего содержания:

WebApplication8.Controllers.HomeController:Error: Unable to delete object with 25 key

Если провайдер поддерживает структурированные данные, то ID будет доступно как отдельное поле. Более того, поле может быть не только простым типом, но и составным объектом. Но это — отдельная тема и здесь касаться этого не будем.

Scope

Например, группировку поддерживает Microsoft.Extensions.Logging.Console . Для её использования это нужно указать явно при настройке провайдера:

Логгирование в ASP.NET Core

Фильтрация логов

Все записи в логах разделяются по степени важности. Например, методы LogDebug() и LogInformation() записывают событие с какими-то отладочными данными. Их смысл в том, чтобы дать представление о процессах, которые происходят в приложении, но не более. С другой стороны LogWarning() , говорит о том, что в приложении произошло что-то, на что было бы неплохо обратить внимание. Наконец, LogError() или LogCritical() записывает информацию об ошибках — т.е. приложение повело себя не так, как планировалось, и на такие записи обязательно нужно обращать внимание.

Это поведение задается при помощи специального параметра:

В этом случае всё, что менее критично, чем Error или Warning будет игнорироваться для этих провайдеров.

Вот как например можно настроить провайдер для записи в консоль:

Другой способ — использовать конфигурационный файл для настройки уровней логгирования:

Конфигурационный файл в этом случае может выглядеть так:

Ещё один удобный способ настроить фильтры — это использовать пакет Microsoft.Extensions.Logging.Filter . После установки этого пакета для ILoggerFactory , появится метод-расширение WithFilter() , позволяющий настроить фильтры:

Такой способ удобен, когда нужно настроить фильтр одновременно для нескольких провайдеров. Также он позволяет задавать фильтры единым способом, даже когда провайдер не имеет параметров для настройки фильтров.

Логирование проекта с помощью NLog Framework

1546

Введение

Многие начинающие разработчики при создании своих проектов не задумываются о такой вещи, как создание журнала события. Мол, проект у меня нормальный, и так сойдет. Но не забываем, что наше приложение мы пишем не для себя самих, а для клиента.

Всем нужна статистика и слежение за проектами. Итак, что же насчет логирования, так это процесс записи всех сведений о проекте, а именно: информации о работе тех или иных элементах приложения, предупреждение о критической нагрузке, всяческие ошибки и т.д. Для . NET приложений был разработан очень удобный фреймворк под название NLog , с его помощью можно вести учет о состоянии всего приложения. Есть поддержка записи в файл, в базу данных.

Первый способ самый простой, так как зондирование проекта уже встроено в саму библиотеку NLog. Вся работа основа на объекте Logger – парне, который занимается ведением учета состояния нашего проекта.

Для того чтобы продемонстрировать работу NLog, создадим проект по шаблону консольного приложения и назовем его NLogUnderstanding . Изначально наш проект выглядит следующим образом:

static void Main( string [] args)

Чтобы начать работу данного фреймворка в нашем проекте, нужно установить следующие библиотеки через Package Manager Console (или же через сам менеджер расширений):

  1. Настройка через конфигурационный файл:

Первое, что нужно сделать- это установить данный пакет:

Установка пакета

После это у нас в проекте появится указанный файлик NLog . config :

Тема связана со специальностями:

Файлы проекта

Начальное содержимое файла выглядеть будет примерно так:

  • Информация о состоянии элементов;
  • Информация, запущенная в режиме debug для отладки проекта (можно применять в тестах);
  • Всяческие предупреждения (например, связанные с нагрузкой);
  • Информация об исключениях;
  • Информация об ошибках, которые привели к критическому завершению приложения.
  • name – название файла, нам оно понадобиться для организации правил, по которым мы будем писать именно в этот файл;
  • fileName – указываем файл и путь к файлу, в который будем писать наши логи;
  • layout – шаблон, по которому будет заполнятся наш файл.

Видео курсы по схожей тематике:

UX/UI Design Стартовый

UX/UI Design Стартовый

UX/UI Design мобильных приложений

UX/UI Design мобильных приложений

Создаем игру типа “Pokémon Go“

  • Создать через первый фабричный метод LogManager . GetLogger ( " Example " ) , в аргументах указываем название логгера, менее эффективный способ, т.к. всегда нужно указывать название класса, в котором происходит запись в журнал;
  • Создание через второй фабричный метод LogManager . GetCurrentClassLogger () , пользуясь данным методом, мы предоставляем возможность экземпляру логгера самому узнать полное квалификационное название класса, в котором произошла запись в журнал.

Теперь привнесем изменения в наш созданный проект:

После компиляции проекта у нас создается файл с текущей датой и в него внесутся следующий записи:

Бесплатные вебинары по схожей тематике:

Веб-дизайн с Figma. Легкий старт

Веб-дизайн с Figma. Легкий старт

SEO – перезагрузка

Введение в Kotlin

Введение в Kotlin

Теперь можно приступать к внедрению NLog в Ваш проект, и отслеживать состояние ваших объектов.


Под разбивкой по страницам понимается получение частичных результатов от API. Представьте, что в базе данных есть миллионы результатов и ваше приложение пытается вернуть их все сразу.

Это не только было бы крайне неэффективным способом возврата результатов, но также могло бы иметь разрушительные последствия для самого приложения или оборудования, на котором оно работает. Более того, каждый клиент имеет ограниченные ресурсы памяти, и ему необходимо ограничить количество отображаемых результатов.

Таким образом, нам нужен способ вернуть клиенту заданное количество результатов, чтобы избежать этих последствий.

Посмотрим, как мы можем это сделать.

Начальная реализация

Прежде чем вносить какие-либо изменения в исходный код, давайте посмотрим, как он выглядит прямо сейчас и как вы, вероятно, начнете с любым проектом.

В нашем случае у нас есть OwnerController , который выполняет все необходимые действия с объектом Owner .

Особое внимание следует обратить на действие GetOwners() , которое необходимо изменить:

Который вызывает GetOwners() с OwnerRepository :

Метод FindAll() - это просто метод из класса базового репозитория, который возвращает весь набор владельцев.

Как видите, это простое действие, предназначенное для возврата всех владельцев из базы данных, упорядоченных по именам.

Но в нашем случае это всего несколько владельцев аккаунтов (пять). Что, если бы в базе данных были тысячи или даже миллионы людей (вы хотите, но все же представьте себе другой вид сущности). Конец, затем добавьте к этому несколько тысяч потребителей API.

В итоге мы получим очень длинный запрос, который возвращает МНОГО данных.

В лучшем случае вы начинаете с небольшого числа владельцев, которое со временем медленно увеличивалось, чтобы вы могли заметить медленное снижение производительности. Другие сценарии гораздо менее благоприятны для вашего приложения и машин (представьте, что они размещены в облаке и не имеют надлежащего кэширования).

Имея это в виду, давайте изменим этот метод для поддержки разбиения на страницы.

Реализация пагинации

Имейте в виду, мы не хотим изменять логику базового репозитория или реализовывать какую-либо бизнес-логику в контроллере.

Начнем с изменения контроллера:

Здесь следует отметить несколько моментов:

  • Мы вызываем метод GetOwners из OwnerRepository , которого еще не существует, но мы скоро его реализуем.
  • Мы используем [FromQuery] , чтобы указать, что мы будем использовать параметры запроса, чтобы определить, какую страницу и сколько владельцев мы запрашиваем.
  • Класс OwnerParameters - это контейнер для фактических параметров.

Нам также необходимо создать класс OwnerParameters , поскольку мы передаем его в качестве аргумента нашему контроллеру. Создадим его в папке Models проекта Entities:

Мы используем константу maxPageSize , чтобы ограничить наш API до 50 владельцев. У нас есть два общедоступных свойства - PageNumber и PageSize . Если вызывающая сторона не устанавливает, PageNumber будет иметь значение 1, а PageSize - 10.

Теперь давайте реализуем самую важную часть - логику репозитория.

Нам нужно расширить метод GetOwners() в интерфейсе IOwnerRepository и в классе OwnerRepository :

Хорошо, проще всего объяснить это на примере.

Допустим, нам нужно получить результаты для третьей страницы нашего веб-сайта, считая 20 как количество результатов, которые мы хотим. Это означало бы, что мы хотим пропустить первые ((3 - 1) * 20) = 40 результатов, а затем взять следующие 20 и вернуть их вызывающему.

Есть ли в этом смысл?

Тестирование решения

Теперь в нашей базе данных всего несколько владельцев, поэтому давайте попробуем что-то вроде этого:

Это должно вернуть следующее подмножество владельцев:

Если это то, что у вас есть, вы на правильном пути.

Что мы можем сделать, чтобы улучшить это решение?

Улучшаем нашу пагинацию

Поскольку мы возвращаем вызывающему только подмножество результатов, мы могли бы получить PagedList вместо List .

PagedList унаследует от класса List и добавит к нему еще несколько. Мы также можем переместить логику пропуска/приема в PagedList, поскольку это имеет больше смысла.

Давайте реализуем это.

Реализация класса PagedList

Мы не хотим, чтобы наша логика пропуска/приема была реализована внутри нашего репозитория. Создадим для него класс:

Как видите, мы перенесли логику пропуска/приема в статический метод внутри класса PagedList . Мы добавили еще несколько свойств, которые пригодятся в качестве метаданных для нашего ответа.

HasPrevious истинно, если CurrentPage больше 1, и HasNext вычисляется, если CurrentPage меньше общего количества страниц. TotalPages также вычисляется путем деления количества элементов на размер страницы и последующего округления до большего числа, поскольку страница должна существовать, даже если на ней есть один элемент.

Теперь, когда мы это прояснили, давайте изменим OwnerRepository и OwnerController соответственно.

Во-первых, нам нужно изменить репо (не забудьте также изменить интерфейс):

А затем контроллер:

Но теперь у нас есть дополнительная полезная информация в заголовке ответа X-Pagination :


Как видите, все наши метаданные здесь. Мы можем использовать эту информацию при построении любого вида функциональности пагинации в интерфейсе. Вы можете поиграть с разными запросами, чтобы увидеть, как это работает в других сценариях.

Есть еще одна вещь, которую мы можем сделать, чтобы сделать наше решение еще более универсальным. У нас есть класс OwnerParameters , но что, если мы захотим использовать его в нашем AccountController ? Параметры, которые мы отправляем контроллеру учетной записи, могут отличаться. Возможно, не для разбиения по страницам, но мы отправим кучу разных параметров позже, и нам нужно разделить классы параметров.

Посмотрим, как это улучшить.

Создание родительского класса для параметров

Сначала создадим абстрактный класс QueryStringParameters . Мы будем использовать этот класс для реализации совместно используемых функций для каждого класса параметров, который мы будем реализовывать. А поскольку у нас есть OwnerController и AccountController , это означает, что нам нужно создать классы OwnerParameters и AccountParameters .

Начнем с определения класса QueryStringParameters в папке Models проекта Entities:

Мы также переместили нашу логику разбиения на страницы внутри класса, поскольку она будет действительна для любой сущности, которую мы, возможно, захотим вернуть через репозиторий.

Теперь нам нужно создать класс AccountParameters , а затем унаследовать класс QueryStringParameters от классов OwnerParameters и AccountParameters.

Удалите логику из OwnerParameters и унаследуйте QueryStringParameters :

И создайте класс AccountParameters внутри папки Models:

Теперь мы можем сделать что-то подобное внутри нашего AccountController :

И за счет наследования параметров загрузки через класс QueryStringParameters мы получаем такое же поведение.

Заключение

Пагинация - полезная и важная концепция при создании любого API. Без него наше приложение, вероятно, значительно замедлилось бы.

Реализованное нами решение далеко не идеально, но вы поняли суть. Мы изолировали различные части механизма загрузки и можем пойти еще дальше и сделать его более общим. Но вы можете сделать это как в статье и реализовать в своем собственном проекте.

Читайте также: