내부 이벤트 추적 빠른 시작

더 효율적이고 확장 가능하며 통합된 추적 API를 제공하기 위해 GitLab은 기존의 RedisHLL 및 Snowplow 추적을 폐기하고 있습니다. 대신, 새로운 track_event (백엔드) 및 trackEvent (프론트엔드) 메서드를 구현하고 있습니다. 이 접근 방식으로 RedisHLL 카운터를 업데이트하고 기존 구현에 대해 걱정 없이 Snowplow 이벤트를 보낼 수 있습니다.

내부 이벤트 추적을 코드에 적용하려면 다음 세 가지 작업을 수행해야 합니다.

  1. 이벤트 정의
  2. 하나 이상의 메트릭 정의
  3. 이벤트 트리거

이벤트 및 메트릭 정의

이벤트 및/또는 메트릭 정의를 만들려면 gitlab 디렉터리의 internal_events 생성기를 사용하십시오.

ruby scripts/internal_events/cli.rb

이 CLI를 사용하면 특정 사용 사례에 기반한 올바른 정의 파일을 생성해 주고, 이에 대한 코드 예제와 테스트를 제공합니다.

이벤트는 <action>_<target_of_action>_<where/when> 형식으로 명명되어야 합니다. 유효한 예시는 create_ci_build 또는 click_previous_blame_on_blob_page 입니다.

이벤트 트리거

이벤트를 트리거하여 메트릭을 업데이트하는 방법은 백엔드와 프론트엔드에서 약간 다릅니다. 아래 관련 섹션을 참조하십시오.

백엔드 추적

이벤트를 트리거하려면 Gitlab::InternalEventsTracking 모듈에서 track_internal_event 메서드를 호출하여 원하는 인수를 전달하십시오.

include Gitlab::InternalEventsTracking

track_internal_event(
  "create_ci_build",
  user: user,
  namespace: namespace,
  project: project
)

이 메서드는 자동으로 이벤트 create_ci_build에 관련된 모든 RedisHLL 메트릭을 증가시키고, 명명된 인수 및 표준 컨텍스트(SaaS 전용)와 함께 해당 Snowplow 이벤트를 보냅니다. 또한 이벤트를 트리거하는 클래스의 이름은 Snowplow 이벤트의 category 속성에 저장됩니다.

unique: project.id와 같이 고유 속성이있는 메트릭을 정의했다면 project 인수를 제공해야 합니다.

가능한 한 많은 수의 user, namespace, project를 작성하는 것이 좋으며, 이는 데이터 품질을 높이고 향후 메트릭을 정의하기 쉽도록 만듭니다.

namespace를 제공하지 않고 project만 제공하는 경우, 이벤트의 namespaceproject.namespace가 사용됩니다.

명시적으로 category를 지정하거나 전혀 제공하려는 경우 모듈을 사용하는 대신 InternalEvents.track_event 메서드를 직접 호출할 수 있습니다.

한 기능이 여러 네임스페이스를 통해 활성화되는 경우 이 기능이 왜 활성화되는지 추적해야 할 수도 있으므로 선택적으로 feature_enabled_by_namespace_ids 매개변수를 사용하여 네임스페이스 ID 배열을 전달할 수 있습니다.

track_internal_event(
  ...
  feature_enabled_by_namespace_ids: [namespace_one.id, namespace_two.id]
)

추가 속성

추적 이벤트 시 추가 속성을 전달할 수 있습니다. 이 속성은 주어진 이벤트와 관련된 추가 데이터를 저장하는 데 사용할 수 있습니다. label (문자열), property (문자열) 및 value (숫자) 키를 사용하여 최대 세 가지 추가 속성을 전송할 수 있습니다.

추가 속성은 additional_properties 해시를 #track_event 호출에 포함시켜 전달됩니다.

track_internal_event(
  "create_ci_build",
  user: user,
  additional_properties: {
    label: 'scheduled',
    value: 20
  }
)

컨트롤러 및 API 도우미

컨트롤러에 대해 내부 이벤트를 추적하기위한 ProductAnalyticsTracking 도우미 모듈이 있으며, #track_internal_event를 호출하여 특정 컨트롤러 작업에 대한 내부 이벤트를 추적할 수 있습니다.

