rubyonrails.org에서 더 보기:

Active Support Instrumentation

Active Support는 Ruby 언어 확장, 유틸리티 및 기타 기능을 제공하는 Rails 핵심의 일부입니다. 여기에 포함된 기능 중 하나는 Rails 애플리케이션이나 프레임워크 자체 내에서 발생하는 특정 작업을 측정하는 데 사용할 수 있는 instrumentation API입니다. 하지만 이는 Rails에만 국한되지 않습니다. 필요한 경우 다른 Ruby 스크립트에서도 독립적으로 사용할 수 있습니다.

이 가이드에서는 Rails와 다른 Ruby 코드 내부의 이벤트를 측정하기 위해 Active Support의 instrumentation API를 사용하는 방법을 배우게 됩니다.

이 가이드를 읽은 후에는 다음 내용을 알게 될 것입니다:

  • Instrumentation이 제공할 수 있는 것들
  • Hook에 subscriber를 추가하는 방법
  • Instrumentation을 위한 Rails 프레임워크 내부의 hook들
  • 사용자 정의 instrumentation 구현을 만드는 방법

1 Instrumentation 소개

Active Support가 제공하는 instrumentation API를 통해 개발자는 다른 개발자들이 연결할 수 있는 hook을 제공할 수 있습니다. Rails 프레임워크 내에는 이러한 것들이 여러 개 있습니다. 이 API를 사용하면 개발자들은 애플리케이션이나 다른 Ruby 코드 내에서 특정 이벤트가 발생할 때 알림을 받도록 선택할 수 있습니다.

예를 들어, Active Record 내에는 Active Record가 데이터베이스에 SQL 쿼리를 사용할 때마다 호출되는 hook이 있습니다. 이 hook을 구독하여 특정 작업 동안의 쿼리 수를 추적하는 데 사용할 수 있습니다. 컨트롤러의 액션 처리와 관련된 다른 hook도 있습니다. 이는 예를 들어 특정 액션이 얼마나 오래 걸렸는지 추적하는 데 사용할 수 있습니다.

애플리케이션 내에서 자신만의 이벤트를 생성하여 나중에 구독할 수도 있습니다.

2 이벤트 구독하기

어떤 알림이든 수신하려면 [ActiveSupport::Notifications.subscribe][]를 블록과 함께 사용하세요. 블록이 받는 인수의 수에 따라 다른 데이터를 받게 됩니다.

이벤트를 구독하는 첫 번째 방법은 단일 인수를 가진 블록을 사용하는 것입니다. 이 인수는 [ActiveSupport::Notifications::Event][]의 인스턴스가 될 것입니다.

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |event|
  event.name        # => "process_action.action_controller" 
  event.duration    # => 10 (밀리초 단위)
  event.allocations # => 1826
  event.payload     # => {:extra=>information}

  Rails.logger.info "#{event} 수신완료!"
end

만약 Event 객체에 기록된 모든 데이터가 필요하지 않다면, 다음 5개의 인자를 받는 블록을 지정할 수도 있습니다:

  • 이벤트의 이름
  • 시작 시간
  • 종료 시간
  • 이벤트를 발생시킨 instrumenter의 고유 ID
  • 이벤트의 payload
ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, started, finished, unique_id, payload|
  # 사용자 정의 코드
  Rails.logger.info "#{name} 수신됨! (시작: #{started}, 종료: #{finished})" # process_action.action_controller 수신됨! (시작: 2019-05-05 13:43:57 -0800, 종료: 2019-05-05 13:43:58 -0800)
end

만약 정확한 경과 시간을 계산하기 위한 startedfinished의 정확성이 걱정된다면, [ActiveSupport::Notifications.monotonic_subscribe][]를 사용하세요. 주어진 block은 위와 동일한 인자들을 받지만, startedfinished는 벽시계 시간 대신 정확한 monotonic 시간값을 가지게 됩니다.

ActiveSupport::Notifications.monotonic_subscribe "process_action.action_controller" do |name, started, finished, unique_id, payload|
  # 사용자 정의 코드
  duration = finished - started # 1560979.429234 - 1560978.425334
  Rails.logger.info "#{name} 수신됨! (duration: #{duration})" # process_action.action_controller 수신됨! (duration: 1.0039)
