Показаны сообщения с ярлыком LogBook. Показать все сообщения
Показаны сообщения с ярлыком LogBook. Показать все сообщения

среда, 11 июня 2008 г.

Enterprise logging system. Part 5. Features.

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

Сцена 1. Сервис. Действующие лица: WCF, LogBook.

Поскольку framework WCF черезвычайно легко расширяется, подключить LogBook к нему одно удовольствие. На тему расширения WCF очень хорошо написал Aaron Skonnard здесь. Так вот, LogBook расширяет WCF при помощи поведения сервиса, реализованного в виде аттрибута, который вы можете декларативно применить к любому WCF сервису. Что же добавляет это поведение? Оно добавляет ErrorHandler ко всем ChannelDispatcher'ам и MessageInspector ко всем DispatchRuntime'ам EndpointDispatcher'ов.

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


ErrorHandler, вызывает метод WriteErrorMessage фасада LogBook.


MessageInspector, вызывает метод WriteTraceMessage фасада LogBook.

Сцена 2. DLinq. Действующие лица: DLinq, LogBook.
В DLinq предусмотрено подключение простого логгера, в который будут сваливаться все запросы, выполняемые системой. Этот логгер должен быть наследником TextWriter и переопределять как минимум метод Write(string). Для LogBook я написал простенькую реализацию, которая будучи подключенной к любому DLinq DataContext'у будет трассировать все запросы.


Linq tracer.

Сцена 3. .Net. действующие лица: System.Diagnostics, LogBook.
И последнее. Вы можете подключить LogBook к системе трассировки .Net при помощи простого TraceListner. Довольно удобно для приложений, код которых вам недоступен. С помощью этого прослушивателя трейсов вы сможете перенаправить сообщения в сервис логирования.



TraceListener.

Кроме того, в System.Diagnostics.Trace нельзя было передать контекст. А здесь можно.

Вот вкратце все сладости в LogBook. Жду предложений по дополнительным фичам!

Удачи!

вторник, 10 июня 2008 г.

Enterprise logging system. Part 4. Facade.

Ура, я проснулся от летаргического сна.

Предлагаю сразу взяться, например за дело.

Итак вперед.

В первой части я кратко описал требования к системе и архитектуру. В этой части я расскажу как эти требования были реализованы.

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

Как вы помните в системе предусмотрено 5 уровней логирования:

  • Trace;
  • Info;
  • Warn;
  • Error;
  • Status;
, соответственно и интерфейс фасада определен количеством и именами этих уровней.

Что же скрывают за собой эти методы? Давайте рассмотрим работу LogBook по шагам.

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

Далее, в зависимости от уровня логирования LogBook создаст соответствующее сообщение и отправит его на валидацию в список подключенных фильтров (важно сделать это до окончательного заполнения сообщения, чтобы не тратить память и время зря, если оно не проходит по условиям фильтров). После этого, дабы не блокировать клиента, фасад LogBook делегирует отправку сообщения RequestManager'у, который в асинхронном режиме постарается отправить его куда следует. Куда следует - это сюда: первым делом он сохранит его в локальной стратегии, а затем попытается отправить его сервису. В случае неудачной попытки, он увеличит счетчик неудачных попыток, проверит условия устаревания сообщения (по времени хранения и количеству неудачных попыток) и сохранит обратно сообщение в локальную очередь, если оно все еще валидно. За подключением сервиса следит ConnectionMonitor, который при изменении статуса подключения генерирует событие, перехватываемое RequestManager'ом.

Все методы перегружены одним дополнительным параметром - контекстом, о котором я писал здесь. Если вызывается метод без котекста, или вместо него передается null, то в качестве контекста будет автоматически подставлен LogContext.Current.

Вот в очень сжатом виде механизм работы моего enterprise LogBook'а.

Подумываю завести проект на CodePlex. Займусь этим на досуге.

Удачи!

среда, 30 апреля 2008 г.

Enterprise Logging System. Part 3. Service.

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


У сервиса 2 обязанности:
  • централизовать хранение настроек;
  • помещать сообщения в сконфигурированное хранилище.
соответственно выглядит его контракт:


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

Для того, чтобы сервис мог десериализовать сообщения классов-наследников базовый класс необходимо пометить атрибутами [KnownType(typeof(...))], где ... - имена классов наследников.

