내부 이벤트 추적을 위한 빠른 시작

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

내부 이벤트 추적을 사용하도록 코드를 설치하려면 다음 세 가지를 수행해야 합니다:

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

이벤트 및 메트릭 정의

이벤트 및/또는 메트릭 정의를 생성하려면 gitlab 디렉토리에서 internal_events 생성기를 사용하세요:

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 속성에 저장됩니다.

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

user, namespace, project 등을 가능한 한 많이 채우는 것이 좋으며, 데이터 품질을 향상시키고 나중에 메트릭 정의를 쉽게 할 수 있습니다.

project가 제공되지만 namespace는 제공되지 않은 경우, 이벤트의 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(숫자)

이러한 세 속성의 임의의 명명 및 유형은 데이터 추출 프로세스의 제약으로 인한 것입니다. 원하는 데이터와 일치하지 않는 속성 이름을 사용하더라도 먼저 이러한 속성을 사용하는 것이 좋습니다. 특히 이러한 속성을 메트릭 필터로 사용하려는 경우에는 특히 중요합니다. 실제로 추적되는 데이터를 이벤트의 YAML 정의의description 속성을 사용하여 자세히 설명할 수 있습니다. 예를 들어, [create_ci_internal_pipeline.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/537ea367dab731e886e6040d8399c430fdb67ab7/config/events/create_ci_internal_pipeline.yml)을 참조하세요.

additional_properties:
  label:
    description: Pipeline 출처, : 푸시, 일정 또는 유사한 .
  property:
    description: 설정의 출처, : 리포지토리, auto_devops 또는 유사한 .

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

track_internal_event(
  "create_ci_build",
  user: user,
  additional_properties: {
    label: source, # 라벨은 파이프라인의 출처를 추적합니다
    property: config_source # 속성은 설정의 출처를 추적합니다
  }
)

세 개 이상의 추가 속성을 전달해야 하는 경우 사용자 정의 키를 사용하여 additional_properties 해시를 사용할 수 있습니다.

track_internal_event(
  "code_suggestion_accepted",
  user: user,
    label: editor_name,
    property: suggestion_type,
    value: suggestion_shown_duration
    lang: 'ruby',
    custom_key: 'custom_value'
  }
)

내장 속성에 추가 속성을 추가하십시오.

사용자 정의 규칙은 메트릭 필터로 사용할 수 없습니다.

컨트롤러 및 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
)

백엔드 테스트

내부 이벤트를 단순히 트리거하고 모든 관련 지표를 증가시키는 코드를 테스트할 때 internal_event_tracking 공유 예제를 사용할 수 있습니다.

it_behaves_like 'internal event tracking' do
  let(:event) { 'update_issue_severity' }
  let(:project) { issue.project }
  let(:user) { issue.author }
  let(:additional_properties) { { label: issue.issueable_severity } }
  subject(:service_action) { described_class.new(issue).execute }
end

다음 컨텍스트를 포함해야 합니다:

  • subject - 이벤트를 트리거하는 동작
  • event - 이벤트의 이름

선택적으로, 컨텍스트에는 다음을 포함할 수 있습니다:

  • user
  • project
  • namespace. 제공되지 않으면 project.namespace가 사용됩니다 (만약 project가 사용 가능한 경우).
  • category
  • additional_properties
  • event_attribute_overrides - 부모 컨텍스트에서 사용 가능한 속성을 재정의해야 하는 경우 사용됩니다. 예를 들어:
let(:event) { 'create_new_issue' }

it_behaves_like 'internal event tracking' do
  let(:event_attribute_overrides) { { event: 'create_new_milestone'} }

  subject(:service_action) { described_class.new(issue).save }
end

다음 레거시 옵션은 이제 사용이 중지되었습니다:

  • label
  • property
  • value

대신 additional_properties를 사용하는 것을 권장합니다.

구성 가능한 매처

단일 작업이 여러 번 이벤트를 트리거하거나, 여러 가지 다른 이벤트를 트리거하거나, 이벤트에 대해 일부 지표를 증가시키지만 다른 것은 증가시키지 않을 때 trigger_internal_eventsincrement_usage_metrics 매처를 사용할 수 있습니다.

 expect { subject }
  .to trigger_internal_events('web_ide_viewed')
  .with(user: user, project: project, namespace: namespace)
  .and increment_usage_metrics('counts.web_views')

