Что такое код asp readlogrecord

Содержание

Типы возвращаемых значений действий контроллера в веб-API ASP.NET Core Controller action return types in ASP.NET Core web API

ASP.NET Core предоставляет следующие параметры для типов возвращаемых значений действий контроллера веб-API: ASP.NET Core offers the following options for web API controller action return types:

В этом документе объясняется, когда лучше использовать каждый тип возвращаемого значения. This document explains when it’s most appropriate to use each return type.

Определенный тип Specific type

Простейшее действие возвращает элементарный или сложный тип данных (например, string или пользовательский тип объекта). The simplest action returns a primitive or complex data type (for example, string or a custom object type). Рассмотрим следующее действие, которое возвращает коллекцию пользовательских объектов Product : Consider the following action, which returns a collection of custom Product objects:

Если не известны условия, которые необходимо соблюдать при выполнении действия, конкретного типа будет достаточно. Without known conditions to safeguard against during action execution, returning a specific type could suffice. Предыдущее действие не принимает параметры, поэтому проверка ограничений параметров не требуется. The preceding action accepts no parameters, so parameter constraints validation isn’t needed.

Если в действии необходимо учитывать известные условия, используется несколько путей возврата. When known conditions need to be accounted for in an action, multiple return paths are introduced. В этом случае рекомендуется комбинировать тип возвращаемого значения класса ActionResult с примитивным или сложным типом возвращаемого значения. In such a case, it’s common to mix an ActionResult return type with the primitive or complex return type. Для этого типа действия требуется IActionResult или ActionResult . Either IActionResult or ActionResult are necessary to accommodate this type of action.

Получение IEnumerable или IAsyncEnumerable Return IEnumerable or IAsyncEnumerable

В ASP.NET Core 2.2 и более ранних версиях получение интерфейса IAsyncEnumerable из действия приводит к тому, что сериализатор выполняет синхронную итерацию операции сбора. In ASP.NET Core 2.2 and earlier, returning IAsyncEnumerable from an action results in synchronous collection iteration by the serializer. В результате вызовы блокируются, что может стать причиной перегрузки пула потоков. The result is the blocking of calls and a potential for thread pool starvation. Представьте, что Entity Framework (EF) Core используется веб-API для доступа к данным. To illustrate, imagine that Entity Framework (EF) Core is being used for the web API’s data access needs. Во время сериализации выполняется синхронное перечисление для типа возвращаемого значения следующего действия: The following action’s return type is synchronously enumerated during serialization:

Чтобы не допустить синхронного перечисления и блокировки операций ожидания для базы данных в ASP.NET Core 2.2 и более ранних версий, вызовите ToListAsync : To avoid synchronous enumeration and blocking waits on the database in ASP.NET Core 2.2 and earlier, invoke ToListAsync :

В ASP.NET Core 3.0 и более поздних версиях получение IAsyncEnumerable из действия: In ASP.NET Core 3.0 and later, returning IAsyncEnumerable from an action:

  • больше не приводит к синхронной итерации; No longer results in synchronous iteration.
  • по эффективности не отличается от получения IEnumerable . Becomes as efficient as returning IEnumerable .

ASP.NET Core 3.0 и более поздних версий помещает результаты следующего действия в буфер перед предоставлением его сериализатору: ASP.NET Core 3.0 and later buffers the result of the following action before providing it to the serializer:

Мы рекомендуем объявлять тип возвращаемого значения для сигнатуры действия как IAsyncEnumerable для гарантированного выполнения асинхронной итерации. Consider declaring the action signature’s return type as IAsyncEnumerable to guarantee the asynchronous iteration. То есть режим итерации зависит от возвращаемого базового конкретного типа. Ultimately, the iteration mode is based on the underlying concrete type being returned. MVC автоматически буферизует все конкретные типы, которые реализуют IAsyncEnumerable . MVC automatically buffers any concrete type that implements IAsyncEnumerable .

Рассмотрим следующее действие, которое возвращает записи о продуктах со сниженной ценой как IEnumerable

: Consider the following action, which returns sale-priced product records as IEnumerable

для предшествующего действия является: The IAsyncEnumerable

equivalent of the preceding action is:

Начиная с версии ASP.NET Core 3.0, оба предшествующих действия не являются блокирующими. Both of the preceding actions are non-blocking as of ASP.NET Core 3.0.

Тип IActionResult IActionResult type

Тип возвращаемого значения IActionResult можно использовать, если в действии допускаются несколько типов возвращаемого значения ActionResult . The IActionResult return type is appropriate when multiple ActionResult return types are possible in an action. Типы ActionResult представляют различные коды состояния HTTP. The ActionResult types represent various HTTP status codes. Любой неабстрактный класс, унаследованный от квалификаторов ActionResult в виде допустимого типа возвращаемого значения. Any non-abstract class deriving from ActionResult qualifies as a valid return type. Некоторые распространенные типы возвращаемых значений в этой категории: BadRequestResult (400), NotFoundResult (404) и OkObjectResult (200). Some common return types in this category are BadRequestResult (400), NotFoundResult (404), and OkObjectResult (200). Кроме того, удобные методы в классе ControllerBase можно использовать для получения типов ActionResult из действия. Alternatively, convenience methods in the ControllerBase class can be used to return ActionResult types from an action. Например, return BadRequest(); — это сокращенная форма return new BadRequestResult(); . For example, return BadRequest(); is a shorthand form of return new BadRequestResult(); .

Так как в типе действия есть несколько типов возвращаемого значения и путей, необходимо использовать атрибут [ProducesResponseType]. Because there are multiple return types and paths in this type of action, liberal use of the [ProducesResponseType] attribute is necessary. Этот атрибут создает описательные сведения об ответе для страниц справки по веб-API, создаваемых с помощью таких инструментов, как Swagger. This attribute produces more descriptive response details for web API help pages generated by tools like Swagger. [ProducesResponseType] указывает известные типы и коды состояния HTTP, возвращаемые действием. [ProducesResponseType] indicates the known types and HTTP status codes to be returned by the action.

Синхронное действие Synchronous action

Рассмотрим следующее синхронное действие, в котором возможны два типа возвращаемых значений: Consider the following synchronous action in which there are two possible return types:

В предшествующем действии: In the preceding action:

  • возвращается код состояния 404, если продукт, представленный id , не существует в базовом хранилище данных; A 404 status code is returned when the product represented by id doesn’t exist in the underlying data store. вызывается удобный метод NotFound в качестве сокращения return new NotFoundResult(); . The NotFound convenience method is invoked as shorthand for return new NotFoundResult(); .
  • Код состояния 200 возвращается с объектом Product , если продукт не существует. A 200 status code is returned with the Product object when the product does exist. Удобный метод Ok вызывается как сокращение для return new OkObjectResult(product); . The Ok convenience method is invoked as shorthand for return new OkObjectResult(product); .

Асинхронное действие Asynchronous action

Рассмотрим следующее асинхронное действие, в котором возможны два типа возвращаемых значений: Consider the following asynchronous action in which there are two possible return types:

В предшествующем действии: In the preceding action:

  • Код состояния 400 возвращается, если описание продукта содержит строку XYZ Widget. A 400 status code is returned when the product description contains «XYZ Widget». Удобный метод BadRequest вызывается как сокращение для return new BadRequestResult(); . The BadRequest convenience method is invoked as shorthand for return new BadRequestResult(); .
  • Код состояния 201 генерируется удобным методом CreatedAtAction при создании продукта. A 201 status code is generated by the CreatedAtAction convenience method when a product is created. В качестве альтернативы вызову CreatedAtAction можно использовать return new CreatedAtActionResult(nameof(GetBy >. An alternative to calling CreatedAtAction is return new CreatedAtActionResult(nameof(GetBy >. В этом пути к коду объект Product предоставляется в тексте ответа. In this code path, the Product object is provided in the response body. Также предоставляется заголовок ответа Location с URL-адресом только что созданного продукта. A Location response header containing the newly created product’s URL is provided.

Например, следующая модель указывает на то, что запросы должны включать свойства Name и Description . For example, the following model indicates that requests must include the Name and Description properties. Если Name и Description не были указаны в запросе, происходит сбой проверки модели. Failure to provide Name and Description in the request causes model validation to fail.

Если атрибут [ApiController] применяется в ASP.NET Core 2.1 и более поздних версиях, ошибки при проверке модели приводят к коду состояния 400. If the [ApiController] attribute in ASP.NET Core 2.1 or later is applied, model validation errors result in a 400 status code. Дополнительные сведения см. в разделе Автоматические отклики HTTP 400. For more information, see Automatic HTTP 400 responses.

Тип ActionResult ActionResult type

ASP.NET Core 2.1 предоставляет тип возвращаемого значения ActionResult для действий контроллера веб-API. ASP.NET Core 2.1 introduced the ActionResult return type for web API controller actions. Он позволяет возвращать тип, производный от ActionResult или определенный тип. It enables you to return a type deriving from ActionResult or return a specific type. ActionResult имеет следующие преимущества по сравнению с типом IActionResult: ActionResult offers the following benefits over the IActionResult type:

  • Свойство Type атрибута [ProducesResponseType] можно исключить. The [ProducesResponseType] attribute’s Type property can be excluded. Например, [ProducesResponseType(200, Type = typeof(Product))] упрощается до [ProducesResponseType(200)] . For example, [ProducesResponseType(200, Type = typeof(Product))] is simplified to [ProducesResponseType(200)] . Ожидаемый тип возвращаемого значения действия вместо этого выводится из T в ActionResult . The action’s expected return type is instead inferred from the T in ActionResult .
  • Операторы неявного приведения поддерживают преобразование T и ActionResult в ActionResult . Implicit cast operators support the conversion of both T and ActionResult to ActionResult . T преобразуется в ObjectResult, то есть return new ObjectResult(T); упрощается до return T; . T converts to ObjectResult, which means return new ObjectResult(T); is simplified to return T; .

C# не поддерживает операторы неявных приведений в интерфейсах. C# doesn’t support implicit cast operators on interfaces. Следовательно, для преобразования в конкретный тип необходимо использовать ActionResult . Consequently, conversion of the interface to a concrete type is necessary to use ActionResult . Например, использование IEnumerable не работает в следующем примере: For example, use of IEnumerable in the following example doesn’t work:

Один из способов исправить приведенный выше код — возвратить _repository.GetProducts().ToList(); . One option to fix the preceding code is to return _repository.GetProducts().ToList(); .

Большинство действий имеют тип возвращаемого значения. Most actions have a specific return type. При выполнении действия могут возникнуть непредвиденные условия, и в этом случае определенный тип не возвращается. Unexpected conditions can occur during action execution, in which case the specific type isn’t returned. Например, входной параметр действия может не пройти проверку модели. For example, an action’s input parameter may fail model validation. В этом случае обычно возвращается подходящий тип ActionResult вместо конкретного типа. In such a case, it’s common to return the appropriate ActionResult type instead of the specific type.

Синхронное действие Synchronous action

Рассмотрим синхронное действие, в котором возможны два типа возвращаемых значений: Consider a synchronous action in which there are two possible return types:

В предшествующем действии: In the preceding action:

  • возвращается код состояния 404, если продукт не существует в базе данных; A 404 status code is returned when the product doesn’t exist in the database.
  • возвращается код состояния 200 с соответствующим объектом Product , если продукт существует. A 200 status code is returned with the corresponding Product object when the product does exist. До версии ASP.NET Core 2.1 строка return product; имела бы вид return Ok(product); . Before ASP.NET Core 2.1, the return product; line had to be return Ok(product); .

Асинхронное действие Asynchronous action

Рассмотрим асинхронное действие, в котором возможны два типа возвращаемых значений: Consider an asynchronous action in which there are two possible return types:

В предшествующем действии: In the preceding action:

Логгирование

Ведение лога и ILogger

ASP.NET Core имеет встроенную поддержку логгирования, что позволяет применять логгирование с минимальными вкраплениями кода в функционал приложения.

Для логгирования данных нам необходим объект ILogger . По умолчанию среда ASP NET Core через механизм внедрения зависимостей уже предоставляет нам такой объект. Например, возьмем стандартный проект по типу Empty и добавим механизм логгирования. Для этого перейдем к классу Startup и изменим его метод Configure() :

Средой выполнения в метод Configure передается объект ILogger, который представляет логгер. А метод логгера logger.LogInformation передает на консоль некоторую информацию. По умолчанию информация логгируется на консоль, поэтому для тестирования логгера нам надо запустить приложение как консольное:

При обращении к приложению с помощью следующего запроса http://localhost:xxxxx/index на консоль будет выведена информация, переданная логгером:

Важно, что, если мы используем класс Startup для логгирования, то получить объект логгера мы можем только в методе Configure() , но никак не в методе ConfigureServices() или конструкторе класса Startup, поскольку инфраструктура логгирования зависит от конфигурации и контейнера DI, который окончательно устанавливаются лишь после завершения работы метода ConfigureServices.

Категория логгера

В примере выше в метод передается не просто объект ILogger, а объект ILogger LogLevel . Всего мы можем использовать следующие значения:

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

Debug : для вывода информации, которая может быть полезной в процессе разработки и отладки приложения

Information : уровень сообщений, позволяющий просто отследить поток выполнения приложения

Warning : используется для вывода сообщений о неожиданных событиях, например, ошибках, которые не влияют не останавливают выполнение приложения, но в то же время должны быть иследованы

Error : информация об ошибках, вследствие которых приложение должно быть остановлено

Critical : уровень критических ошибок, которые могут быть связаны с какими-то ситуациями извне — ошибками операционной системы, потерей данных в бд, переполнение памяти диска и т.д.

None : вывод информации в лог не применяется

Для вывода соответствующего уровня информации у объекта ILogger определены соответствующие методы расширения:

Так, в примере выше для вывода информации на консоль использовался метод LogInformation() .

Вывод сообщений уровня Trace по умолчанию отключен.

Каждый такой метод имеет несколько перегрузок, которые могут принимать ряд различных параметров:

string data : строковое сообщение для лога

int eventId : числовой идентификатор, который связан с логом. Идентификатор должен быть статическим и специфическим для определенной части логгируемых событий.

string format : строковое сообщения для лога, которое моет содержать параметры

object[] args : набор параметров для строкового сообщения

Exception error : логгируемый объект исключения

Также для логгирования определен общий метод Log() , который позволяет определить уровень логгера через один из параметров:

При стандартном логгировании на консоль для каждого уровня/метода определен своя метка и цветовой маркер, которые позволяют сразу выделить сообщение соответствующего уровня. Например, при запуске следующего кода:

Блог GunSmoker-а

. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.

26 июля 2009 г.

Как читать лог-файлы

Стек вызовов

Стек вызовов в EurekaLog (текстовый вид)

Стек вызовов в EurekaLog (вид в EurekaLog Viewer)

Стек вызовов в FastMM

Стек вызовов в JCL

Стек вызовов в Delphi (View/Debug Windows/Call Stack)

Итак, как вы можете видеть, в какой бы утилите не был создан стек вызовов – он почти всегда содержит эквивалентные сведения (ну, в примерах выше приведены разные программы, так что не пытайтесь сравнивать эти стеки вызовов ;) ).

Во-первых, обычно первым элементом в строке идёт сам адрес кода. Это числа вида 00488ABC или 488ABC (с обрезанными нулями). В стиле Delphi это будет выглядеть как $00488ABC – т.е. указатель на место в памяти, а именно: на код. Вы можете использовать эти адреса и вручную (например, если другая информация отсутствует): запустите программу, вызовите меню Search/Goto address (программа должна стоять на паузе) и введите $00488ABC – и вы попадёте ровно в это место (при условии, что адрес не изменился при перезапуске программы).

Почти всегда рядом с адресом указывается имя исполняемого модуля, которому он принадлежит. Например, Project8.exe или user32.dll . По этому признаку можно быстро отделить “свой код” от системного. Впрочем, сам вид адреса зачастую говорит об этом. К примеру, exe практически всегда загружается по адресу $400000 – следовательно, адреса вида $488ABC (близкие к $400000 ) почти наверняка принадлежат нашей программе. А вот системные DLL обычно загружаются по верхним адресам. Так что адреса вида $75B94911 скорее всего принадлежат не нам. Промежуточные адреса (типа $58C3C0 ) обычно принадлежат сторонним DLL (нашим или не нашим, но не системным). Окей, это весьма грубое и не совсем точное описание – но это всего лишь введение для новичков, так что не придирайтесь ;)

Иногда вместе с адресами указывается смещение относительно начала исполняемого модуля или сегмента кода – например 87A8C для адреса 00488A8C в логе JCL ( $00400000 + $1000 + $87A8C = $00488A8C ). Такая информация не слишком часто нужна, но, если вы понимаете в этом, то включайте (выше приведён полный вывод, а вообще он настраиваемый) – лишним не будет. Тут также может пригодиться дополнительная информация в лог-файле: список модулей с дескриптором каждого.

Но достаточно про эти сухие цифры – уж их-то вы будете использовать редко :) Потому что при наличии отладочной информации, ассоциированной с этим адресом, рядом будет указана более дружественная информация. А именно: имя модуля (unit), класса, метода или функции и номер строки. Иногда часть информации может отсутствовать – это зависит от детальности самой информации. Мы уже обсуждали это здесь и здесь.

Илон Маск рекомендует:  Что такое код asp defaultlogondomain

Собственно, эта информация говорит сама за себя: вот вам Delphi-вый модуль, вот вам метод и даже строка в нём, где возникла ошибка (для первой строки в стеке вызовов) или произошёл вызов подпрограммы (для всех остальных строк). Единственное, что требует комментария: номера строк. Часто вместе с абсолютными номерами строк (отсчёт от начала файла) указываются смещения в строках (и иногда даже в байтах) самой строки от начала процедуры, которой она принадлежит. Например, пусть у нас есть такой код:
Слева указывается обычная нумерация строк, как она есть в редакторе кода – абсолютная, от начала файла. Так вот, номера строк для DoActions2 может выглядеть как “ 48[2] ” (стиль EurekaLog) или “ Line 48, «Unit1.pas» + 1 ” (стиль JCL). Что следует читать как: это 48-я строка в файле или вторая строка в процедуре Test.

Зачем нужны эти относительные смещения? Ну, они чрезвычайно удобны при чтении стека вызовов, если ваши исходники поменялись. Например, вы добавили процедуру до процедуры Test. Сама процедура Test сместилась ниже на 27 строк. Понятно, что теперь строка 48 принадлежит совершенно другому коду. Однако, вы все ещё можете найти DoActions2 , если вы посмотрите на имя процедуры в стеке вызовов (“ Test ”) и отсчитаете 2 строки от её начала.

Кстати, EurekaLog также умеет смотреть в папку __history (доступна только в новых версиях Delphi), чтобы извлечь оттуда наиболее подходящую версию исходника для просмотра (это происходит когда вы дважды щёлкаете по строке в стеке вызовов в EurekaLog Viewer).

Чтение стека вызовов

Вот несколько моментов, которые надо иметь ввиду, когда вы читаете стек вызовов.
Во-первых, в одном файле баг-отчёта может быть несколько отчётов об ошибках. Во-вторых, даже в одном отчёте может быть перечислено несколько стеков вызовов: по одному на каждый поток. Так что, прежде чем приступать к анализу стека, – убедитесь, что вы собрались читать нужный ;)

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

