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

Захватывайте все виды пользовательских событий через API пользовательских событий

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

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

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

Добавление пользовательских событий

Section titled Добавление пользовательских событий

Для этого примера возьмём типичный сайт электронной коммерции и добавим несколько событий, чтобы понимать, когда наш пользователь добавляет товар в корзину покупок.

По умолчанию OpenReplay не отслеживает эту информацию. Однако с помощью пользовательских событий вы можете легко это отслеживать.

Создание Tracker Provider

Section titled Создание Tracker Provider

Всё, что вам нужно знать сейчас, — это то, что мы создаём провайдер контекста, который позволит вам взаимодействовать с трекером через несколько функций.

В частности, нас будут интересовать logIssue и logEvent, которые позволяют отправлять пользовательскую проблему или событие на платформу.

  • События предназначены для регистрации действий, специфичных для пользователя. В нашем случае, например, мы будем регистрировать добавление товара в корзину.
  • Проблемы, с другой стороны, предназначены для регистрации ошибок, которые наш трекер не определяет автоматически. В нашем случае мы смоделируем сетевую ошибку, которая не позволяет нам получить доступ к стороннему API. Когда это произойдёт, мы зарегистрируем проблему на платформе.
import { createContext, useCallback } from 'react'
import Tracker from '@openreplay/tracker'
import { v4 as uuidV4 } from 'uuid'
import { useReducer } from 'react'

export const TrackerContext = createContext()
function defaultGetUserId() {
  return uuidV4()
}
function newTracker(config) {
  const getUserId =
    config?.userIdEnabled && config?.getUserId
      ? config.getUserId
      : defaultGetUserId
  let userId = null
  const trackerConfig = {
    projectKey:
      config?.projectKey || process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY,
 
  }
  if (config?.ingestPoint || process.env.NEXT_PUBLIC_OPENREPLAY_INGEST_POINT) {
    trackerConfig.ingestPoint =
      config?.ingestPoint || process.env.NEXT_PUBLIC_OPENREPLAY_INGEST_POINT
  }

  console.log('Tracker configuration: ')
  console.log(trackerConfig)
  const tracker = new Tracker(trackerConfig)
  if (config?.userIdEnabled) {
    userId = getUserId()
    tracker.setUserID(userId)
  }
  return tracker
}
function reducer(state, action) {
  switch (action.type) {
    case 'init': {
      if (!state.tracker) {
        console.log('Instantiaing the tracker for the first time...')
        let t = newTracker(state.config)
        let pluginsReturnedValue = {}
        if (state.config.plugins) {
          state.config.plugins.forEach((p) => {
            console.log('Using plugin...')
            pluginsReturnedValue[p.name] = t.use(p.fn(p.config))
          })
        }
        return {
          ...state,
          pluginsReturnedValue: pluginsReturnedValue,
          tracker: t,
        }
      }
      return state
    }
    case 'start': {
      console.log('Starting tracker...')
      state.tracker.start()
      return state
    }
    case 'logEvent': {
      console.log('Logging event')
      state.tracker?.event(action.payload?.name, action.payload?.data)
      return state
    }
    case 'logIssue': {
      console.log('Logging issue')
      state.tracker?.issue(action.payload?.name, action.payload?.data)
      return state
    }
  }
}
export default function TrackerProvider({ children, config = {} }) {
  let [state, dispatch] = useReducer(reducer, {
    tracker: null,
    pluginsReturnedValue: {},
    config,
  })
  let value = {
    startTracking: () => dispatch({ type: 'start' }),
    initTracker: () => dispatch({ type: 'init' }),
    logEvent: (evnt) => dispatch({ type: 'logEvent', payload: evnt }),
    logIssue: (evnt) => dispatch({ type: 'logIssue', payload: evnt }),
    pluginsReturnedValues: { ...state.pluginsReturnedValue },
  }
  return (
    <TrackerContext.Provider value={value}>{children}</TrackerContext.Provider>
  )
}

Функции logEvent и logIssue имеют одинаковую сигнатуру; мы передадим объект со свойствами name и data. name будет использоваться для идентификации нашей записи в интерфейсе OpenReplay, а data будет содержать регистрируемую информацию.

Помните: свойство data должно содержать сериализуемый объект.

Затем мы можем настроить этого провайдера в нашем файле _app.tsx следующим образом:


//imports here...

export default function MyApp({ Component, pageProps }: AppProps) {
  const Layout = (Component as any).Layout || Noop

  useEffect(() => {
    document.body.classList?.remove('loading')
  }, [])

  return (
    <TrackerProvider config={{}}>
      <Head />
      <ManagedUIContext>
        <Layout pageProps={pageProps}>
          <Component {...pageProps} />
        </Layout>
      </ManagedUIContext>
    </TrackerProvider>
  )
}

Покончив с этим, мы можем перейти к запуску событий.

Регистрация пользовательских проблем и событий

Section titled Регистрация пользовательских проблем и событий

Для этого мы воспользуемся нашим интерфейсом:

Страница товара

Мы будем записывать новое событие каждый раз, когда пользователь добавляет товар в нашу корзину (по сути, когда он нажимает кнопку «ADD TO CART»).

И мы зарегистрируем проблему, если он сделает это, не выбрав сначала размер.

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

В начале нашего компонента мы будем использовать хук useContext:

//outside the component
import { TrackerContext } from '../../../context/trackerProvider'

//inside the component
const { logEvent, logIssue } = useContext(TrackerContext)

Внутри функции addToCart мы добавим следующую логику для проверки того, что не выбран допустимый размер:

const validSizes = product.options
      .filter((o) => o.id == 'option-size')
      .map((o) => o.values)[0]

    let pickedSized = validSizes.find((s) => {
      return selectedOptions.size == s.label.toLowerCase()
    })

    if (!pickedSized) {
      logIssue({
        name: 'Product added without a size',
        data: {
          product_id: product.id,
          added_date: new Date(),
          available_options: validSizes,
        },
      })
    }

Ключевая часть кода — это последний IF: когда мы понимаем, что допустимый размер не выбран, мы вызываем функцию logIssue, которую получили из предыдущего вызова useContext.

А для события мы углубимся в ту же функцию и добавим:

logEvent({
        name: 'product_added',
        data: {
          id: product.id,
          date_added: new Date(),
        },
      })

Это всё, что нам нужно; затем мы можем перейти в OpenReplay, найти нашу запись сессии и просмотреть раздел Events:

Панель пользовательских событий

И если нам нужны подробности, мы можем нажать на ссылку «DETAILS», которая появляется при наведении курсора на одну из строк:

Подробности пользовательского события

Это те подробности, которые мы записали при добавлении товара.

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

Если у вас возникнут какие-либо проблемы с пользовательскими событиями в вашем проекте, свяжитесь с нами в нашем сообществе Slack и задайте вопросы нашим разработчикам напрямую!