Metadata.

Первая задача предельно проста. Необходимо сохранять и извлекать настройки клиентов из БД. Если настроек для конкретного клиента нет, то возвращаются дефолтные настройки, общие для всех клиентов.
Метаданные хранятся в БД и извлекаются при помощи Linq2Sql классов.

Вот классы, являющиеся метаданными системы:


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






Logs
Сохранение сообщений делегируется адаптеру системы логирования, который подключается к сервису в файле конфигурации хоста сервиса. Я писал об этом здесь: http://bobbbloggg.blogspot.com/2008/04/loosely-coupled-wcf-service.html. В системе поумолчанию используется адаптер Enterprise Library Logging AB. Так как логи должны сохраняться в надежном хранилище с возможностью репликации со всеми данными системы на основной сервер, то конечно же в роли этого хранилища выступает БД. Дабы не тащить за собой Data Access AB из Enterprise Library, т.к. мне совсем не нравится его реализация, я написал свой легковесный custom DBTraceListener, сохраняющий логи в БД при помощи класов Linq2Sql. Этот TraceListener подключается к Logging AB в файле конфигурации и легко может быть заменен на любой другой.

Вот структура таблиц логов в БД.


Linq2Sql поддерживает паттерн отображения наследования с одной таблицой (см. Фаулер). Поэтому в таблицу введено специальное поле дискриминатор (DiscKey) и linq2sql классы образуют повторяющую LogMessage иерархию.


Все сообщения разделены на категории по уровню (LogLevel). Также в системе по умолчанию настроена одна дополнительная "специальная" категория сообщений - CriticalErrors, к которой подключен EmailTraceListener, который при деплойменте может быть настроен на почтовый ящик администратора системы и в случае получения сообщений о критических ошибках он будет получать письмо по почте, с тем чтобы привлечь его внимание к проблеме. Распределением сообщений по категориям занимается LoggingSystemAdapter.


В следующий раз я опишу фасад системы.
Удачи.

понедельник, 28 апреля 2008 г.

Enterprise Logging system. Part2 Context.

Перед тем как писать о сервисе коротко опишу контекст, передаваемый ему (сервису) вместе с сообщением. Контекст входит в data contract сообщения LogBook, т.е. я не использую появившийся в .Net 3.5 xxxContextBinding по той простой причине, что однажды установив его уже нельзя будет изменить. Да и передавать контекст в заголовке сообщения также не вижу особого смысла, поэтому он уютно располагается в теле сообщения.

Контекст в LogBook передается в виде словаря Dictionary<string, object>.
Чтобы облегчить себе жизнь я написал класс LogContext. LogContext имеет implicit operator конвертирующий его в Dictionary, поэтому не надо делать явных приведений типов.
У него есть свойство Current, которое возвращает текущий контекст (собирает небольшой набор данных об окружении) и несколько методов, с помощью которых его можно расширить.
Эти методы написаны в семантике “fluent interface”, каждый из которых возвращает копию исходного контекста с добавленными свойствами.


public LogContext AddAttributes(IDictionary attributes)
public LogContext AddAttribute(string name, object value)

и создание нового расширенного контекста будет выглядеть следующим образом:

var context = LogContext.Current.AddAttribute("1", 1)
.AddAttribute("datacontract", new Class1 { MyProperty = 1})
.AddAttribute("serializable", new Class2 { MyProperty = 2});
- очень удобно!

для того, чтобы передавать в контексте свои «кастомные» объекты, необходимо проделать следующее:
1. Пометить эти классы одним из атрибутов : Serializable или DataContract;
2. Сконфигурировать DataContractSerializer на клиенте и на сервере, добавив в конфигурационный файл приложения следующий блок:

<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="System.Collections.Generic.Dictionary`2,mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<knownType type="ContextSendingTest.Class1, ContextSendingTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
<knownType type="ContextSendingTest.Class2, ContextSendingTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>


Далее при добавлении атрибута контекст проверит, помечен ли класс одним из упомянутых атрибутов и сконфигурирован ли DataContractSerializer.

Контекст реализует интерфейс IEquatable, сравнивая коллекции аттрибутов с использованием дефолтных EqualityComparer, поэтому «кастомные» классы должны перегружать метод Equals для того чтобы изменить логику их сравнения и сравнения контекстов в целом.

