Багатошарова архітектура: де я повинен реалізувати журнал помилок \ обробку?


17

В даний час я переробляю велику підсистему з багатошаровою архітектурою, і я намагаюся створити ефективну стратегію реєстрації помилок \ обробку.

Скажімо, моя архітектура складається з наступних трьох шарів:

  • Загальнодоступний інтерфейс (IE і контролер MVC)
  • Шар домену
  • Шар доступу до даних

Моє джерело плутанини - це те, де мені слід застосувати журнал помилок \ обробку:

  1. Найпростішим рішенням було б здійснити ведення журналів на верхньому рівні (IE the Public Interface \ MVC Controller). Однак це відчуває себе неправильно, оскільки це означає перемикання винятку через різні шари, а потім його реєстрацію; а не записувати виняток у своєму джерелі.

  2. Реєстрація винятку в його джерелі, очевидно, найкраще рішення, оскільки я маю найбільше інформації. Моя проблема з цим полягає в тому, що я не можу зловити кожен виняток у джерелі, не вловивши ВСІ винятки, а в шарі домену / загальнодоступного інтерфейсу це призведе до вилучення виключень, які вже були схоплені, записані та повторно кинуті шаром нижче .

  3. Інша можлива стратегія - це поєднання №1 та №2; внаслідок чого я вловлюю конкретні винятки на шарі, які вони, швидше за все, будуть кинуті (IE Ловля, ведення журналів та повторне закидання SqlExceptionsв рівень доступу до даних), а потім реєструю будь-які подальші невиконані винятки на верхньому рівні. Однак це також вимагає від мене вибору та повторного перегляду кожного винятку на найвищому рівні, тому що я не можу розрізнити помилки, які вже були введені \ обробляються, та тими, що не мають.

Очевидно, це проблема в більшості програмних додатків, тому повинно бути стандартне рішення цієї проблеми, яке призводить до того, що винятки можуть бути схоплені у джерелі та записані один раз; однак я просто не бачу, як це зробити самостійно.

Зауважте, заголовок цього питання дуже схожий на " Журнал винятків у багаторівневій програмі" ", проте відповіді в цій публікації недостатньо деталізовані і недостатньо, щоб відповісти на моє запитання.


1
Один із підходів, який я іноді використовую, - це створити власний підклас Exception (або RuntimeException) та скинути його, включаючи оригінальний виняток як вкладений. Звичайно, це означає, що при захопленні винятків на верхніх рівнях мені потрібно перевірити тип виключення (мої власні винятки просто перезавантажуються, інші винятки реєструються та включаються до нових екземплярів моїх винятків). Але я довго працюю сольно, тому не можу дати "офіційних" порад.
SJuan76

4
The easiest solution would be to implement the logging at the top level- зробити це. Реєструвати винятки в їх джерелі - це не дуже гарна ідея, і кожна програма, з якою я стикався, робила це налагодження PITA. За поводження з винятками повинно бути відповідальність за абонента.
Джастін

Здається, ви маєте на увазі певну техніку / мову впровадження, інакше ваше твердження "винятки, які були зафіксовані в журналі, не відрізняються від тих, які не були" важко зрозуміти. Чи можете ви дати більше контексту?
Vroomfondel

@Vroomfondel - Ти маєш рацію. Я використовую C #, і обгортання коду в кожному шарі try{ ... } catch(Exception ex) { Log(ex); }призведе до того, що на кожному шарі буде записано однаковий виняток. (Також видається досить поганою практикою ловити всі винятки на кожному шарі в кодовій базі.)
KidCode

Відповіді:


18

До ваших питань:

Найпростішим рішенням було б здійснити ведення журналу на верхньому рівні

Мати бульбашку винятків до верхнього рівня - це абсолютно правильний і правдоподібний підхід. Жоден із методів вищого шару не намагається продовжити якийсь процес після відмови, який, як правило, не може досягти успіху. І добре обладнаний виняток містить всю інформацію, необхідну для ведення журналу. І якщо нічого не робити у винятках, ви не зможете зберегти чистий код і зосередитись на головній задачі замість збоїв.

Реєстрація винятку в його джерелі, очевидно, найкраще рішення, оскільки я маю найбільше інформації.

Це наполовину правильно. Так, найкорисніша інформація доступна там. Але я б рекомендував помістити все це в об’єкт виключення (якщо його ще немає), а не негайно входити в нього. Якщо ви зареєструєтесь на низькому рівні, вам все-таки потрібно зробити виняток, щоб сказати своїм абонентам, що ви не завершили роботу. Це закінчується в декількох журналах однієї події.

Винятки

Моє основне керівництво - це виловлювати та реєструвати винятки лише на верхньому рівні. І всі шари нижче повинні переконатися, що вся необхідна інформація про відмову переноситься до верхнього рівня. У програмі для одноразового використання, наприклад, на Java, це здебільшого означає не намагатися / ловити або входити в журнал взагалі за межами верхнього рівня.

Іноді потрібно, щоб деяка інформація про контекст була включена до журналу винятків, який недоступний у вихідному винятку, наприклад, оператор SQL та параметри, які були виконані під час скидання виключення. Тоді ви можете зловити оригінальний виняток і повторно кинути новий, що містить оригінальний плюс контекст.