class Projects::PipelinesController < Projects::ApplicationController
  include ProductAnalyticsTracking
  
  track_internal_event :charts, name: 'visit_charts_on_ci_cd_pipelines', conditions: -> { should_track_ci_cd_pipelines? }
  
  def charts
    ...
  end
  
  private
  
  def should_track_ci_cd_pipelines?
    params[:chart].blank? || params[:chart] == 'pipelines'
  end
end

이벤트를 추적할 수 있도록 도우미가 현재 프로젝트와 네임스페이스를 얻을 수 있도록 컨트롤러 몸체에 이 두 메서드를 추가해야 합니다.

  private
  
  def tracking_namespace_source
    project.namespace
  end
  
  def tracking_project_source
    project
  end

또한 API 도우미도 있습니다.

track_event(
  event_name,
  user: current_user,
  namespace_id: namespace_id,
  project_id: project_id
)

프론트엔드 추적

프론트엔드 추적 호출은 현재 페이지의 컨텍스트에서 user.id, namespace.id, project.id 값을 자동으로 전달합니다.

extra, context, label, property, value와 같은 추가 속성을 전달해야 하는 경우 사용 안 함으로 표시된 snowplow 구현을 사용할 수 있습니다. 이 경우 내부 이벤트에 대한 피드백 이슈에 특정 사용 사례에 대해 알려주십시오.

Vue 컴포넌트

Vue 컴포넌트에서 추적을 사용하려면 Vue mixin을 사용할 수 있습니다.

Vue 컴포넌트 추적을 구현하려면:

  1. InternalEvents 라이브러리를 가져와 mixin 메서드를 호출합니다.

      import { InternalEvents } from '~/tracking';
      const trackingMixin = InternalEvents.mixin();
    
  2. 컴포넌트에서 mixin을 사용합니다.

    export default {
      mixins: [trackingMixin],
         
      data() {
        return {
          expanded: false,
        };
      },
    };
    
  3. trackEvent 메서드를 호출합니다. 추적 옵션을 두 번째 매개변수로 전달할 수 있습니다.

    this.trackEvent('click_previous_blame_on_blob_page');
    

    또는 템플릿에서 trackEvent 메서드를 사용할 수 있습니다.

    <template>
      <div>
        <button data-testid="toggle" @click="toggle">토글</button>
           
        <div v-if="expanded">
          <p>Hello world!</p>
          <button @click="trackEvent('click_previous_blame_on_blob_page')">다른 이벤트 추적</button>
        </div>
      </div>
    </template>
    

Raw JavaScript

임의의 프론트엔드 JavaScript 코드에서 이벤트를 직접 추적하기 위해 JavaScript 모듈이 제공됩니다. 이 모듈은 믹스인을 사용할 수없는 컴포넌트 컨텍스트 외부에서 사용할 수 있습니다.

import { InternalEvents } from '~/tracking';
InternalEvents.trackEvent('click_previous_blame_on_blob_page');

Data-event 속성

이 속성은 GitLab 내부 이벤트를 버튼에 대해 추적하려는 경우 JavaScript 코드를 클릭 핸들러에 작성할 필요가 없습니다. 대신 이벤트 값과 함께 data-event-tracking 속성을 추가하면 됩니다. 이는 HAML 뷰와 함께 사용할 수도 있습니다.

  <gl-button
    data-event-tracking="click_previous_blame_on_blob_page"
  >
   클릭
  </gl-button>

Haml

= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle',  data: { event_tracking: 'click_previous_blame_on_blob_page' }}) do

렌더링시 내부 이벤트

가끔 컴포넌트가 렌더링되거나 로드될 때 내부 이벤트를 보내고 싶을 때가 있습니다. 이러한 경우에는 data-event-tracking-load="true" 속성을 추가할 수 있습니다:

= render Pajamas::ButtonComponent.new(button_options: { data: { event_tracking_load: 'true', event_tracking: 'click_previous_blame_on_blob_page' } }) do
        = _("New project")

추가 속성

추가로, 추적 이벤트를 할 때 추가 속성을 전달할 수 있습니다. 특정 이벤트와 관련된 추가 데이터를 저장하는 데 사용할 수 있습니다. label (문자열), property (문자열), value (숫자) 키를 가진 추가 속성을 최대 3개까지 보낼 수 있습니다.

Vue 믹스인의 경우:

   this.trackEvent('click_view_runners_button', {
    label: 'group_runner_form',
    property: dynamicPropertyVar,
    value: 20
   });

원시 자바스크립트의 경우:

   InternalEvents.trackEvent('click_view_runners_button', {
    label: 'group_runner_form',
    property: dynamicPropertyVar,
    value: 20
   });