Вот, вкратце так.

Удачи!

понедельник, 21 апреля 2008 г.

Enterprise Logging system. Part1 Architecture.

Имею желание поделиться с вами своим взглядом на дизайн системы логирования масштаба предприятия.

"О чем этот сумасшедший?"

Я о той части SOA инфраструктуры, которая отвечает за логирование событий в enterprise. С началом разработки данной системы мы начинаем лавировать в сторону настоящей SOA инфраструктуры, малыми галсами приближаясь к нашему видению такой модной нынче SOA концепции как ESB.

"Точно двинутый, ничего не понятно же".

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

Итак задачи, ограничения и требования к данной системе:


  • должна быть предельно проста в применении разработчиками;
  • должна функционировать в распределенной системе - конечная точка сервис;
  • возможность работать в отключенном режиме;
  • нельзя расчитывать на то, что на клиенте будет установлен SQL сервер, поэтому должна быть возможность подключать различные стратегии кеширования;
  • возможность подключения различных библиотек логирования;
  • возможность настройки уровня, категорий и пр. данных сообщений, которые будут отправляться с клиента;
  • сохранение логов в БД сервера, с тем чтобы реплицировать их с остальными данными на основной сервер;
  • возможность настройки альтернативного хранилища/цели сообщений логов (e-mail, event log…) (при помощи сторонних библиотек логирования);
  • возможность передать некий контекст;
  • управление исходящим траффиком
  • ...

Теперь обратимся к литературе. На codeproject я нашел несколько статей, в которых описано решение подобных задач:

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

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

Функциональность и модель программирования во всех этих проектах примерно одинаковая и включает в себя возможность логировать в такие хранилища как Event log, база данных, E-mail, и т.д. и т.п. В конечном итоге выбор я остановился на Enterprise library, во-первых потому что я уже имею опыт работы с Enterprise Library и использовал Validation Application Block в слое бизнес объектов в проектах (писал об этом здесь), а во-вторых чтобы сохранить единую базу, на которой строятся все проекты.

После обработки материалов и требований у меня сложилась следующая архитектура.


Характерными особенностями ее являются:
  • логирование всегда ведется на сервере, поэтому сервис логирования является основной компонентой системы;
  • сервис слабо связан с непосредственным кодом логирования и библиотекой, берущей на себя эти функции при помощи уже описанного мною приема http://bobbbloggg.blogspot.com/2008/04/loosely-coupled-wcf-service.html; Это оставляет за мной возможность безболезненно сменить библиотеку логирования, в случае, если звезды изменят свое расположение и я передумаю использовать Logging AB;
  • клиент работает с библотекой через фасад, а всю работу по конфигурированию, кешированию и работе с сервисом система берет на себя прозрачно для клиента, при этом по возможности не блокируя его;
  • сервис логирования - WCF singleton-сервис; Его функция централизовывать хранение настроек и делегировать сохранение сообщений адаптеру библиотеки логирования;
  • сохранение логов в базу данных производится через подключаемый к Logging AB Custom TraceListener.

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

  • Trace;
  • Info;
  • Warn;
  • Error;
  • Status;

Настройки клиентов, уникально себя идентифицирующих, хранятся в БД. При первом обращении к фасаду системы, клиент обращается к сервису с просьбой передать настройки, если сервис недоступен - пытается "поднять" их из локального конфига.

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

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

Всю работу по отправке сообщений в сервис берет на себя компонента системы Disconnected Service Agent (DSA). В Smart Client Software Library есть Application Block, реализующий данную функциональность, но по упомянутым выше причинам я предпочел не использовать готовую функциональность, а написал свой небольшой блок, опять же почерпнув из MS-ской реализации светлые идеи. Основными частями данного блока являются:

  • Connection Monitor - агент, который следит за состоянием подключения к сервису;
  • Request Manager - диспетчер запросов. Ставит запросы в очередь (локальную стратегию кеширования), пытается асинхронно отправить их, дабы не блокировать клиента;
  • Local Store Strategy - локальная стратегия кеширования, которая подключается в процессе конфигурирования.


На этом сегодня все в следующий раз подробнее о сервисе.

Удачи.