Обёртывание ошибок с помощью `%w`
Часть раздела Логика и управление потоком путешествия по GO на Coddy. Урок 39 из 68.
Когда в вашем приложении возникают ошибки, им часто требуется дополнительный контекст по мере прохождения через разные уровни кода. Go предоставляет для этого мощный механизм: оборачивание ошибок с помощью форматирующего глагола %w в fmt.Errorf.
Обёртывание ошибок позволяет добавить контекстную информацию к существующей ошибке, сохранив исходную ошибку в её основе. Так создаётся цепочка ошибок, которая сохраняет полную историю произошедшего:
originalErr := errors.New("file not found")
wrappedErr := fmt.Errorf("failed to load config: %w", originalErr)Форматный глагол %w особенный: он указывает fmt.Errorf обернуть ошибку, а не просто преобразовать её в строку. Это сохраняет исходную ошибку, чтобы позже её можно было изучить с помощью таких функций, как errors.Is и errors.As.
Этот шаблон особенно полезен в крупных приложениях, где ошибка может пройти через несколько функций, каждая из которых добавляет свой контекст. Например, ошибка подключения к базе данных может быть обёрнута сообщением "failed to connect to database", а затем ещё раз сообщением "unable to start server", создавая ясную цепочку событий и показывая, где именно всё произошло.
Задание
ЛегкоСоздайте систему обработки файлов, которая добавляет к ошибкам контекстную информацию по мере их прохождения через разные уровни приложения. В этом задании показано, как использовать спецификатор формата %w с fmt.Errorf, чтобы создавать цепочки ошибок, сохраняющие исходную ошибку и добавляющие значимый контекст.
Вы получите два входных значения:
- Строку со сведениями об операции с файлом в формате
"filename,operation,status"(например,"config.json,read,permission_denied") - Строку с контекстом системы в формате
"service_name,component"(например,"UserService,ConfigLoader")
Ваша задача:
- Разобрать первый вход, разделив его по запятым, чтобы получить имя файла, операцию и статус
- Разобрать второй вход, разделив его по запятым, чтобы получить имя сервиса и компонент
- Создать исходную ошибку на основе статуса с помощью
errors.New():- Если статус —
"permission_denied":"permission denied" - Если статус —
"not_found":"file not found" - Если статус —
"corrupted":"file corrupted" - Для любого другого статуса:
"unknown error"
- Если статус —
- Создать ошибку уровня файла с обёрткой с помощью
fmt.Errorfи%w:"failed to [operation] file [filename]: %w" - Создать ошибку уровня компонента с обёрткой:
"[component] error: %w" - Создать ошибку уровня сервиса с обёрткой:
"[service_name] operation failed: %w" - Вывести цепочку ошибок, напечатав каждый уровень:
"Original error: [original_error_message]""File level: [file_level_error_message]""Component level: [component_level_error_message]""Service level: [service_level_error_message]"
- Вывести сводку по цепочке ошибок:
"Error Chain Summary:""File: [filename]""Operation: [operation]""Component: [component]""Service: [service_name]""Root cause: [original_error_message]"
Используйте пакет strings, чтобы разделить входные строки по запятым, пакет errors — чтобы создать исходную ошибку, а пакет fmt — чтобы создать ошибки с обёрткой. В этом задании показано, как обёртывание ошибок с помощью %w создаёт чёткую цепочку контекста по мере прохождения ошибок через разные уровни приложения, что значительно упрощает отладку.
Попробуйте сами
package main
import (
"errors"
"fmt"
"strings"
)
func main() {
// Считать входные данные
var fileDetails string
var systemContext string
fmt.Scanln(&fileDetails)
fmt.Scanln(&systemContext)
// Разобрать входные данные
fileInfo := strings.Split(fileDetails, ",")
filename := fileInfo[0]
operation := fileInfo[1]
status := fileInfo[2]
contextInfo := strings.Split(systemContext, ",")
serviceName := contextInfo[0]
component := contextInfo[1]
// TODO: Напишите свой код ниже
// Создать исходную ошибку на основе status
// Создать обёрнутую ошибку на уровне файла
// Создать обёрнутую ошибку на уровне компонента
// Создать обёрнутую ошибку на уровне сервиса
// Вывести ошибку на каждом уровне и сводку
}В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Логика и управление потоком
1Продвинутое управление потоком
Оператор switch с `fallthrough`Выход из вложенных цикловПереход к следующей итерации конкретного циклаОператор `goto`Повторение: расширенное управление циклами4Проект: простой список задач
Настройка проектаДобавление задачи7Обработка ошибок: углублённый разбор
Пользовательские типы ошибокОбёртывание ошибок с помощью `%w`Извлечение исходной ошибки с помощью `errors.Is`Извлечение исходной ошибки с помощью `errors.As`Разбираемся с `panic`Использование `recover`Итоги — безопасное деление2Структуры и методы
Определение методов для структурПолучатели-значенияПолучатели-указателиВыбор получателейМетоды и функцииПовторение: поведение структур5Карты: подробный разбор
Карты структурУказатели как значения картПроверка карт на nilСравнение картПовторение — счётчик частоты слов8Проект: простой складской учёт
Обзор проектаПроверка запасов3Интерфейсы (основы)
Что такое интерфейс?Определение интерфейсаРеализация интерфейсаИспользование типов интерфейсовПустой интерфейсУтверждения типовПереключатель типовПовторение — формы и поведениеПотренируйтесь самостоятельно: Онлайн-компилятор Go