data-event 속성의 경우:

  <gl-button
    data-event-tracking="click_view_runners_button"
    data-event-label="group_runner_form"
    :data-event-property=dynamicPropertyVar
  >
   Click Me
  </gl-button>

Haml의 경우:

= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle',  data: { event_tracking: 'action', event_label: 'group_runner_form', event_property: dynamic_property_var, event_value: 2 }}) do

프론트엔드 테스트

코드 중 trackEvent 메서드를 사용하는 경우, 원시 자바스크립트이든 Vue 컴포넌트이든 useMockInternalEventsTracking 헬퍼 메서드를 사용하여 trackEvent가 호출되었는지 확인할 수 있습니다.

예를 들어, 아래 Vue 컴포넌트를 테스트해야 하는 경우,

<script>
import { GlButton } from '@gitlab/ui';
import { InternalEvents } from '~/tracking';
import { __ } from '~/locale';

export default {
  components: {
    GlButton,
  },
  mixins: [InternalEvents.mixin()],
  methods: {
    handleButtonClick() {
      // 애플리케이션 로직
      // 특정 이벤트 발생 시 추적 호출
      this.trackEvent('click_view_runners_button', {
        label: 'group_runner_form',
        property: 'property_value',
        value: 3,
      });
    },
  },
  i18n: {
    button1: __('샘플 버튼'),
  },
};
</script>
<template>
  <div style="display: flex; height: 90vh; align-items: center; justify-content: center">
    <gl-button class="sample-button" @click="handleButtonClick">
      {{ $options.i18n.button1 }}
    </gl-button>
  </div>
</template>

해당 컴포넌트에 대한 테스트 케이스는 아래와 같을 것입니다.

import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import DeleteApplication from '~/admin/applications/components/delete_application.vue';
import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper';

describe('DeleteApplication', () => {
  /** @type {import('helpers/vue_test_utils_helper').ExtendedWrapper} */
  let wrapper;
  
  const createComponent = () => {
    wrapper = shallowMountExtended(DeleteApplication);
  };
  
  beforeEach(() => {
    createComponent();
  });
  
  describe('sample button 1', () => {
    const { bindInternalEventDocument } = useMockInternalEventsTracking();
    it('sample button을 클릭했을 때 trackEvent 메서드를 호출해야 합니다', async () => {
      const { trackEventSpy } = bindInternalEventDocument(wrapper.element);
      
      await wrapper.find('.sample-button').vm.$emit('click');
      
      expect(trackEventSpy).toHaveBeenCalledWith(
        'click_view_runners_button',
        {
          label: 'group_runner_form',
          property: 'property_value',
          value: 3,
        },
        undefined,
      );
    });
  });
});

Vue/View 템플릿에서 추적 속성을 사용하는 경우,

<script>
import { GlButton } from '@gitlab/ui';
import { InternalEvents } from '~/tracking';
import { __ } from '~/locale';

export default {
  components: {
    GlButton,
  },
  mixins: [InternalEvents.mixin()],
  i18n: {
    button1: __('샘플 버튼'),
  },
};
</script>
<template>
  <div style="display: flex; height: 90vh; align-items: center; justify-content: center">
    <gl-button
      class="sample-button"
      data-event-tracking="click_view_runners_button"
      data-event-label="group_runner_form"
    >
      {{ $options.i18n.button1 }}
    </gl-button>
  </div>
</template>

해당 컴포넌트에 대한 테스트 케이스는 아래와 같을 것입니다.

import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import DeleteApplication from '~/admin/applications/components/delete_application.vue';
import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper';

describe('DeleteApplication', () => {
  /** @type {import('helpers/vue_test_utils_helper').ExtendedWrapper} */
  let wrapper;
  
  const createComponent = () => {
    wrapper = shallowMountExtended(DeleteApplication);
  };
  
  beforeEach(() => {
    createComponent();
  });
  
  describe('sample button', () => {
    const { bindInternalEventDocument } = useMockInternalEventsTracking();
    it('sample button을 클릭했을 때 trackEvent 메서드를 호출해야 합니다', () => {
      const { triggerEvent, trackEventSpy } = bindInternalEventDocument(wrapper.element);
      triggerEvent('.sample-button');
      expect(trackEventSpy).toHaveBeenCalledWith('click_view_runners_button', {
        label: 'group_runner_form',
      });
    });
  });
});