trigger_internal_events 매처는 receive 매처와 동일한 체인 메서드를 수용합니다 (#once, #at_most 등). 기본적으로 제공된 이벤트가 한 번만 트리거되기를 기대합니다.

#with 체인 메서드는 다음 매개변수를 수용합니다:

  • user - 사용자 오브젝트
  • project - 프로젝트 오브젝트
  • namespace - 네임스페이스 오브젝트. 제공되지 않으면 project.namespace로 설정됩니다.
  • additional_properties - 해시. 이벤트와 함께 전송해야 하는 추가 속성. 예: { label: 'scheduled', value: 20 }
  • category - 문자열. 제공되지 않으면 이벤트를 트리거하는 객체의 클래스 이름으로 설정됩니다.

increment_usage_metrics 매처는 change 매처와 동일한 체인 메서드를 수용합니다 (#by, #from, #to 등). 기본적으로 제공된 메트릭이 한 번씩 증가하기를 기대합니다.

expect { subject }
  .to trigger_internal_events('web_ide_viewed')
  .with(user: user, project: project, namespace: namespace)
  .exactly(3).times

두 매처 모두 블록에서 작용하는 다른 매처와 조합할 수 있습니다(change 매처와 유사).

expect { subject }
  .to trigger_internal_events('mr_created')
    .with(user: user, project: project, category: category, additional_properties: { label: label } )
  .and increment_usage_metrics('counts.deployments')
    .at_least(:once)
  .and change { mr.notes.count }.by(1)

이벤트가 트리거되지 않았음을 테스트하려면 not_trigger_internal_events 매처를 사용할 수 있습니다. 메시지 체인을 수용하지 않습니다.

expect { subject }.to trigger_internal_events('mr_created')
    .with(user: user, project: project, namespace: namespace)
  .and increment_usage_metrics('counts.deployments')
  .and not_trigger_internal_events('pipeline_started')

또는 not_to 구문을 사용할 수 있습니다.

expect { subject }.not_to trigger_internal_events('mr_created', 'member_role_created')

프론트엔드 추적

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

extra, context, label, property, value와 같은 추가 속성을 전달해야 하는 경우 사용 중단된 스노우플로 구현을 사용할 수 있습니다. 이 경우, 특정 사용 사례에 대해 저희에게 내부 이벤트 피드백 이슈에서 알려주세요.

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">Toggle</button>
    
        <div v-if="expanded">
          <p>Hello world!</p>
          <button @click="trackEvent('click_previous_blame_on_blob_page')">Track another event</button>
        </div>
      </div>
    </template>
    

Raw JavaScript

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

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

데이터 이벤트 속성

이 속성은 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’인 숫자로 최대 세 가지 추가 속성을 전달할 수 있습니다.

참고: 추가 속성으로 페이지 URL 또는 페이지 경로를 전달하지 마십시오. 각 이벤트에 대해 익명화된 페이지 URL을 이미 추적하고 있기 때문입니다. window.location에서 URL을 가져오면 프로젝트 및 네임스페이스 정보가 익명화되지 않습니다 문서화된 내용과 같습니다.

Vue Mixin에서:

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

Raw JavaScript에서:

   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
    data-event-additional='{"key1": "value1", "key2": "value2"}'
  >
   클릭
  </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, event_additional: '{"key1": "value1", "key2": "value2"}' }}) do

프론트엔드 테스트

코드에서 trackEvent 메서드를 사용하는 경우, 원시 JavaScript이든 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('샘플 버튼을 클릭했을 때 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('샘플 버튼을 클릭했을 때 trackEvent 메서드를 호출해야 함', () => {
      const { triggerEvent, trackEventSpy } = bindInternalEventDocument(wrapper.element);
      triggerEvent('.sample-button');
      expect(trackEventSpy).toHaveBeenCalledWith('click_view_runners_button', {
        label: 'group_runner_form',
      });
    });
  });
});

내부 이벤트 API 사용

GitLab 인스턴스에 연결된 다른 시스템에서 이벤트를 추적하기 위해 API를 사용할 수 있습니다. 자세한 정보는 사용 데이터 API 문서를 참조하세요.

다른 시스템에서의 내부 이벤트

GitLab 코드베이스 외에도, 아래 목록에 나열된 시스템에서는 내부 이벤트를 사용하고 있습니다.

  1. AI Gateway
  2. Switchboard