end

정규식과 매칭되는 이벤트를 구독할 수도 있습니다. 이를 통해 한 번에 여러 이벤트를 구독할 수 있습니다. 다음은 ActionController에서 모든 이벤트를 구독하는 방법입니다:

ActiveSupport::Notifications.subscribe(/action_controller/) do |event|
  # 모든 ActionController 이벤트 검사
end

3 Rails Framework Hooks

Ruby on Rails 프레임워크 내에는, 일반적인 이벤트들을 위한 다수의 hooks가 제공됩니다. 이러한 이벤트들과 그들의 payload는 아래에서 자세히 설명됩니다.

3.1 Action Controller

3.1.1 start_processing.action_controller

Key Value
:controller 컨트롤러 이름
:action 액션
:request ActionDispatch::Request 객체
:params 필터링되지 않은 파라미터들의 Hash
:headers Request 헤더
:format html/js/json/xml 등
:method HTTP request 메소드
:path Request 경로
{
  controller: "PostsController",
  action: "new", 
  params: { "action" => "new", "controller" => "posts" },
  headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
  format: :html,
  method: "GET",
  path: "/posts/new"
}

3.1.2 process_action.action_controller

Key Value
:controller controller 이름
:action action
:params 필터링되지 않은 request parameter의 Hash
:headers Request header
:format html/js/json/xml 등
:method HTTP request verb
:path Request path
:request ActionDispatch::Request 객체
:response ActionDispatch::Response 객체
:status HTTP 상태 코드
:view_runtime view에서 소비된 시간(ms)
:db_runtime 데이터베이스 쿼리 실행에 소비된 시간(ms)
{
  controller: "PostsController",
  action: "index", 
  params: {"action" => "index", "controller" => "posts"},
  headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
  format: :html,
  method: "GET",
  path: "/posts",
  request: #<ActionDispatch::Request:0x00007ff1cb9bd7b8>,
  response: #<ActionDispatch::Response:0x00007f8521841ec8>,
  status: 200,
  view_runtime: 46.848, # 뷰 렌더링 시간(밀리초)
  db_runtime: 0.157 # 데이터베이스 쿼리 시간(밀리초)
}

3.1.3 send_file.action_controller

:path 파일의 전체 경로

호출자에 의해 추가 키가 더해질 수 있습니다.

3.1.4 send_data.action_controller

ActionController는 payload에 어떤 특정 정보도 추가하지 않습니다. 모든 옵션이 payload로 전달됩니다.

3.1.5 redirect_to.action_controller

:status HTTP 응답 코드
:location 리다이렉트할 URL
:request ActionDispatch::Request 객체
{
  status: 302,
  location: "http://localhost:3000/posts/new",
  request: <ActionDispatch::Request:0x00007ff1cb9bd7b8>
} 

3.1.6 halted_callback.action_controller

Key Value
:filter action을 중단시킨 filter
{
  filter: ":halting_filter"
}

3.1.7 unpermitted_parameters.action_controller

Key Value
:keys 허용되지 않은 키들
:context 다음 키들을 가진 Hash: :controller, :action, :params, :request

3.1.8 send_stream.action_controller

Key Value
:filename 파일명
:type HTTP content type
:disposition HTTP content disposition
{
  filename: "subscribers.csv",
  type: "text/csv",
  disposition: "attachment" # 첨부파일
}

3.2 Action Controller: 캐싱

3.2.1 write_fragment.action_controller

Key Value
:key 완전한 key
{
  key: 'posts/1-dashboard-view'
}

3.2.2 read_fragment.action_controller

:key 완전한 키
{
  key: 'posts/1-dashboard-view'
}

3.2.3 expire_fragment.action_controller

:key 완전한 키
{
  key: 'posts/1-dashboard-view'
}

3.2.4 exist_fragment?.action_controller

:key 전체 키
{
  key: 'posts/1-dashboard-view'
}

3.3 Action Dispatch

3.3.1 process_middleware.action_dispatch

Key Value
:middleware 미들웨어의 이름

3.3.2 redirect.action_dispatch

Key Value
:status HTTP 응답 코드
:location 리다이렉트할 URL
:request ActionDispatch::Request 객체

3.3.3 request.action_dispatch