Первые (снизу) несколько процедур в стеке вызовов обычно являются системными или принадлежат RTL Delphi и, как правило, редко представляют интерес. Часто информация о них отображается лишь частично – ввиду отсутствия отладочной информации. Также иногда стек вызовов имеет ограничение в глубину, т.е. не может содержать более N элементов. В этом случае нижняя часть и вовсе обрезается.

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

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

Далее, надо также иметь ввиду способ, которым строится стек. Их два: по фреймам вызовов (frame-based) и raw-метод. Первый метод строит стек, опираясь на последовательность фреймов, которые добавляются в стек при большинстве вызовов процедур. Обычно этот метод даёт хорошие результаты, если только у вас нет большого числа коротких процедур – для них стековые фреймы, как правило, не создаются (хотя вы и можете это исправить, включая опцию “Stack Frames”). Благодаря тому, что метод смотрит только на информацию о реальных вызовах – он довольно точен и быстр, т.к. не смотрит всю информацию в стеке, а просто проходит по цепочке фреймов, каждый из которых ссылается на предыдущий.

Raw-метод работает иначе: он просто сканирует весь стек, пытаясь найти в нём адреса возврата. Действительно, создаётся или нет фрейм вызова – это вопрос. Но вот адрес возврата-то кладётся в стек всегда. Вот их-то и пытается найти raw-метод. Для этого он берёт каждое число в стеке и пытается определить: похоже это на адрес возврата или нет? Поэтому raw-метод применяется только в сочетании с некоторой эвристикой. В зависимости от качества этой эвристики, построенные стеки вызовов могут значительно отличаться. К примеру, можно смотреть на то, указывает ли значение на сегмент кода, есть ли для него отладочная информация и т.п.

К чему я вас этим гружу? Да к тому, что чтение стека вызова будет зависеть от того, каким методом он построен. И вам лучше бы знать это до того, как вы приступите к анализу отчётов.
Какая есть разница? Ну, по описанию можно и самому сообразить: frame-based метод может пропускать вызовы, если для них не создаются фреймы (как правило, это очень короткие процедуры) и вовсе застопориться, если хотя бы один фрейм был повреждён. С другой стороны, очевидно, что raw-метод во многом “агрессивнее” frame-based метода: уж он почти наверняка не пропустит вызов (если только он не отсёкся эвристикой), но может добавлять в стек вызовов лишние элементы – так называемые “ложные срабатывания”.

Поэтому, анализируя стек и видя странные вещи (“ну не может по моему коду эта процедура вызываться отсюда!”) – помните о методе, которым построен стек и либо предполагайте отсутствие метода в стеке (для frame-based), либо предполагайте лишнюю (ложную) запись (для raw). Особенно нужно помнить об этой разнице, если вы используете какой-либо метод получения стека вызова в самой программе – например, хотите получить имя вызывавшей вас процедуры. Не всегда (хотя и чаще всего) в стеке вызовов вызывающий будет следовать второй строкой.

EurekaLog версии 6 использует только raw-метод (поддержка frame-based планируется для версии 7). FastMM и JCL поддерживают оба метода, переключение между которыми осуществляется в опциях (последние версии по-умолчанию используют frame-based метод).

Поиск места ошибки

В основном тут хочется сказать такую вещь: ошибка не обязательно сидит в том месте, куда указывает стек вызова! Эту простую истину почему-то не понимает огромное количество народа.

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

Просто анализируйте ситуацию, чтобы не свалить всё на ни в чём не повинный код.

Более того, некоторые типы отчётов возможно получить именно не в момент ошибки. Речь, конечно же, идёт не об исключениях (уж их-то мы ловим сразу), а о проблемах с памятью (утечки и порча памяти). Например, очевидно, что менеджер памяти не может проверять весь пул памяти на корректность при каждой операции в программе: “а уж не собирается ли вот эта инструкция кода сейчас затереть нужную память?” Более того, это невозможно даже для выполнения только во время обращения к менеджеру памяти (т.е. к GetMem / FreeMem ). Т.к. памяти выделяется много, управляющих структур ещё больше – и если сканировать их все при каждом обращении к менеджеру памяти, то производительность вашей программы упадёт до нуля.

Поэтому, менеджер памяти анализирует проблемы только по текущему контексту. Например, просим мы его освободить память: он проверит, уж не повреждён ли служебный заголовок у этого блока. Заметьте: только у этого, а не у всех. Далее, просим мы его выделить память: он пробегается по списку условно-свободных блоков и находит один. Прежде чем вернуть его вызывающему, менеджер памяти поверяет: а уж не писал ли кто чего в этот блок памяти, пока он был свободным?

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

Примеры? Ну, давайте посмотрим на FastMM (функциональность EurekaLog скромнее, поэтому с ней вы разберётесь по аналогии). Каждое сообщение FastMM начинается как “FastMM has detected an error during a ” – т.е. “FastMM обнаружил ошибку при ”. Далее указывается при какой же операции он нашёл ошибку: GetMem , FreeMem , ReallocMem или сканировании при поиске. Затем идёт собственно ошибка. Вот примеры ошибок:

  • “The block header has been corrupted”
  • “The block footer has been corrupted”
  • “FastMM detected that a block has been modified after being freed”
  • “An attempt has been made to free/reallocate an unallocated block”

Ну и другие, более редкие сообщения, типа вызова виртуального метода удалённого объекта. Смотрите: сообщения говорят о конкретных проблемах: FastMM обнаружил, что заголовок (header) или заглушка (footer) блока повреждены, запись в свободный блок или попытку повторного освобождения памяти. Ниоткуда не следует, что проблема произошла именно в этот момент! В текущий момент времени менеджер памяти просто обнаружил проблему. Сама проблема возникла ранее.

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

  1. Вы выделили блок (FastMM покажет этот стек вызовов).
  2. Вы освободили блок (FastMM покажет этот стек вызовов).
  3. Какой-то код испортил память, ошибочно записывая в этот свободный блок.
  4. Вы вызвали GetMem. В этот момент FastMM обнаруживает изменение в блоке и трубит тревогу (FastMM покажет этот стек вызовов).

