Страницы

Поиск по вопросам

воскресенье, 8 марта 2020 г.

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

#c_sharp #winforms #логирование


Заранее прошу прощения за несколько размытый вопрос, который может иметь, пожалуй,
несколько ответов, но всё-таки интересует прежде всего, практический аспект. 

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

Часто в коде вижу различные (что естественно, imho) варианты отлова ошибок, от вполне
годных и уместных до просто try{...} catch{MessageBox.Show("Ошибка!");} (Да, и не такое
бывает), часто критичные места вообще без блоков try.

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

     if (string.IsNullOrEmpty(path))
        {
            throw new ArgumentNullException(path, "Путь к каталогу должен быть задан.");
        }


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

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

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

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

Но в целом интересны мнения сообщества, какие еще практические реализации и подходы,
методики были бы уместны и удобны в повседневном использовании с учетом описанного
контекста? Возможно, есть какие-то более эффективные, удобные и проверенные способы
или средства?

P.S. 


Локализации проекта на несколько языков — пока нет.
Вносить существенные изменения в код модулей не очень хотелось бы ("малой кровью"
обойтись бы).
Хотелось бы избежать сторонних решений, если, конечно они не решают задачу системно
и применимы в дальнейшем (вспомнился EurekaLog в Delphi).


Спасибо за внимание к вопросу!
    


Ответы

Ответ 1



Ошибки нужно отлавливать try/catch/finaly только в тех местах где они ожидаемы и только тех типов, которые ожидаются, банальный пример обращение к методу, в методе проверка на, к примеру, доступность этого вызова текущему пользователю, ожидаемое исключение скажем PermitionDeniedException, в блоке catch его и нужно отлавливать, остальное (например ArgumentNullException) должно пробрасываться на верх стека и обрабатывать "глобально", возможно тотальным крахом приложения. [System.Serializable] public class PermitionDeniedException : System.Exception { public PermitionDeniedException() : base("Пользователю не доступен этот метод") { } public PermitionDeniedException(string message) : base(message) { } public PermitionDeniedException(string message, Exception inner ) : base( message, inner ) { } protected PermitionDeniedException(SerializationInfo info, StreamingContext context) : base(info,context ) { } } try { SomeMethod(); } catch (PermitionDeniedException ex) { _logger.Error(ex, "Сообщение") } private static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) { var ex = e.ExceptionObject; if (ex.GetType() == typeof(ExceptionType1)) { _logger.Error(ex, "Сообщение"); // TODO: Уведомить пользователя } else if (ex.GetType() == typeof(ExceptionType2)) { _logger.Fatal(ex, "Сообщение"); // Экстренно завершить приложение } else { _logger.Fatal(ex, "Сообщение"); // Можно сделать дамп и завершить приложение } } Для Winforms, где-то в методе Main наверное стоит подписать на AppDomain.UnhandledException Например так: System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper; В обработчике UnhandledExceptionTrapper можно получить тип исключения, проверить его критичность и в зависимости от нее (критичности) либо экстренно свалиться, сообщив пользователю что то вроде "Программа выполнила недопустимую ошибку и будет закрыта" или же продолжить работу, но опять же известив пользователя. Что касается мультиязычности, то тут нужны динамические ресурсы и словари ресурсов, в них можно хранить строки, которыми можно описать исключение. П.С. Структуру исключении стоит продумать, она может быть и одноуровневой, что скорее даже плюс. Максимально общие (корни) необходимо наследовать от System.Exception, что писать в сообщении придумать тоже не сложно, если у вас продуманная структура исключений. Для логирования ошибок лучше всего использовать готовые логгеры, или Trace. Последний не такой гибкий, зато ничего из nuget тащить не нужно. Из готовых последнее время сам пользуюсь NLog. Несколько сумбурно на мой взгляд, поэтому буду рад критике.

Комментариев нет:

Отправить комментарий