Key Value
:request ActionDispatch::Request 객체

3.4 Action View

3.4.1 render_template.action_view

Key Value
:identifier template의 전체 경로
:layout 적용 가능한 layout
:locals template에 전달되는 로컬 변수들
{
  identifier: "/Users/adam/projects/notifications/app/views/posts/index.html.erb",
  layout: "layouts/application", 
  locals: { foo: "bar" }
}

3.4.2 render_partial.action_view

Key Value
:identifier template의 전체 경로
:locals template에 전달되는 local 변수들
{
  identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb",
  locals: { foo: "bar" }
}

3.4.3 render_collection.action_view

Key Value
:identifier template까지의 전체 경로
:count collection의 크기
:cache_hits cache에서 가져온 partial의 수

:cache_hits key는 collection이 cached: true로 렌더링된 경우에만 포함됩니다.

{
  identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb",
  count: 3,
  cache_hits: 0
}

3.4.4 render_layout.action_view

Key Value
:identifier template의 전체 경로
{
  identifier: "/Users/adam/projects/notifications/app/views/layouts/application.html.erb"
}

3.5 Active Record

3.5.1 sql.active_record

Key Value
:sql SQL 구문
:name 작업의 이름
:connection Connection 객체
:transaction 현재 transaction (있는 경우)
:binds Bind 파라미터
:type_casted_binds Typecast된 bind 파라미터
:statement_name SQL Statement 이름
:async 쿼리가 비동기적으로 로드되면 true
:cached 캐시된 쿼리가 사용되면 true가 추가됨
:row_count 쿼리에 의해 반환된 행의 수
:affected_rows 쿼리에 의해 영향받은 행의 수

Adapter들은 자신만의 데이터를 추가할 수 있습니다.

{
  sql: "SELECT \"posts\".* FROM \"posts\" ", # SQL 쿼리문
  name: "Post Load", # 수행된 작업 이름
  connection: <ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00007f9f7a838850>, # 데이터베이스 연결 어댑터 
  transaction: <ActiveRecord::ConnectionAdapters::RealTransaction:0x0000000121b5d3e0> # 트랜잭션 정보
  binds: [<ActiveModel::Attribute::WithCastValue:0x00007fe19d15dc00>], # SQL 바인딩 파라미터
  type_casted_binds: [11], # 타입 캐스팅된 바인딩 값들
  statement_name: nil, # 준비된 구문의 이름
  row_count: 5, # 조회된 행 수
  affected_rows: 0 # 영향을 받은 행 수
}

쿼리가 트랜잭션 컨텍스트에서 실행되지 않는 경우, :transactionnil입니다.

3.5.2 strict_loading_violation.active_record

이 이벤트는 config.active_record.action_on_strict_loading_violation:log로 설정된 경우에만 발생합니다.

Key Value
:owner strict_loading이 활성화된 Model
:reflection 로드를 시도한 association의 Reflection

3.5.3 instantiation.active_record

Key Value
:record_count 인스턴스화된 레코드 수
:class_name 레코드의 클래스
{
  record_count: 1,
  class_name: "User"
}

3.5.4 start_transaction.active_record

이 이벤트는 transaction이 시작되었을 때 발생합니다.

Key Value
:transaction Transaction 객체
:connection Connection 객체

Active Record는 필요할 때까지 실제 데이터베이스 transaction을 생성하지 않는다는 점에 유의하세요:

ActiveRecord::Base.transaction do
  # 블록 내부에 있지만 아직 아무 이벤트도 발생하지 않았습니다.

  # 다음 라인에서 Active Record가 transaction을 시작합니다.
  User.count # 여기서 이벤트가 발생합니다.
end

일반적인 중첩 호출은 새로운 transaction을 생성하지 않는다는 점을 기억하세요:

ActiveRecord::Base.transaction do |t1|
  User.count # t1에 대한 이벤트를 발생시킴
  ActiveRecord::Base.transaction do |t2|
    # 다음 라인은 t2에 대한 이벤트를 발생시키지 않음
    # 이 예제에서 실제 데이터베이스 transaction은 t1 뿐이기 때문
    User.first.touch
  end
end

하지만 만약 requires_new: true가 전달되면, nested transaction에 대한 이벤트도 얻게 됩니다. 이것은 내부적으로 savepoint일 수 있습니다:

ActiveRecord::Base.transaction do |t1|
  User.count # t1에 대한 이벤트를 발생시킵니다.
  ActiveRecord::Base.transaction(requires_new: true) do |t2|
    User.first.touch # t2에 대한 이벤트를 발생시킵니다.
  end
end

3.5.5 transaction.active_record

이 이벤트는 데이터베이스 트랜잭션이 완료될 때 발생합니다. 트랜잭션의 상태는 :outcome 키에서 확인할 수 있습니다.

:transaction Transaction 객체
:outcome :commit, :rollback, :restart, 또는 :incomplete
:connection Connection 객체

실제로는 transaction 객체로 많은 작업을 할 수 없지만, 데이터베이스 활동을 추적하는 데 여전히 도움이 될 수 있습니다. 예를 들어, transaction.uuid를 추적하는 것으로 말이죠.

3.6 Action Mailer

3.6.1 deliver.action_mailer

Key Value
:mailer mailer 클래스의 이름
:message_id Mail gem에서 생성된 메시지의 ID
:subject 메일의 제목
:to 메일의 받는 주소
:from 메일의 보내는 주소
:bcc 메일의 BCC 주소
:cc 메일의 CC 주소
:date 메일의 날짜
:mail 인코딩된 형태의 메일
:perform_deliveries 이 메시지의 전송 여부
{
  mailer: "Notification",
  message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail",
  subject: "Rails Guides",
  to: ["users@rails.com", "dhh@rails.com"],
  from: ["me@rails.com"],
  date: Sat, 10 Mar 2012 14:18:09 +0100,
  mail: "...", # 간단히 하기 위해 생략
  perform_deliveries: true 
}

3.6.2 process.action_mailer

Key Value
:mailer Mailer 클래스의 이름
:action Action
:args 인자들
{
  mailer: "Notification",
  action: "welcome_email", 
  args: []
}

3.7 Active Support: Caching

3.7.1 cache_read.active_support

Key Value
:key store에서 사용된 key
:store store 클래스의 이름
:hit 이 읽기가 hit인지 여부
:super_operation fetch로 읽기가 수행된 경우 :fetch

3.7.2 cache_read_multi.active_support

Key Value
:key store에서 사용된 keys
:store store 클래스의 이름
:hits 캐시 hit된 keys
:super_operation fetch_multi로 읽기가 수행된 경우 :fetch_multi

3.7.3 cache_generate.active_support

이 이벤트는 fetch가 블록과 함께 호출될 때만 발생합니다.

Key Value
:key store에서 사용된 key
:store store 클래스의 이름

fetch에 전달된 옵션들은 store에 쓰기를 할 때 payload와 병합됩니다.

{
  key: "name-of-complicated-computation",
  store: "ActiveSupport::Cache::MemCacheStore"
}

3.7.4 cache_fetch_hit.active_support

이 이벤트는 블록과 함께 fetch가 호출될 때만 발생합니다.

:key 저장소에서 사용된 키
:store 저장소 클래스의 이름

fetch에 전달된 옵션들은 payload와 병합됩니다.

{
  key: "name-of-complicated-computation",
  store: "ActiveSupport::Cache::MemCacheStore"  
}

3.7.5 cache_write.active_support

Key Value
:key store에서 사용되는 key
:store store 클래스의 이름

Cache store는 자신의 데이터를 추가할 수도 있습니다.

{
  key: "name-of-complicated-computation", 
  store: "ActiveSupport::Cache::MemCacheStore"
}

3.7.6 cache_write_multi.active_support

:key store에 기록된 키와 값들
:store store 클래스의 이름

3.7.7 cache_increment.active_support

:key store에서 사용된 키
:store store 클래스의 이름
:amount 증가량
{
  key: "bottles-of-beer", 
  store: "ActiveSupport::Cache::RedisCacheStore",
  amount: 99
}

3.7.8 cache_decrement.active_support

:key store에서 사용되는 키
:store store 클래스의 이름
:amount 감소시킬 값
{
  key: "bottles-of-beer",
  store: "ActiveSupport::Cache::RedisCacheStore",
  amount: 1
}

3.7.9 cache_delete.active_support