В отчёте будет три (!) стека вызова для одной проблемы и ни один из них не будет представлять указание на проблему: проблема будет сидеть где-то между вторым и последним стеками вызовов.

Короче говоря: просто немного используйте свою голову (читайте сообщения, а не ломитесь проверять код по стекам вызовов) и всё будет отлично ;)

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

Другая информация в отчёте

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

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

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

Ну, например, странный AV на, казалось бы, ровном месте при вызове функции. Посмотрев на поле версии ОС, вы увидите, что программа была запущена в Windows 2000, в которой вы её не проверяли. А копнув глубже, вы обнаружите, что проблема была в том, что используемая вами функция не существует в Windows 2000 (вы динамически импортировали функцию, не проверяя на ошибки).

Корректный ASP.NET Core

Специально для любителей книг из серии «С++ за 24 часа» решил написать статью про ASP.NET Core.

Если вы раньше не разрабатывали под .NET или под какую-то аналогичную платформу, то смысла заходить под кат для вас нет. А вот если вам интересно узнать что такое IoC, DI, DIP, Interseptors, Middleware, Filters (то есть все то, чем отличается Core от классического .NET), то вам определенно есть смысл нажать на «Читать дальше», так как заниматься разработкой без понимания всего этого явно не корректно.

IoC, DI, DIP

Если театр начинается с вешалки, то ASP.NET Core начинается с Dependency Injection. Для того, чтобы разобраться с DI нужно понять, что такое IoC.

Говоря о IoC очень часто вспоминают голливудский принцип «Don’t call us, we’ll call you». Что означает «Не нужно звонить нам мы позвоним вам сами».

Различные источники приводят различные паттерны, к которым может быть применен IoC. И скорее всего они все правы и просто дополняют друг друга. Вот некоторые их этих паттернов: factory, service locator, template method, observer, strategy.

Давайте разберем IoC на примере простого консольного приложения.

Допустим у нас есть два простых класса, реализующих интерфейс с одним методом:

Они оба зависят от абстракции (в данном случае в виде абстракции выступает интерфейс).

И, допустим, у нас есть объект более высокого уровня, использующий эти классы:

В зависимости от параметра конструктора переменная _instance инициализируется определенным классом. Ну и далее при вызове Write будет совершен вывод на консоль или в Debug. Все вроде бы неплохо и даже, казалось бы, соответствует первой части принципа Dependency Inversion

Объекты более высокого уровня не зависят от объектов более низкого уровня. И те, и те зависят от абстракций.

В качестве абстракции в нашем случае выступает ILayer.

Но у нас должен быть еще и объект еще более высокого уровня. Тот, который использует класс Logging

Инициализируя Logging с помощью 1 мы получаем в классе Logging экземпляр класса, выводящего данные на консоль. Если мы инициализируем Logging любым другим числом, то log.Write будет выводить данные в Debug. Все, казалось бы, работает, но работает плохо. Наш объект более высокого уровня Main зависит от деталей кода объекта более низкого уровня – класса Logging. Если мы в этом классе что-то изменим, то нам необходимо будет изменять и код класса Main. Чтобы это не происходило мы сделаем инверсию контроля – Inversion of Control. Сделаем так чтобы класс Main контролировал то, что происходит в классе Logging. Класс Logging будет получать в виде параметра конструктора экземпляр класса, реализующего интерфейс интерфейс ILayer

И теперь нас класс Main будет выглядеть таким образом:

Фактически мы декорируем наш объект Logging с помощью необходимого для нас объекта.

Теперь наше приложение соответствует и второй части принципа Dependency Inversion:

Абстракции не зависят от деталей. Детали зависят от абстракций. Т.е. мы не знаем деталей того, что происходит в классе Logging, мы просто передаем туда класс, реализующий необходимую абстракцию.

Есть такой термин tight coupling – тесная связь. Чем слабее связи между компонентами в приложении, тем лучше. Хотелось бы заметить, что данный пример простого приложения немного не дотягивает до идеала. Почему? Да потому что в классе самого высокого уровня в Main у нас дважды используется создание экземпляров класса с помощью new. А есть такая мнемоническая фраза «New is a clue» — что означает чем меньше вы используется new, тем меньше тесных связей компонентов в приложении и тем лучше. В идеале мы не должны были использовать new DebugLayer, а должны были получить DebugLayer каким-нибудь другим способом. Каким? Например, из IoC контейнера или с помощью рефлексии из параметра передаваемого Main.

Теперь мы разобрались с тем, что такое Inversion of Control (IoC) и что такое принцип Dependency Inversion (DIP). Осталось разобраться с тем, что такое Dependency Injection (DI). IoC представляет собой парадигму дизайна. Dependency Injection это паттерн. Это то, что у нас теперь происходит в конструкторе класса Logging. Мы получаем экземпляр определенной зависимости (dependency). Класс Logging зависит от экземпляра класса, реализующего ILayer. И это экземпляр внедряется (injected) через конструктор.

IoC container

IoC контейнер это такой объект, который содержит в себе множество каких-то определенных зависимостей (dependency). Зависимость можно иначе назвать сервисом – как правило это класс с определенным функционалом. При необходимости из контейнера можно получить зависимость необходимого типа. Внедрение dependency в контейнер — это Inject. Извлечение – Resolve. Приведу пример самого простого самостоятельно написанного IoC контейнера:

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

Зарегистрировать зависимость (допустим, ConsoleLayer или DebugLayer которые мы использовали в прошлом примере) можно так:

А извлечь из контейнера в необходимом месте программы так:

В реальных контейнерах еще реализуется и Dispose(), позволяющий уничтожать ставшие ненужными ресурсы.

Кстати, имя IoC контейнер не совсем точно передает смысл, так как термин IoC гораздо шире по применению. Поэтому в последнее время все чаще применяется термин DI контейнер (так как все-таки применяется dependency injection).

Service lifetimes + various extension methods in Composition Root

Приложения ASP.NET Core содержат файл Startup.cs который является отправной точкой приложения, позволяющей настроить DI. Настраивается DI в методе ConfigureServices.

Этот код добавит в DI контейнер класс SomeRepository, реализующий интерфейс ISomeRepository. То, что сервис добавлен в контейнер с помощью AddScoped означает, что экземпляр класса будет создаваться при каждом запросе страницы.
Добавить сервис в контейнер можно и без указания интерфейса.

Но такой способ не рекомендуется, так как у вашего приложения теряется гибкость и появляются тесные связи. Рекомендуется всегда указывать интерфейс, так как в таком случае в любой момент времени можно заменить одну реализацию интерфейса другой. И если реализации поддерживают принцип Liskov substitution, то «легким движением руки» сменив название класса реализации вы измените и функционал всего приложения.

Есть еще 2 варианта добавить сервис – AddSingleton и AddTransient.
При использовании AddSingleton сервис создается один раз и при использовании приложения обращение идет к одному и тому же экземпляру. Использовать этот способ нужно особенно осторожно, так как возможны утечки памяти и проблемы с многопоточностью.

У AddSingleton есть небольшая особенность. Он может быть инициализирован либо при первом обращении к нему

либо сразу же при добавлении в конструктор

Вторым способом можно даже добавить параметр в конструктор.
Если хочется добавить параметр в конструктор сервиса, добавленного не только с помощью AddSingleton, но и с помощью AddTransient/AddScoped, то можно использовать лямбда выражение:

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

Если с AddSingleton и AddScoped все должно быть более-менее понятно, то AddTransient требует разъяснений. Официальная документация приводит пример, в котором определенный сервис добавлен в DI контейнер и в качестве параметра конструктора другого сервиса и отдельно самостоятельно. И вот в случае, если он добавлен отдельно с помощью AddTransient, он создает свой экземпляр 2 раза. Приведу очень-очень упрощенный пример. В реальной жизни к применению не рекомендуется, т.к. классы для упрощения не наследуют интерфейсы. Допустим у нас есть простой класс:

Илон Маск рекомендует:  Что такое код unix

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

Теперь совершаем inject двух сервисов:

И в каком-нибудь контроллере в Action добавим получение наших зависимостей и вывод значений в окно Debug.

Так вот в результате мы получим 2 разных значения Guid. А вот если мы заменим AddTransient на AddScoped, то в результате мы получим 2 одинаковых значения.

