Menu
CoddyTech

Извлечение исходной ошибки с помощью `errors.Is`

Часть раздела Логика и управление потоком путешествия по GO на Coddy. Урок 40 из 68.

При работе с обёрнутыми ошибками нужно иметь возможность проверить, есть ли конкретная ошибка где-либо в цепочке ошибок. Функция errors.Is предоставляет именно такую возможность, позволяя распознавать ошибки-сентинелы, даже если они были обёрнуты несколько раз.

Функция errors.Is принимает два параметра: ошибку, которую вы хотите изучить, и целевую ошибку, которую ищете. Она возвращает true, если целевая ошибка найдена где-либо в цепочке ошибок:

var ErrNotFound = errors.New("not found")

wrappedErr := fmt.Errorf("failed to load user: %w", ErrNotFound)

if errors.Is(wrappedErr, ErrNotFound) {
    fmt.Println("This is a not found error")
}

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

challenge icon

Задание

Легко

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

Вам будут переданы два входных значения:

  • Строка со сведениями об операции с базой данных в формате "database_name,operation,error_type" (например, "users_db,connect,connection_timeout")
  • Строка с контекстом приложения в формате "service_name,retry_count" (например, "AuthService,3")

Ваша задача:

  1. Определить три ошибки-маркера с помощью errors.New():
    • ErrConnectionTimeout с сообщением "connection timeout"
    • ErrConnectionRefused с сообщением "connection refused"
    • ErrDatabaseNotFound с сообщением "database not found"
  2. Разобрать первое входное значение, разделив его по запятым, чтобы получить имя базы данных, операцию и тип ошибки
  3. Разобрать второе входное значение, разделив его по запятым, чтобы получить имя службы и количество повторных попыток
  4. Создать исходную ошибку на основе типа ошибки:
    • Если тип ошибки — "connection_timeout": использовать ErrConnectionTimeout
    • Если тип ошибки — "connection_refused": использовать ErrConnectionRefused
    • Если тип ошибки — "database_not_found": использовать ErrDatabaseNotFound
    • Для любого другого типа ошибки: использовать errors.New("unknown database error")
  5. Создать ошибку, обёрнутую на уровне базы данных, с помощью fmt.Errorf и %w: "failed to [operation] database [database_name]: %w"
  6. Создать ошибку, обёрнутую на уровне службы: "[service_name] database operation failed after [retry_count] retries: %w"
  7. Использовать errors.Is, чтобы проверить, соответствует ли итоговая обёрнутая ошибка каждой ошибке-маркеру, и вывести результаты:
    • "Checking for connection timeout: [true/false]"
    • "Checking for connection refused: [true/false]"
    • "Checking for database not found: [true/false]"
  8. Вывести цепочку ошибок:
    • "Error Chain:"
    • "Original: [original_error_message]"
    • "Database level: [database_level_error_message]"
    • "Service level: [service_level_error_message]"
  9. Вывести анализ ошибки:
    • "Error Analysis:"
    • "Database: [database_name]"
    • "Operation: [operation]"
    • "Service: [service_name]"
    • "Retries: [retry_count]"
    • "Error type detected: [detected_error_type]", где detected_error_type — одно из значений "Connection Timeout", "Connection Refused", "Database Not Found" или "Unknown Error"

Используйте пакет strings, чтобы разделить входные строки по запятым, пакет errors для ошибок-маркеров и errors.Is, а пакет fmt — для обёртывания ошибок. В этом задании показано, как errors.Is обеспечивает надёжное обнаружение ошибок, которое корректно работает с обёрнутыми ошибками, что делает его необходимым для надёжной обработки ошибок в сложных приложениях.

Попробуйте сами

package main

import (
	"errors"
	"fmt"
	"strings"
)

func main() {
	// Считайте входные данные
	var dbOperation string
	var appContext string
	fmt.Scanln(&dbOperation)
	fmt.Scanln(&appContext)
	
	// Разберите входные данные
	dbParts := strings.Split(dbOperation, ",")
	databaseName := dbParts[0]
	operation := dbParts[1]
	errorType := dbParts[2]
	
	contextParts := strings.Split(appContext, ",")
	serviceName := contextParts[0]
	retryCount := contextParts[1]
	
	// TODO: Напишите свой код ниже
	// 1. Определите три сигнальные ошибки с помощью errors.New()
	// 2. Создайте исходную ошибку в зависимости от типа ошибки
	// 3. Создайте обёрнутую ошибку уровня базы данных с помощью fmt.Errorf и %w
	// 4. Создайте обёрнутую ошибку уровня сервиса
	// 5. Используйте errors.Is для проверки каждой сигнальной ошибки
	// 6. Выведите результаты и цепочку ошибок
	
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Логика и управление потоком

Потренируйтесь самостоятельно: Онлайн-компилятор Go