Key Value
:key store에서 사용된 key
:store store 클래스의 이름
{
  key: "복잡한-계산의-이름",
  store: "ActiveSupport::Cache::MemCacheStore"
}

3.7.10 cache_delete_multi.active_support

Key Value
:key 저장소에서 사용된 키들
:store 저장소 클래스의 이름

3.7.11 cache_delete_matched.active_support

이 이벤트는 RedisCacheStore, FileStore, 또는 MemoryStore를 사용할 때만 발생합니다.

Key Value
:key 사용된 키 패턴
:store 저장소 클래스의 이름
{
  key: "posts/*", 
  store: "ActiveSupport::Cache::RedisCacheStore"
}

3.7.12 cache_cleanup.active_support

이 이벤트는 MemoryStore를 사용할 때만 발생합니다.

Key Value
:store 스토어 클래스의 이름
:size 정리 전 캐시의 엔트리 개수
{
  store: "ActiveSupport::Cache::MemoryStore",
  size: 9001
}

3.7.13 cache_prune.active_support

이 이벤트는 MemoryStore를 사용할 때만 발생합니다.

Key Value
:store store 클래스의 이름
:key 캐시의 목표 크기(바이트)
:from 정리(prune) 이전의 캐시 크기(바이트)
{
  store: "ActiveSupport::Cache::MemoryStore",
  key: 5000,
  from: 9001
}

3.7.14 cache_exist?.active_support

Key Value
:key store에서 사용되는 Key
:store store 클래스의 이름
{
  key: "name-of-complicated-computation", 
  store: "ActiveSupport::Cache::MemCacheStore"
}

3.8 Active Support: Messages

3.8.1 message_serializer_fallback.active_support

Key Value
:serializer 기본(의도된) serializer
:fallback 대체(실제) serializer
:serialized 직렬화된 문자열
:deserialized 역직렬화된 값
{
  serializer: :json_allow_marshal,
  fallback: :marshal,
  serialized: "\x04\b{\x06I\"\nHello\x06:\x06ETI\"\nWorld\x06;\x00T", 
  deserialized: { "Hello" => "World" },
}

3.9 Active Job

3.9.1 enqueue_at.active_job

Key Value
:adapter job을 처리하는 QueueAdapter 객체
:job Job 객체

3.9.2 enqueue.active_job

Key Value
:adapter job을 처리하는 QueueAdapter 객체
:job Job 객체

3.9.3 enqueue_retry.active_job

Key Value
:job Job 객체
:adapter job을 처리하는 QueueAdapter 객체
:error 재시도의 원인이 된 에러
:wait 재시도 지연 시간

3.9.4 enqueue_all.active_job

Key Value
:adapter job을 처리하는 QueueAdapter 객체
:jobs Job 객체의 배열

3.9.5 perform_start.active_job

Key Value
:adapter job을 처리하는 QueueAdapter 객체
:job Job 객체

3.9.6 perform.active_job

Key Value
:adapter job을 처리하는 QueueAdapter 객체
:job Job 객체
:db_runtime 데이터베이스 쿼리 실행에 소요된 시간(ms)

3.9.7 retry_stopped.active_job

| Key | Value |

| ------------ | -------------------------------------- | | :adapter | job을 처리하는 QueueAdapter 객체 | | :job | Job 객체 | | :error | retry를 발생시킨 에러 |

3.9.8 discard.active_job

Key Value
:adapter job을 처리하는 QueueAdapter 객체
:job Job 객체
:error discard를 발생시킨 에러

3.10 Action Cable

3.10.1 perform_action.action_cable

Key Value
:channel_class channel 클래스의 이름
:action 액션
:data 데이터의 해시

3.10.2 transmit.action_cable

Key Value
:channel_class channel 클래스의 이름
:data 데이터의 해시
:via Via

3.10.3 transmit_subscription_confirmation.action_cable

Key Value
:channel_class channel 클래스의 이름

3.10.4 transmit_subscription_rejection.action_cable

Key Value
:channel_class channel 클래스의 이름

3.10.5 broadcast.action_cable

Key Value
:broadcasting 명명된 broadcasting
:message 메시지의 해시
:coder coder

3.11 Active Storage

3.11.1 preview.active_storage

Key Value
:key 보안 토큰