В IoC контейнере приложений ASP.NET Core по умолчанию содержатся уже некоторые сервисы. Например, IConfiguration – сервис с помощью которого можно получить настройки приложения из файлов appsettings.json и appsettings.Development.json. IHostingEnvironment и ILoggerFactory с помощью которых можно получить текущую конфигурацию и вспомогательный класс, позволяющий проводить логирование.

Извлекают классы из контейнера с помощью следующей типичной конструкции (самый банальный пример):

В области видимости контроллера создается переменная с модификаторами доступа private readonly. Зависимость получается из контейнера в конструкторе класса и присваивается приватной переменной. Далее эту переменную можно использовать в любых методах или Action контроллера.
Иногда не хочется создавать переменную для того, чтобы использовать ее только в одном Action. Тогда можно использовать атрибут [FromServices]. Пример:

Выглядит странно, но для того, чтобы в коде не вызывать метод статического класса DateTime.Now() иногда делают так, что значение времени получается из сервиса в качестве параметра. Таким образом появляется возможность передать любое время в качестве параметра, а значит становится легче писать тесты и, как правило, становится проще вносить изменения в приложение.
Нельзя сказать, что static – это зло. Статические методы выполняются быстрее. И скорее всего static может использоваться где-то в самом IoC контейнере. Но если мы избавим наше приложение от всего статического и new, то получим большую гибкость.

Сторонние DI контейнеры

То, что мы рассматривали и то, что фактически реализует ASP.NET Core DI контейнер по умолчанию, — constructor injection. Имеется еще возможность внедрить зависимость в property с помощью так называемого property injection, но эта возможность отсутствует у встроенного в ASP.NET Core контейнера. Например, у нас может быть какой-то класс, который вы внедряем как зависимость, и у этого класса есть какое-то public property. Теперь представьте себе, что во время или после того как мы внедряем зависимость, нам нужно задать значение property. Вернемся к примеру похожему на пример, который мы недавно рассматривали.
Если у нас есть такой вот класс:

который мы можем внедрить как зависимость,

то используя стандартный контейнер задать значение для свойства мы не можем.
Если вы захотите использовать такую возможность задать значение для свойства OperationId, то вы можете использовать какой-то сторонний DI контейнер, поддерживающий property injection. К слову сказать property injection не особо рекомендуется использовать. Однако, существуют еще Method Injection и Setter Method Injection, которые вполне могут вам пригодится и которые также не поддерживаются стандартным контейнером.

У сторонних контейнеров могут быть и другие очень полезные возможности. Например, с помощью стороннего контейнера можно внедрять зависимость только в контролеры, у которых в названии присутствует определенное слово. И довольно часто используемый кейс – DI контейнеры, оптимизированные на быстродействие.
Вот список некоторых сторонних DI контейнеров, поддерживаемых ASP.NET Core: Autofac, Castle Windsor, LightInject, DryIoC, StructureMap, Unity

Хоть при использовании стандартного DI контейнера и нельзя использовать property/method injection, но зато можно внедрить зависимый сервис в качестве параметра конструктора реализовав паттерн «Фабрика» следующим образом:

В данном случае GetService вернет null если зависимый сервис не найден. Есть вариация GetRequiredService, которая выбросит исключение в случае, если зависимый сервис не найден.
Процесс получения зависимого сервиса с помощью GetService фактически применяет паттерн Service locator.

Autofac

Разберем Autofac на практическом примере. Удобно то, что сервисы из контейнера можно будет регистрировать и получать, как и дефолтным способом, так и с помощью Autofac.

Установим NuGet пакет Autofac.Extensions.DependencyInjection.
Изменим возвращаемое методом ConfigureServices значение с void на IServiceProvider. И добавим property

После этого станет возможным добавить в конец метода ConfigureServices класса Startup код вроде следующего (это лишь один из вариантов регистрации сервисов):

Здесь builder.Populate(services); добавляет в контейнер сервисы из IServiceCollection. Ну и далее уже можно регистрировать сервисы с помощью builder.RegisterType. Ах, да. Чуть не забыл. Необходимо изменить с void на IServiceProvider возвращаемое значение метода ConfigureServices.

AOP с помощью ASP.NET Core — Autofac Interseptors

Говоря про аспектно-ориентированное программирование, упоминают другой термин – cross-cutting concerns. Concern – это какая-то часть информации, которая влияет на код. В русском варианте употребляют слово ответственность. Ну а cross-cutting concerns это ответственности, которые влияют на другие ответственности. А в идеале ведь они не должны влиять друг на друга, так ведь? Когда они влияют на друг друга, то становится сложнее изменять программу. Удобнее, когда у нас все операции происходят по отдельности. Логирование, транзакции, кеширование и многое другое можно совершать с помощью AOP не изменяя код самих классов и методов.

В мире .NET часто применяется способ, когда AOP код внедряется с помощью пост-процессора в уже откомпилированный код приложения (PostSharp) Или же альтернативно можно применять интерцепторы – это такие перехватчики событий, которые можно добавлять в код приложения. Эти перехватчики, как правило, используют для своей работы уже рассмотренный нами паттерн декоратор.

Давайте создадим свой интерцептор. Самый простой и типичный пример, который проще всего воспроизвести — это логирование.
Установим дополнительно к пакету Autofac.Extensions.DependencyInjection еще и пакет Autofac.Extras.DynamicProxy
Установили? Добавим простенький класс лога, который будет вызываться при обращении к определенным сервисам.

Добавляем в нашу регистрацию Autofac регистрацию интерцептора:

И теперь при каждом обращении к классу будет вызван метод Intercept класса Logger.
Таким образом мы можем упростить себе жизнь и не писать в начале каждого метода запись в лог. Она у нас будет вестись автоматически. И при желании нам будет несложно ее изменить или отключить для всего приложения.

Также мы можем убрать .InterceptedBy(typeof(Logger)); и добавить перехват вызовов только для конкретных сервисов приложения с помощью атрибута [Intercept(typeof(Logger))] – необходимо указать его перед заголовком класса.

Middleware

В ASP.NET существует определенная цепочка вызовов кода, которая происходит при каждом request. Еще до того, как загрузился UI/MVC выполняются определенные действия.

То есть, например, если мы добавим в начало метода Configure класса Startup.cs код

то мы сможем посмотреть в консоли дебага какие файлы запрашивает наше приложение. Фактически мы получаем возможности AOP “out of box”
Немного useless, но понятный и познавательный пример использования middleware я вам сейчас покажу:

При каждом запросе начинает выполнятся цепочка вызовов. Из каждого app.Use после вызова next.invoke() совершается переход ко следующему вызову. И все завершается после того как отработает app.Run.
Можно выполнять какой-то код только при обращении к определенному route.
Сделать это можно с помощью app.Map:

Теперь если просто перейти на страницу сайта, то можно будет увидеть текст “Hello!”, а если добавить к строке адреса /Goodbye, то вам будет отображено Goodbye.

Кроме Use и Map можно использовать UseWhen или MapWhen для того, чтобы добавлять код в цепочку middleware только при каких-то определенных условиях.

До сих пор были все еще useless примеры, правда? Вот вам нормальный пример:

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

Или же вот пример локализации:

Теперь если вы к адресу страницы добавите параметр ?culture=fr то вы сможете переключить язык приложения на французский (если в ваше приложение добавлена локализация, то все сработает)

Filters

Если цепочка middleware относятся к процессам до MVC, то фильтры работают вместе с MVC.
Следующее схематическое изображение показывает, как отрабатывают фильтры

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

Затем отрабатывают фильтры ресурсов. С помощью этих фильтров можно, например, вернуть какую-то информацию из кеша.

Затем происходит привязка данных и выполняются Action фильтры. С их помощью можно манипулировать параметрами передаваемыми Action и возвращаемым результатом.

Exception фильтры как намекает название позволяют добавить какую-то общую обработку ошибок для приложения. Должно быть довольно удобно обрабатывать ошибки везде одинаково. Эдакий AOP-шный плюс.

Result фильтры позволяют совершить какие-то действия до выполнения Action контроллера или после. Они довольно похожи на Action фильтры, но выполняются только в случае отсутствия ошибок. Подходят для логики завязанной на View.

Все должно быть более понятно на примере. А у меня под рукой как раз есть пример упрощенного авторизационного фильтра:

Добавляете этот класс в DI контейнер (как обычно в Startup.cs)

И теперь становится возможным добавить какую-то свою авторизацию любому Action добавив следующий атрибут