Звичайно, реальне життя іноді заважає:

  • У Java іноді доводиться виловлювати виняток і перетворювати його на інший тип винятків лише для підпорядкування деяких підписів фіксованого методу. Але якщо ви повторно кидаєте виняток, переконайтеся, що повторно викинутий містить всю інформацію, необхідну для подальшого ведення журналу.

  • Якщо ви перетинаєте межу міжпроцесового режиму, часто технічно ви не можете передати повний об'єкт виключення, включаючи трасування стека. І звичайно зв’язок може загубитися. Отже, ось момент, коли сервіс повинен реєструвати винятки, а потім спробувати зробити все можливе, щоб якомога більше інформації про відмову передавались клієнту по всій лінії. Послуга повинна переконатися, що клієнт отримує повідомлення про помилку, або отримавши відповідь про відмову, або запустивши тайм-аут у разі розриву зв'язку. Це, як правило, призводить до того, що один і той же помилок не буде зареєстрований двічі, один раз всередині сервісу (з більш детальною інформацією) та один раз на найвищому рівні клієнта.

Ведення журналів

Я додаю кілька речень про ведення журналу в цілому, а не лише про реєстрацію винятків.

Крім виняткових ситуацій, ви хочете, щоб важливі дії вашої програми також були записані в журнал. Отже, використовуйте рамку ведення журналів.

Будьте уважні щодо рівнів журналів (читання журналів, де інформація про налагодження та серйозні помилки не позначені різними відповідно - це біль!). Типові рівні журналу:

  • ПОМИЛКА: Деякі функції не вдалося усунути. Це не обов'язково означає, що вся ваша програма вийшла з ладу, але якесь завдання не вдалося виконати. Як правило, у вас є об'єкт виключення, що описує збій.
  • ПОПЕРЕДЖЕННЯ: Сталося щось дивне, але це не спричинило збій будь-якого завдання (виявлена ​​дивна конфігурація, тимчасовий зрив з'єднання, що спричиняє деякі спроби тощо)
  • ІНФОРМАЦІЯ: Ви хочете повідомити деяку значну дію програми місцевому адміністратору системи (запустивши якусь послугу з її конфігурації та версії програмного забезпечення, імпорт файлів даних у базу даних, користувачі входять у систему, ви отримаєте ідею ...).
  • DEBUG: Те, що ви, як розробник, хочете побачити, коли ви налагоджуєте якусь проблему (але ви ніколи не дізнаєтесь заздалегідь, що вам дійсно потрібно у випадку тієї чи іншої помилки - якщо ви зможете це передбачити, ви виправите помилку ). Одне, що завжди корисно - це реєструвати діяльність на зовнішніх інтерфейсах.

Під час виробництва встановіть рівень журналу в INFO. Результати повинні бути корисні системному адміністратору, щоб він знав, що відбувається. Очікуйте, що він зателефонує вам за допомогою або виправлення помилок для кожної ПОМИЛКИ у журналі та половині ПОПЕРЕДЖЕННЯ.

Увімкніть рівень DEBUG лише під час справжніх сесій налагодження.

Згрупуйте записи журналу за відповідними категоріями (наприклад, за повністю кваліфікованим іменем класу коду, який генерує запис), що дозволяє вмикати журнали налагодження для конкретних частин вашої програми.


Дякую за таку детальну відповідь, я дуже ціную також частину журналу.
KidCode

-1

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

Виключення з випуску, набагато менше їх повторного ведення, - це додаткові зусилля з невеликою користю. Ловіть виняток у джерелі (так, найпростіше), запишіть його, але потім не перекидайте виняток, просто повідомте «помилку» абоненту. "-1", null, порожня рядок, деякий перелік, що завгодно. Абоненту потрібно лише знати, що виклик не вдався, майже ніколи не викликає жахливих деталей. І вони будуть у вашому журналі, правда? У рідкісному випадку, коли абонентові потрібні деталі, продовжуйте роботу з бульбашкою вгору, але не як автоматичний задум за задумом.


3
Проблема з повідомленням про помилку зворотними значеннями: 1. Якщо абонент піклується про збої, він повинен перевірити спеціальне значення. Але зачекайте, це nullчи порожня рядок? Це -1 або якесь від’ємне число? 2. Якщо абонент не піклується (тобто не перевіряє), це призводить до виявлення помилок, не пов'язаних з первинною причиною, наприклад, a NullPointerException. Або ще гірше: розрахунок триває з неправильними значеннями. 3. Якщо абонент потурбується, але програміст не вважає, що цей метод не працює, компілятор не нагадує йому. Винятки не мають цих проблем, або ви їх спіймаєте, або перекиньте.
siegi

1
Вибачте, мені довелося це зняти. Підхід, який ви описуєте (замініть винятки спеціальними поверненими значеннями), був найсучаснішим у 1970-х роках, коли виникла мова С, і є багато вагомих причин, чому сучасні мови мають винятки, і для мене головне - це Правильне використання винятків значно спрощує запис надійного коду. І ваші "винятки з бурбалки вгору [...] - це додаткові зусилля [...]", очевидно, неправильно: просто не робіть нічого щодо винятків, і вони все будуть переплутати самі по собі.
Ральф Клеберхофф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.