3.11.2 transform.active_storage

3.11.3 analyze.active_storage

Key Value
:analyzer 분석기 이름 (예: ffprobe)

3.12 Active Storage: Storage Service

3.12.1 service_upload.active_storage

Key Value
:key 보안 토큰
:service 서비스 이름
:checksum 무결성 보장을 위한 체크섬

3.12.2 service_streaming_download.active_storage

Key Value
:key 보안 토큰
:service 서비스 이름

3.12.3 service_download_chunk.active_storage

Key Value
:key 보안 토큰
:service 서비스 이름
:range 읽기를 시도한 바이트 범위

3.12.4 service_download.active_storage

Key Value
:key 보안 토큰
:service 서비스 이름

3.12.5 service_delete.active_storage

Key Value
:key 보안 토큰
:service 서비스 이름

3.12.6 service_delete_prefixed.active_storage

Key Value
:prefix 키 접두사
:service 서비스 이름

3.12.7 service_exist.active_storage

Key Value

| :key | 보안 토큰 | | :service | 서비스의 이름 | | :exist | 파일이나 블롭의 존재 여부 |

3.12.8 service_url.active_storage

Key Value
:key 보안 토큰
:service 서비스의 이름
:url 생성된 URL

3.12.9 service_update_metadata.active_storage

이 이벤트는 Google Cloud Storage 서비스를 사용할 때만 발생합니다.

Key Value
:key 보안 토큰
:service 서비스의 이름
:content_type HTTP Content-Type 필드
:disposition HTTP Content-Disposition 필드

3.13 Action Mailbox

3.13.1 process.action_mailbox

:mailbox ActionMailbox::Base를 상속받는 Mailbox 클래스의 인스턴스
:inbound_email 처리 중인 수신 이메일에 대한 데이터를 포함한 Hash
{
  mailbox: #<RepliesMailbox:0x00007f9f7a8388>,
  inbound_email: {
    id: 1,
    message_id: "0CB459E0-0336-41DA-BC88-E6E28C697DDB@37signals.com", 
    status: "processing"
  }
}

3.14 Railties

3.14.1 load_config_initializer.railties

Key Value
:initializer config/initializers에서 로드된 initializer의 경로

3.15 Rails

3.15.1 deprecation.rails

Key Value
:message deprecation 경고
:callstack deprecation이 발생한 위치
:gem_name deprecation을 보고하는 gem의 이름
:deprecation_horizon deprecated된 동작이 제거될 버전

4 Exceptions

instrumentation 도중 예외가 발생하면, payload에 관련 정보가 포함됩니다.

Key Value
:exception 두 요소로 구성된 배열. 예외 클래스 이름과 메시지
:exception_object 예외 객체

5 Creating Custom Events

자신만의 이벤트를 추가하는 것도 쉽습니다. Active Support가 모든 복잡한 작업을 처리해줄 것입니다. 단순히 ActiveSupport::Notifications.instrumentname, payload, 그리고 블록과 함께 호출하기만 하면 됩니다. 블록이 반환된 후에 notification이 전송됩니다. Active Support가 시작 및 종료 시간을 생성하고, instrumenter의 고유 ID를 추가합니다. instrument 호출에 전달된 모든 데이터는 payload에 포함됩니다.

다음은 예시입니다:

ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
  # 여기에 사용자 정의 작업을 작성하세요
end

이제 다음과 같이 이벤트를 리스닝할 수 있습니다:

ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, finished, unique_id, data|
  puts data.inspect # {:this=>:data}
end

instrument를 블록 없이 호출할 수도 있습니다. 이를 통해 instrumentation 인프라를 다른 메시징 용도로 활용할 수 있습니다.

ActiveSupport::Notifications.instrument "my.custom.event", this: :data

ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, finished, unique_id, data|
  puts data.inspect # {:this=>:data}
end

이름, 시작, 종료, 고유 ID와 함께 페이로드 데이터는 subscribe 블록으로 전달됩니다.

Rails 컨벤션에 따라 자신만의 event를 정의해야 합니다. 형식은 다음과 같습니다: event.library. 만약 애플리케이션이 Tweet을 보내고 있다면, tweet.twitter라는 이름의 event를 생성해야 합니다.



맨 위로