Забавная штука – можно создать свое middleware и добавлять его каким-то action в качестве фильтра. Для того чтобы сделать так нужно создать класс с произвольным названием и методом Configure

Теперь этот класс можно добавлять Action-ам с помощью следующего атрибута

Управление онлайновыми журналами Oracle Redo logs

Онлайновые журналы повторного выполнения (redo logs) — это средства Oracle, благодаря которым гарантируется, что все изменения, проведенные пользователями, будут зафиксированы в журналах на случай, если произойдет сбой между моментом проведения изменений и моментом записи их в постоянное хранилище. Таким образом, журналы повторного выполнения — основа процесса восстановления.

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

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

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

Сравнение аппаратного зеркального отображения и мультиплексирования Oracle

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

Если вы потеряете онлайновый журнал повторного выполнения, то можете утратить важные данные, поэтому при мультиплексированной системе журналов повторного выполнения фоновый процесс LGWR, который отвечает за запись данных из буфера журнала повторного выполнения, пишет одновременно во все (идентичные) файлы-члены мультиплексированной группы. Если возникнут проблемы записи в один из членов мультиплексированной группы журналов повторного выполнения, запись в другие члены группы пройдет беспрепятственно.

Группы онлайнового журнала повторного выполнения

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

Ниже описаны некоторые базовые условия для групп журнала повторного выполнения Oracle.

  • Все файлы журнала повторного выполнения в группе должны иметь одинаковый размер.
  • Хотя можно разместить оба члена группы онлайнового журнала повторного выполнения на одном физическом диске, разумнее разнести их по разным дискам, чтобы идентичные копии страховали друг друга на случай сбоя диска; в такой ситуации пострадает только один из членов группы. Oracle продолжит писать в уцелевшие члены группы онлайнового журнала повторного выполнения, когда один член окажется недоступным для записи (возможно, из-за проблем с дисковым приводом).

Создание групп онлайнового журнала повторного выполнения

Добавление групп журнала повторного выполнения

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

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

Следующий оператор, который использует синтаксис ADD LOGFILE GROUP, добавляет новую группу журнала повторного выполнения в базу данных. Обратите внимание, что эта группа дублирована; в ней создается два файла журнала повторного выполнения, а не один:

В примере из предыдущего раздела были созданы три группы онлайнового журнала повторного выполнения, но каждая из них включала только по одному члену. Чтобы продублировать эти группы с целью повышения безопасности, потребуется добавить по одному члену в каждую группу. Для добавления одного члена в существующую группу используется оператор ADD LOGFILE MEMBER:

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

Переименование файлов журнала повторного выполнения

Для переименования файла журнала повторного выполнения выполните следующие шаги.

  1. Остановите базу данных и запустите ее в смонтированном режиме:
  2. Переместите файлы в новое местоположение командой операционной системы:
  3. Воспользуйтесь командой ALTER DATABASE RENAME datafile TO для переименования этого файла внутри управляющего файла:

Удаление онлайновых журналов повторного выполнения

С помощью следующей команды можно уничтожить всю группу журнала повторного выполнения:

Для удаления единственного члена группы служит такая команда:

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

Повреждение онлайнового журнала повторного выполнения

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

Также не удастся удалить файл онлайнового журнала повторного выполнения, если этот файл является частью текущей группы. В этом случае стратегия должна состоять в повторной инициализации файла журнала с помощью следующей команды:

Если группы журналов еще не были архивированы, можно применить следующий оператор:

Мониторинг журналов повторного выполнения

Для мониторинга журналов повторного выполнения применяются два ключевых динамических представления — V$LOG и V$LOGFILE.

Представление V$LOGFILE содержит полные имена файлов журналов повторного выполнения, их состояние и тип, как показано ниже:

Представление V$LOG выдает детальную информацию о размерах и состоянии журналов повторного выполнения, а также показывает, были ли журналы архивированы:

Сообщества › ВАЗ: Ремонт и Доработка › Блог › Снятие и чтение логов ЭБУ.

Прежде чем говорить о снятии и чтении логов пара строк о самой диагностики.
1) ДИАГНОСТИКА И ЧТЕНИЕ -СБРОС ОШИБОК АБСОЛЮТНО РАЗНЫЕ
ВЕЩИ.
2) НИ КАКОЙ ЕЛМ СО СМАРТФОНОМ А ТАК ЖЕ БК НЕ СМОЖЕТ СДЕЛАТЬ НОРМ ДИАГНОСТИКУ. ОН НУЖЕН ЧТОБЫ В ДОРОГЕ ПОСМОТРЕТЬ ОШИБКИ В СЛУЧАЕ ПОЛОМКИ И ПОПЫТАТЬСЯ ИХ САМОСТОЯТЕЛЬНО УСТРАНИТЬ. НЕ БОЛЕЕ.(пример на последних скринах)
3) ДИАГНОСТИКА БЫВАЕТ В РЕЖИМЕ online и offline.
Остановимся на этом виде диагностики двигателя в режиме offline.
Данный вид диагностики необходим для анализа работы двигателя в спокойной обстановке а так же чтобы выявить быстрые и резкие отклонения в работе двигателя которые невозможно определить online. Ну и просто для удобства. Думаю согласитесь что есть разница — ехать на трассе одним глазом смотреть на дорогу, а вторым в ноут или же просматривать те же данные работы двигателя лежа на диване.
Чтобы снять лог работы двигателя нужно:
— Программа диагностики которая позволяет снимать логи
— Программа для анализа Log файлов
Запишем лог на примере программы диагностики OpenDiag (практически все программы диагностики пишут логи. см настройки.)
Для этого

ecuEdit
Одна из первых подобных программ. Огромный функционал.
Но для ее работы лог должен быть записан в формате Excel. Для этого используем конвертор Atomic converter который переводит наш снятый лог в другой формат. Программа работает только в EcuEdit версии 2.4.

DiagView
Хорошая программа для построения графиков

ReadLogs
Программа создана специально для OpenDiag скачать ее можно на их официальном сайте.
И тд.
Думаю статья кому нибудь окажется полезной.

Путь ASP.NET Core [уровень 1] Основы

ASP.NET Core — новейший фреймворк для кроссплатформенной веб разработки. Пока его популярность (как и количество вакансий) только начинает набирать обороты самое время узнать о нем побольше. Ну а для того, чтобы все знания не испарились сразу после прочтения — добавим существенную практическую часть. Создадим простое приложение, для тестирования прочитанного.

Если вы считаете, что уже достаточно круты в новом фреймворке — можете попробовать пройти тест до того, как прочтете статью. Линк. Весь код проекта можно посмотреть на гитхабе.

Первая часть включает:

  • Что такое .NET Core и ASP.NET Core?
  • Основы создания приложения и его структура
  • Добавление новых элементов, скаффолдинг
  • Основы встроенного Dependency Injection
  • Деплоймент в Azure

Разберемся в терминах. Один из наиболее не понятных моментов — это зависимость между старым фреймворком ASP.NET MVC и новым ASP.NET Core, а также в чем отличия .NET и .NET Core. Начнем с последнего. .NET Core — это общая платформа для разработки программного обеспечения. Фактически это еще одна реализация стандарта .NET (другие реализации — .NET, Mono). Отличия и особенности этой реализации (.NET Core) в том, что она:

  • С открытым исходным кодом
  • Кроссплатформенная
  • Гибкая в установке — может быть внутри приложения и можно поставить несколько версий на одной и той же машине
  • Все сценарии работы поддерживаются с помощью консольных инструментов

Перейдем к ASP.NET Core. Это новый фреймворк от Microsoft для разработки Веб приложений, который появился вследствие редизайна ранее существующего фреймворка ASP.NET MVC. Нужно понимать, что ASP.NET Core не обязательно должен базироваться на .NET Core. Можно создать ASP.NET Core приложение на основе старого доброго .NET. Такая опция есть в стандартном диалоге создания нового проекта:

Илон Маск рекомендует:  Что такое код pdf_curveto

В чем же тогда особенности и отличия ASP.NET Core от предыдущего ASP.NET? Некоторые из них это:

  • Возможность намного лучше контролировать нужные модули, сборки. Например, нет жесткой привязки к IIS, System.Web.dll
  • Встроенный функционал для внедрения зависимостей
  • Открытый исходный код

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

Класс Statup можно, в какой-то степени, охарактеризовать как новый вариант Global.asax (Это класс для глобальной настройки всего приложения в предыдущей версии ASP.NET). Грубо говоря, можно сказать, что метод ConfigureServices нужен для конфигурации контейнера для внедрения зависимостей и его сервисов, а метод Configure для конфигурации конвейера обработки запросов.

