AWS X-Ray - это сервис AWS, который отслеживает ваше приложение. Это позволяет отслеживать ошибки и время выполнения в режиме реального времени. Итак, как вы используете его в своем бессерверном приложении JavaScript?

Сначала вам нужно установить aws-xray-sdk-core модуль, выполнив одну из следующих команд:

yarn add aws-xray-sdk-core
npm i --save aws-xray-sdk-core

Начнем с простой лямбды с одним сегментом:

import AWSXRay from 'aws-xray-sdk-core'

AWSXRay.enableManualMode()

const fetchData = () => {
  return {
    message: 'hello'
  }
}

export const hello = (event, context, callback) => {
  const segment = new AWSXRay.Segment('hello')

  try {
    const data = fetchData()

    callback(null, {
      statusCode: 200,
      body: data,
    })

  } catch(err) {
    segment.addError(err)
  } finally {
    segment.close()
  }
}

Запустив этот код, вы увидите что-то подобное в консоли AWS X-Ray:

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

export const slowFunction = async () => {
  await new Promise(resolve => setTimeout(resolve, 2000))
}

export const fasterFunction = async () => {
  await new Promise(resolve => setTimeout(resolve, 150))
}

export const unreliableFunction = async () => {
  if(Math.random() < 0.2) {
    throw new Error('Something went wrong')
  }
}

Вот где в игру вступают подсегменты или дополнительные сегменты:

import AWSXRay from 'aws-xray-sdk-core'

import { slowFunction, fasterFunction, unreliableFunction } from './functions'

AWSXRay.enableManualMode()

const fetchData = async (segment) => {
  const slowSubsegment = segment.addNewSubsegment('Slow Function')

  try {
    await slowFunction()
  } finally {
    slowSubsegment.close()
  }

  const fasterSubsegment = segment.addNewSubsegment('Faster Function')

  try {
    await fasterFunction()
  } finally {
    fasterSubsegment.close()
  }

  const unreliableSubsegment = segment.addNewSubsegment('Unreliable Function')

  try {
    await unreliableFunction()
  } catch (err) {
    unreliableSubsegment.addError(err)
    throw err
  } finally {
    unreliableSubsegment.close()
  }

  return {
    message: 'hello'
  }
}

export const hello = async (event, context, callback) => {
  const segment = new AWSXRay.Segment('hello function')

  try {
    const data = await fetchData(segment)

    callback(null, data)
  } catch(err) {
    segment.addError(err)
    callback(err)
  } finally {
    segment.close()
  }
}

При успешном запуске этой функции в X-Ray появится следующий след:

Теперь вы можете анализировать время отклика с помощью производственных данных в реальном времени.

Если наша ненадежная функция выдает ошибку, трассировка также будет иметь информацию об этом:

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

import AWSXRay from 'aws-xray-sdk-core'

import { slowFunction, fasterFunction, unreliableFunction } from './functions'

AWSXRay.enableManualMode()

const asyncSubsegment = async (name, parent, fn) => {
  const subsegment = parent.addNewSubsegment(name)
  try {
    return await fn()
  } catch (e) {
    subsegment.addError(e)
    throw e
  } finally {
    subsegment.close()
  }
}

const fetchData = async (segment) => {
  await asyncSubsegment('Slow Function', segment, slowFunction)

  await asyncSubsegment('Faster Function', segment, fasterFunction)

  return asyncSubsegment('Unreliable Function', segment, unreliableFunction)
}

export const hello = async (event, context, callback) => {
  const segment = new AWSXRay.Segment('hello function')

  try {
    const data = await fetchData(segment)
    callback(null, data)
  } catch(err) {
    segment.addError(err)
    callback(err)
  } finally {
    segment.close()
  }
}

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

Полный пример кода

Возможно, вы захотите создать сегмент вместо подсегмента, в зависимости от ваших потребностей:

new AWSXRay.Segment('Segment Name', parentSegment.trace_id, parentSegment.id)

Использование сегментов приведет к созданию следующего вывода в консоли AWS:

Что действительно приятно, если вы хотите анализировать разные ветки кода по отдельности.

Передача сегмента при каждом вызове функции может быть немного обременительной, поэтому правильный способ справиться с этим - использовать автоматический режим. Чтобы использовать его с AWS Lambda, необходимо включить активную трассировку.

Если вы используете Serverless Framework, вам понадобится serverless-plugin-tracingmodule.

Когда вы закончите с этим, пора изменить нашу asyncSubsegment функцию, чтобы использовать текущий глобально доступный сегмент.

const asyncSubsegment = async (name, fn) => {
  const subsegment = AWSXRay.getSegment().addNewSubsegment(name)
  try {
    return await fn()
  } catch (e) {
    subsegment.addError(e)
    throw e
  } finally {
    subsegment.close()
  }
}

После этого функция fetchData будет выглядеть намного чище:

const fetchData = async () => {
  await asyncSubsegment('Slow Function', slowFunction)

  await asyncSubsegment('Faster Function', fasterFunction)

  return asyncSubsegment('Unreliable Function', unreliableFunction)
}

Полный пример кода, как его использовать, также доступен в отдельной ветке.