В дизайне серверной службы перехват исключений является важным фактором, который следует учитывать.
Когда возникает аномалия, очень важно иметь возможность обнаружить ее с первого раза и иметь возможность получить достаточно информации для локализации проблемы.Это также содержание этой статьи
В начале позвольте мне задать два вопроса
- Если база данных, подключенная к серверной части в производственной среде, зависла, можете ли вы получить уведомление и найти проблему как можно скорее, вместо того, чтобы ждать полдня, чтобы найти проблему после обратной связи с пользователем (хотя эксплуатация и обслуживание точно узнаю проблему с первого раза) зависает база данных)
- Есть проблема с API в рабочей среде, можете ли вы измерить срочность ошибки и решить проблему по отчету
Ссылка на эту статью:
коллекция исключений
Исключения обычно возникают в следующих местах.
-
На уровне API/GraphQL промежуточное ПО используется на самом внешнем уровне уровня API для централизованной обработки и сообщения об ошибках. На конкретном логическом уровне часто нет необходимости активно перехватывать исключения, если нет специальной обработки исключений, такой как откат после сбоя транзакции базы данных.
// 在中间件中集中处理异常 app.use(async (ctx, next) => { try { await next(); } catch (err) { ctx.body = formatError(err) } }) // graphql 中也可以 new ApolloServer({ typeDefs, resolvers, formatError })
Когда исключение перехватывается на логическом уровне, а затем создается вручную, не теряйте контекстную информацию. В настоящее время вы можете использовать добавление поля originalError, чтобы сохранить контекстную информацию.
-
Уровни, не относящиеся к API, такие как сценарии, такие как конфигурация извлечения, сценарии миграции базы данных, запланированные задачи и т. д.
В дополнение к активно перехваченным исключениям, есть также некоторые исключения, которые могут быть пропущены, что может привести к завершению программы.
process.on('uncaughtException', (err) => {
console.error('uncaught', err)
})
process.on('unhandledRejection', (reason, p) => {
console.error('unhandle', reason, p)
})
Структура исключений API
Когда на уровне API возникает исключение, оно должно быть структурировано при передаче данных клиенту, чтобы облегчить следующий шаг отчета об исключении и внешнего анализа структурированной информации и соответствующего отображения.
Используйте следующееFormatError
Представляет структурированную информацию, которую API должен возвращать при возникновении исключения.
Когда возникает исключение, оно может быть единообразно захвачено промежуточным программным обеспечением верхнего уровня в качестве промежуточного программного обеспечения обработки ошибок, и при его обнаружении может использоваться функция.formatError
Унифицировать информацию о структурированных исключениях в промежуточном программном обеспечении
type FormatError {
code: string;
message: string;
info: any;
data: Record<string, any> | null;
originalError?: Error;
}
function formatError (error: Error): FormatError;
Ниже приведеныFormatError
Индикация каждого поля
code
Указывает идентификационный код ошибки, который используется для классификации ошибок, таких как недопустимые данные ввода пользователя (ValidationError), исключение базы данных (DatabaseError), сбой запроса внешней службы (RequestError)
По моему опыту, я делю код на следующие категории
- ValidationError, пользовательский ввод недействителен
- Ошибка базы данных, проблема с базой данных
- DatabaseUniqueError
- DatabaseConnectionError
- ...
- RequestError, внешняя служба
- RequestTimeoutError
- ForbiddenError
- AuthError, Несанкционированный запрос на авторизацию ресурса
- AppError, бизнес-проблема
- AppBadRequest
- ...
- ...
Для проверки данных, исключений базы данных и сбоев запросов мы обычно используем сторонние библиотеки.На этом этапе код можно настроить в соответствии с ошибкой сторонней библиотеки.
message
Представляет удобочитаемые сообщения об ошибках, но не обязательно для отображения во внешнем интерфейсе. Человек здесь представляет разработчика, а не пользователя, такие как следующие два сообщения, не подходят для отображения во внешнем интерфейсе
- connect ECONNREFUSED postgres.xiange.tech Не нужно отображать сообщение об отключении базы данных на внешнем интерфейсе
- электронная почта обязательна Проверка входных данных, хоть и может отображаться пользователю, но нужно отображать на китайском языке (интернационализация)
Вы можете решить, может ли внешний интерфейс отображать информацию, которую ожидает от нее внутренний интерфейс, в соответствии с кодом, а внешний интерфейс также может выполнять глобальную централизованную обработку в соответствии с кодом.
info
Указывает более подробную информацию о коде
- Когда отправка запроса не удалась, вы, по крайней мере, знаете, как выглядит неудачный запрос: метод, параметры/тело и заголовки.
- Когда проверка входных данных пользователя не удалась, по крайней мере узнайте, какие поля
originalError
originalError представляет API-интерфейс ошибки, вызванный этим исключением, который обычно содержит более подробную контекстную информацию.
originalError.stack
Указывает стек текущей ошибки.При возникновении исключения вы можете быстро определить местонахождение проблемы (хотя информация о стеке, иногда выдаваемая узлом, представляет собой файл, который он никогда не видел)
В средах разработки и тестирования добавление originalError к API может быстро найти проблему,Когда вы работаете, не добавляйте свой originalError в API., вы можете найти полное сообщение об ошибке в системе мониторинга
Вы можете использовать следующие два API для оптимизации вашегоstacktrace
Error.captureStackTrace(error, constructorOpt)
Error.prepareStackTrace(error, structuredStackTrace)
Для конкретного использования см.v8 stack trace api
data
Представляет данные, возвращаемые этим интерфейсом. Когда API сообщает об ошибке, должны ли данные возвращаться какnull
?
Нет, когда API сообщает об ошибке, проблемы могут быть только в некоторых полях, а остальные поля могут возвращаться нормально.потому чтоgraphql
агрегируется по полям, которые находятся вgraphql
проявились очень ярко.
http status
При возникновении ошибки во время обработки API должен быть возвращен код состояния 400+.
- HTTP/1.1 400 Bad Request
- HTTP/1.1 401 Unauthorized
- HTTP/1.1 403 Forbidden
- HTTP/1.1 429 Too Many Requests
Система наблюдения
Для мониторинга в первую очередь требуется система мониторинга, которую я рекомендую здесь.Sentry
, как развернуть можете обратиться к моей предыдущей статье:Как развернуть Сентри.
Вы также можете использовать версию SaaS непосредственно при официальной регистрации:Часовой Платный. Персональная бесплатная версия имеет месячный лимит ошибок 5K, чего достаточно для личного использования.
По сравнению с самодельной версией, использование SaaS экономит часть ежедневной работы по эксплуатации и обслуживанию. Самое главное,Самостоятельно построенные системы имеют функциональные ограничения.
вот примерноSentry
документация
показатель
За исключением исключений, в дополнение к самому исключению собирается больше индикаторов.
Наиболее важной целью мониторинга исключений является восстановление сценария создания исключений.
-
Уровни исключений: Fatel, Error и Warn. Это определяет, будете ли вы продолжать махать или открывать свой блокнот, чтобы исправить ошибку, когда вы получаете тревожное электронное письмо или текстовое сообщение в воскресенье. может быть отмечен кодом
const codeLevelMap = { ValidationError: 'warn', DatabaseError: 'error' }
-
Среда: производственная среда или тестовая среда, которая может напрямую считывать переменные среды службы приложений, если проблемы обнаруживаются раньше, чем пользователь и тест.
-
Контекст: например, какой запрос API, какой пользователь и более подробная информация о http-сообщении. Вы можете напрямую использовать Sentry API для передачи контекстной информации.
Sentry.configureScope(scope => { scope.addEventProcessor(event => Sentry.Handlers.parseRequest(event, ctx.request)) })
-
Пользователь: ошибка API была вызвана тем, какой пользователь
-
код: легко классифицировать ошибки
-
request_id: это удобно для трассировки, а также удобно для получения дополнительной отладочной информации: найти оператор SQL, выполняемый текущим API в elk
const requestId = ctx.header['x-request-id'] || Math.random().toString(36).substr(2, 9) Sentry.configureScope(scope => { scope.setTag('requestId', requestId) })
Из вышеизложенного видно, что данные для собранных индикаторов обычно поступают из двух аспектов: http-пакетов и переменных среды.
Filter
При локальной разработке часто нет необходимости сообщать об исключениях вSentry
.Sentry
Он также предоставляет хуки для фильтрации исключений перед отчетом.
beforeSend?(event: Event, hint?: EventHint): Promise<Event | null> | Event | null;
Добро пожаловать, чтобы обратить внимание на мой общедоступный номерГорная Луна Путешествие, записывайте сюда мой технический рост, добро пожаловать в общение