Приступим к практической реализации

Для того, чтобы лучше понять все новшества, создадим ASP.NET Core приложение на основе .NET Core.

Чтобы облегчить себе жизнь, выберем Web Application и поменяем аутентификацию на Individual User Accounts. Таким образом Visual Studio уже сгенерирует весь нужный код для базового приложения.

Рассмотрим детальней что же нового появилось в ASP.NET Core. С точки зрения разработки вся концепция осталась прежней. Структура проекта базируется на паттерне MVC. Для работы с данными по умолчанию используем Entity Framework, логика описана в классах-контроллерах, на уровне представлений используем синтаксис cshtml + новая фишка tag helpers.

Проверим классы Program.cs и Startup.cs, они действительно выглядят такими же, как было описано выше. Конечно класс Startup не совсем пуст, а уже вмещает функционал для считывания конфигурации, настройки базового логирования, маршрутизации и привязку на нашу модель базы данных.

Дополним модель базы данных сущностями для создания и прохождения тестов. Будем использовать следующие сущности: Набор тестовых вопросов — TestPackage, Сам вопрос (тест) — TestItem, Результат теста — TestResult. Пример можно посмотреть тут. Радует, что EntityFramework Core уже поддерживает большинство функционала и можно полноценно пользоваться Code First миграциями.

Добавляем логику

Теперь, когда у нас есть модель базы данных, мы можем приступить к созданию логики для нашего приложения. Самый простой способ создания админки — это механизм scaffolding. Для этого, кликаем правой кнопкой мыши по папке контроллеров и выбираем Add → New Scaffold Item:

Выбираем «MVC Controller с представлениями, с использованием Entity Framework». Этот шаблон позволяет нам быстро создать контроллер и вьюхи для управления одной конкретной моделью. Проделаем такой трюк для TestPackage и TestItem. В результате у нас есть готовый прототип админки для нашей системы. Можно запустить проект и зайти на страницы этих контроллеров, просто добавить его имя без слова Controller в конец адреса, например, /testpackages. Конечно в ней еще не все идеально, поэтому нужно допилить некоторые моменты и сделать их более удобными.

После создания модели и простого функционала для работы с ней, можно перейти к самому процессу прохождения тестов. В общем, все выглядит просто. Сервер присылает вопрос, варианты, мы отсылаем назад ответ. Сервер сохраняет его. В конце теста показываем результат. Добавим код для этого поведения.

В общем, все что нужно для теста у нас есть.

Основы Dependency Injection в ASP.NET Core

Важным новшеством новой версии ASP.NET так же является встроенный механизм внедрения зависимостей. В 2020 году уже никого не удивишь тем, что механизм внедрения зависимостей можно перенести внутрь фреймворка. Мало какое серьёзное приложение пишут без использование этого подхода. DI в ASP.NET Core реализован достаточно базово, но в то же время позволяет решить большинство задач управления зависимостями.

Конфигурация контейнера осуществляется в методе ConfigureServices класса Startup. Пример:

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

После регистрации сервисов, самый легкий способ получить из в коде (например, в контроллерах) — это просто добавить параметр конструктора типа, который был зарегистрирован. Такие контроллеры по умолчанию создаются скаффолдингом.

Деплой

Одним из самых простых способов деплоймента остается Microsoft Azure. Нам достаточно самых базовых настроек для полноценной работы. Развертывание сайта на сервере все так же просто — с помощью нескольких кликов, начиная с контекстного меню на файле проекта.

Выводы

Пока не известно будущее «классического» .NET фреймворка, так как он, все же, является более стабильным и проверенным, поэтому может существовать еще довольно долго (привет, Python 2), хотя возможна и ситуация быстрой миграции большинства девелоперов на Core версии (не такой уже .NET и старый — 14 лет всего лишь).

Структурное логирование на примере Serilog и Seq

Обычные лог-записи состоят из строк и для поиска записи среди массива текста нам приходится использовать регулярные выражение

Структурное логирование хранит записи в виде объектов (структур). Например, с помощью JSON

Будет сохранено две записи:

  1. Шаблон для конечного вывода строки «Запись в лог файл. Пост на хабре »
  2. Объект, который будем подставлять в шаблон:

Главное преимущество данного подхода заключается в том, что теперь можно хранить объект integer ID в виде отдельного параметра, например в NoSql БД. И осуществлять удобный и быстрый поиск с использованием типизированных операций сравнения вместо написания регулярных выражений.

Serilog

Одной из удобных библиотек .NET, которые поддерживают структурное логирование является Serilog.
Библиотека поддерживает все основные функции логирования, которые есть у log4net, Nlog, и других известных библиотек:

  • Несколько общепринятых типов записи:
    Verbose — самое низкоуровнивое и детальное логирование (например, пришедшие аргументы в метод)
    Debug — данные для отладки кода, на один уровень выше Verbose (например, какой метод запускали, и результат выполнения)
    Warning — предупреждение для бизнес-процесса, не должно содержать Debug данные (например, запустили расчет зарплат)
    Error — ошибка в приложении, которое не ожидали
    Fatal — исключительная ошибка останавливающая бизнес процессы приложения (например, перенаправили пользователя в PayPal и оплата покупателя не равна ожидаемой сумме).
  • Разные типы хранения, называемых в Serilog стоком: текстовый файл, реляционные БД, NoSql БД, Windows Events, http/s запросы, и т.д.
  • Удобная конфигурация через код, так и через .config файлы

Итак, после установки Serilog Nuget пакета, настраиваем конфигурацию. Сделаем это в коде вместо .config файла.

Теперь можно передавать объект logger через Dipendency Injection, или сразу его использовать.
Допустим мы создаем программу для диагностики автомобиля и нам необходима информация о производителе, серии и дате впуска. И конечно, чтобы все хранилось в структурном виде.

Поскольку мы использовали вывод в консоль и файл, то и результат будет в виде привычной строки.

Одной из удобных особенностей является добавка часто используемых свойств. Для web приложения это может быть сессия пользователя, посещаемый URL, данные браузера. В более общем случае можно добавить версию приложения. Сделать это можно с помощью свойства Enrich и уже встроенных классов, или написать свой.

Для удобного вывода в консоль или текстовый файл сложных объектов лучше подсказать шаблону Serilog о том, что используем именно сложный объект, а не примитивный тип, добавив символ @. Иначе будет выводится typeof(MyClass).ToSting().

Приложение Seq приходит на помощь для удобного хранения и поиска структурных логов.
Работает Seq в виде windows сервиса, который принимает REST-запросы, а внутри хранит данные в NoSql БД.

После установки Seq необходимо также добавить в приложение Serilog.Sinks.Seq Nuget пакет. А далее подружить наш Serilog с Seq.

Теперь можно удобно делать поиск по нашим полям, включая сравнение чисел и дат

Параметры поиска можно сохранять и использовать в других частях приложения. Добавляем Environment в Dashboard

А можно добавить real-time Dashboard отображение ошибок, пришедших именно от приложения «MyApp», версии «1.2.1» и произошедших в методе «Repository.GetUserByIdAndPassword()».

Бизнес требования

Как и любую новую, модную, светящуюся программу надо проверять, задавая вопросы:
«Какую выгоду несет данная программа для бизнеса?», «Какую задачу она решает и за какую цену?».
Часто в отношение логирования цели сводятся к

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

Если первые два пункта решаются детальными записями и удобным поиском, то информирование об ошибках часто пропускают. Я это называю «спокойствием в неведении». Откроем журналы событий Windows и окажется, что почти на каждом сервере есть программы, которые сыпятся с критическими ошибками.

С помощью расширений Seq можно информировать о происходящих событиях. К примеру, сразу посылать сообщение программисту/администратору, если случилась критическая ошибка. И отправлять список всех ошибок, произошедших за день или неделю. Настроить расширения можно в разделе Seq -> Settings -> Apps

Online redo logs или Событие контрольной точки в Oracle

Немного теории

Итак. Когда приложение запрашивает данные, база складывает их в буферный кэш — область памяти в SGA. Когда данные изменяются, база производит изменения не непосредственно в файле данных, а в буферном кэше. Одновременно в отдельную область памяти — redo log buffer — записывается информация, по которой, в случае необходимости, можно будет повторить произошедшее изменение. Когда изменение фиксируется (commit), оно, опять же, не сбрасывается сразу в файл данных, но информация из redo log buffer сбрасывается в online redo лог — специально для этого предназначенный файл. До тех пор, пока изменение не записано в файл данных, необходимо хранить информацию о нём где-то на диске на тот случай, если база упадёт. Если, к примеру, выключится питание сервера, то, само собой, все данные, хранящиеся в памяти, будут потеряны. В этом случае redo лог — это единственное место, где хранится информация о произошедшем изменении. После рестарта базы Oracle фактически повторит прошедшую транзакцию, вновь изменит нужные блоки и сделает commit. Поэтому до тех пор, пока информация из redo лога не будет сброшена в файл данных, повторно использовать этот redo лог невозможно.

Специальный фоновый процесс базы данных DBWn по мере необходимости освобождает буферный кэш, а также выполняет событие контрольной точки (checkpoint). Контрольная точка — это событие, во время которого «грязные» (изменённые) блоки записываются в файлы данных. За событие контрольной точки отвечает процесс CKPT (checkpoint process), который и пишет информацию о контрольной точке в control file (о том, что такое control file, в другой раз) и заголовки файлов данных.

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

Существует несколько видов контрольных точек.

  • Потоковые контрольные точки (thread checkpoins). В файл данных пишутся подряд все изменения, произошедшие в рамках определённого экземпляра до определённого момента. Случаются они в следующих ситуациях:
    • полная остановка базы;
    • alter system checkpoint;
    • переключение online redo лога;
    • alter database begin backup.
  • Контрольные точки файлов данных и табличных пространств. Случаются, когда происходят операции с табличными пространствами и файлами данных (alter tablespace offline, alter tablespace read only, ужатие файла данных и.т.п.)
  • Инкрементальные контрольные точки. Подвид контрольной точки экземпляра, предназначенный для того, чтобы избежать записи на диск огромного количества блоков во время переключения redo логов. Процесс DBWn как минимум раз в три секунды проверяет, появились ли новые «грязные» блоки для записи на диск. Если появились, то они заносятся в файлы данных, метка контрольной точки в redo лог сдвигается (чтобы в следующий раз пришлось просматривать меньше логов), но заголовки файлов данных при этом не изменяются.

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

Параметры, от которых зависит частота событий контрольной точки и которые при желании можно настраивать:

  • FAST_START_MTTR_TARGET (сколько времени в секундах займёт восстановление базы после сбоя; если я что-нибудь в чём-нибудь понимаю, то речь идёт о времени, которое потребуется для применения имеющихся в наличии online redo логов).
  • LOG_CHECKPOINT_INTERVAL (частота события контрольной точки — допустимое количество блоков файла online redo log, которые были заполнены после пердыдущей контрольной точки; блоки — это блоки в терминах операционной системы, а не базы данных).
  • LOG_CHECKPOINT_TIMEOUT (максимально допустимое количество секунд, между двумя событиями контрольной точки).
  • LOG_CHECKPOINTS_TO_ALERT (true/false; определяет, скидывать ли переключение контрольной точки в alert.log; полезная штука, лучше бы выставить в true).

Имеет смысл уточнить, что параметры FAST_START_MTTR_TARGET и LOG_CHECKPOINT_INTERVAL, если верить документации, являются взаимоисключающими.

Заглянем теперь в нашу базу

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

Для отслеживания частоты переключения логов можно заглянуть в alert log.
Пример переключения online redo логов.

Иногда в alert.log можно обнаружить следующие ошибки.

Это означает, что Oracle собирается повторно использовать online redo лог, данные из которого ещё не сброшены в файлы данных. В этом случае все операции в базе приостанавливаются (производительность приложения резко ухудшается), вызывается событие контрольной точки и «грязные» блоки срочно сбрасываются на диск. Если подобные ошибки возникают от случая к случаю, то, пожалуй, ничего катастрофического в этом нет. Однако, если они становятся постоянными, то пора полумать о том, чтобы изменить размер и количество redo логов.

Из cookbook. Как изменить размер и/или количество online redo логов

1. Для начала просто посмотрим на состояние логов.

Попробуем увеличить размеры логов до 100M.

2. Посмотрим непосредственно на файлы redo логов.

3. Создадим три новых группы логов по 100M каждая.

Посмотрим, что получилось

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

Теперь с чистой совестью удаляем лишние логи

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

После чего смело повторяем попытку.

5. Проверим, всё ли у нас получилось.

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

7. Теперь можно удалить лишние файлы операционной системы.

Что делать, если у нас установлен Oracle RAC.

В принципе, всё то же самое. С учётом того, что для каждого экземпляра существует свой отдельный набор redo логов и оперировать ими придётся по-отдельности.

Команда ALTER SYSTEM CHECKPOINT LOCAL работает только с экземпляром, с которым вы в данный момент соединены. Чтобы вызвать событие контрольной точки для всей базы, нужно вызвать ALTER SYSTEM CHECKPOINT или ALTER SYSTEM CHECKPOINT GLOBAL.

Команда ALTER SYSTEM SWITCH LOGFILE влияет только на тот экземпляр, с которым вы в данный момент соединены. Чтобы переключить online redo логи для всей системы можно воспользоваться командой ALTER SYSTEM ARCHIVE LOG CURRENT.

Создавать новые online redo логи придётся для каждого экземпляра в отдельности.

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

Ещё кстати. Можно файлы online redo логов размножить.

Зачем размножить? Потому что, если по каким-то причинам файл online redo лога будет повреждён или потерян, то, имея его неповреждённую и непотерянную копию на другом диске, восстановление — дело двух минут. А вот если копии нет, то придётся повозиться (но процесс восстановления утерянных redo логов — уже совсем другая история).

Логгирование

Ведение лога и ILogger

ASP.NET Core имеет встроенную поддержку логгирования, что позволяет применять логгирование с минимальными вкраплениями кода в функционал приложения.

Для логгирования данных нам необходим объект ILogger . По умолчанию среда ASP NET Core через механизм внедрения зависимостей уже предоставляет нам такой объект. Например, возьмем стандартный проект по типу Empty и добавим механизм логгирования. Для этого перейдем к классу Startup и изменим его метод Configure() :

Средой выполнения в метод Configure передается объект ILogger, который представляет логгер. А метод логгера logger.LogInformation передает на консоль некоторую информацию. По умолчанию информация логгируется на консоль, поэтому для тестирования логгера нам надо запустить приложение как консольное:

При обращении к приложению с помощью следующего запроса http://localhost:xxxxx/index на консоль будет выведена информация, переданная логгером:

Важно, что, если мы используем класс Startup для логгирования, то получить объект логгера мы можем только в методе Configure() , но никак не в методе ConfigureServices() или конструкторе класса Startup, поскольку инфраструктура логгирования зависит от конфигурации и контейнера DI, который окончательно устанавливаются лишь после завершения работы метода ConfigureServices.

Категория логгера

В примере выше в метод передается не просто объект ILogger, а объект ILogger LogLevel . Всего мы можем использовать следующие значения:

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

Debug : для вывода информации, которая может быть полезной в процессе разработки и отладки приложения

Information : уровень сообщений, позволяющий просто отследить поток выполнения приложения

Warning : используется для вывода сообщений о неожиданных событиях, например, ошибках, которые не влияют не останавливают выполнение приложения, но в то же время должны быть иследованы

Error : информация об ошибках, вследствие которых приложение должно быть остановлено

Critical : уровень критических ошибок, которые могут быть связаны с какими-то ситуациями извне — ошибками операционной системы, потерей данных в бд, переполнение памяти диска и т.д.

None : вывод информации в лог не применяется

Для вывода соответствующего уровня информации у объекта ILogger определены соответствующие методы расширения:

Так, в примере выше для вывода информации на консоль использовался метод LogInformation() .

Вывод сообщений уровня Trace по умолчанию отключен.

Каждый такой метод имеет несколько перегрузок, которые могут принимать ряд различных параметров:

string data : строковое сообщение для лога

int eventId : числовой идентификатор, который связан с логом. Идентификатор должен быть статическим и специфическим для определенной части логгируемых событий.

string format : строковое сообщения для лога, которое моет содержать параметры

object[] args : набор параметров для строкового сообщения

Exception error : логгируемый объект исключения

Также для логгирования определен общий метод Log() , который позволяет определить уровень логгера через один из параметров:

При стандартном логгировании на консоль для каждого уровня/метода определен своя метка и цветовой маркер, которые позволяют сразу выделить сообщение соответствующего уровня. Например, при запуске следующего кода:

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL