Active Support가 제공하는 instrumentation API를 통해 개발자는 다른 개발자들이 연결할 수 있는 hook을 제공할 수 있습니다. Rails 프레임워크 내에는 이러한 것들 이 여러 개 있습니다. 이 API를 사용하면 개발자들은 애플리케이션이나 다른 Ruby 코드 내에서 특정 이벤트가 발생할 때 알림을 받도록 선택할 수 있습니다.
예를 들어, Active Record 내에는 Active Record가 데이터베이스에 SQL 쿼리를 사용할 때마다 호출되는 hook 이 있습니다. 이 hook을 구독 하여 특정 작업 동안의 쿼리 수를 추적하는 데 사용할 수 있습니다. 컨트롤러의 액션 처리와 관련된 다른 hook 도 있습니다. 이는 예를 들어 특정 액션이 얼마나 오래 걸렸는지 추적하는 데 사용할 수 있습니다.
애플리케이션 내에서 자신만의 이벤트를 생성 하여 나중에 구독할 수도 있습니다.
어떤 알림이든 수신하려면 [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
Copy
만약 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
Copy
만약 정확한 경과 시간을 계산하기 위한 started
와 finished
의 정확성이 걱정된다면, [ActiveSupport::Notifications.monotonic_subscribe
][]를 사용하세요. 주어진 block은 위와 동일한 인자들을 받지만, started
와 finished
는 벽시계 시간 대신 정확한 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
Copy
정규식과 매칭되는 이벤트를 구독할 수도 있습니다. 이를 통해 한 번에 여러 이벤트를 구독할 수 있습니다. 다음은 ActionController
에서 모든 이벤트를 구독하는 방법입니다:
ActiveSupport :: Notifications . subscribe ( /action_controller/ ) do | event |
# 모든 ActionController 이벤트 검사
end
Copy
Ruby on Rails 프레임워크 내에는, 일반적인 이벤트들을 위한 다수의 hooks가 제공됩니다. 이러한 이벤트들과 그들의 payload는 아래에서 자세히 설명됩니다.
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"
}
Copy
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 # 데이터베이스 쿼리 시간(밀리초)
}
Copy
호출자에 의해 추가 키가 더해질 수 있습니다.
ActionController
는 payload에 어떤 특정 정보도 추가하지 않습니다. 모든 옵션이 payload로 전달됩니다.
{
status: 302 ,
location: "http://localhost:3000/posts/new" ,
request: < ActionDispatch :: Request : 0x00007ff1cb9bd7b8 >
}
Copy
Key
Value
:filter
action을 중단시킨 filter
{
filter: ":halting_filter"
}
Copy
Key
Value
:keys
허용되지 않은 키들
:context
다음 키들을 가진 Hash: :controller
, :action
, :params
, :request
Key
Value
:filename
파일명
:type
HTTP content type
:disposition
HTTP content disposition
{
filename: "subscribers.csv" ,
type: "text/csv" ,
disposition: "attachment" # 첨부파일
}
Copy
{
key: 'posts/1-dashboard-view'
}
Copy
{
key: 'posts/1-dashboard-view'
}
Copy
{
key: 'posts/1-dashboard-view'
}
Copy
{
key: 'posts/1-dashboard-view'
}
Copy
Key
Value
:middleware
미들웨어의 이름
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" }
}
Copy
Key
Value
:identifier
template의 전체 경로
:locals
template에 전달되는 local 변수들
{
identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb" ,
locals: { foo: "bar" }
}
Copy
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
}
Copy
Key
Value
:identifier
template의 전체 경로
{
identifier: "/Users/adam/projects/notifications/app/views/layouts/application.html.erb"
}
Copy
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 # 영향을 받은 행 수
}
Copy
쿼리가 트랜잭션 컨텍스트에서 실행되지 않는 경우, :transaction
은 nil
입니다.
이 이벤트는 config.active_record.action_on_strict_loading_violation
이 :log
로 설정된 경우에만 발생합니다.
Key
Value
:owner
strict_loading
이 활성화된 Model
:reflection
로드를 시도한 association의 Reflection
Key
Value
:record_count
인스턴스화된 레코드 수
:class_name
레코드의 클래스
{
record_count: 1 ,
class_name: "User"
}
Copy
이 이벤트는 transaction이 시작되었을 때 발생합니다.
Key
Value
:transaction
Transaction 객체
:connection
Connection 객체
Active Record는 필요할 때까지 실제 데이터베이스 transaction을 생성하지 않는다는 점에 유의하세요:
ActiveRecord :: Base . transaction do
# 블록 내부에 있지만 아직 아무 이벤트도 발생하지 않았습니다.
# 다음 라인에서 Active Record가 transaction을 시작합니다.
User . count # 여기서 이벤트가 발생합니다.
end
Copy
일반적인 중첩 호출은 새로운 transaction을 생성하지 않는다는 점을 기억하세요:
ActiveRecord :: Base . transaction do | t1 |
User . count # t1에 대한 이벤트를 발생시킴
ActiveRecord :: Base . transaction do | t2 |
# 다음 라인은 t2에 대한 이벤트를 발생시키지 않음
# 이 예제에서 실제 데이터베이스 transaction은 t1 뿐이기 때문
User . first . touch
end
end
Copy
하지만 만약 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
Copy
이 이벤트는 데이터베이스 트랜잭션이 완료될 때 발생합니다. 트랜잭션의 상태는
:outcome
키에서 확인할 수 있습니다.
키
값
:transaction
Transaction 객체
:outcome
:commit
, :rollback
, :restart
, 또는 :incomplete
:connection
Connection 객체
실제로는 transaction 객체로 많은 작업을 할 수 없지만, 데이터베이스 활동을 추적하는 데 여전히 도움이 될 수 있습니다. 예를 들어, transaction.uuid
를 추적하는 것으로 말이죠.
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
}
Copy
Key
Value
:mailer
Mailer 클래스의 이름
:action
Action
:args
인자들
{
mailer: "Notification" ,
action: "welcome_email" ,
args: []
}
Copy
Key
Value
:key
store에서 사용된 key
:store
store 클래스의 이름
:hit
이 읽기가 hit인지 여부
:super_operation
fetch
로 읽기가 수행된 경우 :fetch
Key
Value
:key
store에서 사용된 keys
:store
store 클래스의 이름
:hits
캐시 hit된 keys
:super_operation
fetch_multi
로 읽기가 수행된 경우 :fetch_multi
이 이벤트는 fetch
가 블록과 함께 호출될 때만 발생합니다.
Key
Value
:key
store에서 사용된 key
:store
store 클래스의 이름
fetch
에 전달된 옵션들은 store에 쓰기를 할 때 payload와 병합됩니다.
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
Copy
이 이벤트는 블록과 함께 fetch
가 호출될 때만 발생합니다.
키
값
:key
저장소에서 사용된 키
:store
저장소 클래스의 이름
fetch
에 전달된 옵션들은 payload와 병합됩니다.
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
Copy
Key
Value
:key
store에서 사용되는 key
:store
store 클래스의 이름
Cache store는 자신의 데이터를 추가할 수도 있습니다.
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
Copy
키
값
:key
store에 기록된 키와 값들
:store
store 클래스의 이름
키
값
:key
store에서 사용된 키
:store
store 클래스의 이름
:amount
증가량
{
key: "bottles-of-beer" ,
store: "ActiveSupport::Cache::RedisCacheStore" ,
amount: 99
}
Copy
키
값
:key
store에서 사용되는 키
:store
store 클래스의 이름
:amount
감소시킬 값
{
key: "bottles-of-beer" ,
store: "ActiveSupport::Cache::RedisCacheStore" ,
amount: 1
}
Copy
Key
Value
:key
store에서 사용된 key
:store
store 클래스의 이름
{
key: "복잡한-계산의-이름" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
Copy
Key
Value
:key
저장소에서 사용된 키들
:store
저장소 클래스의 이름
이 이벤트는 RedisCacheStore
, FileStore
, 또는 MemoryStore
를 사용할 때만 발생합니다.
Key
Value
:key
사용된 키 패턴
:store
저장소 클래스의 이름
{
key: "posts/*" ,
store: "ActiveSupport::Cache::RedisCacheStore"
}
Copy
이 이벤트는 MemoryStore
를 사용할 때만 발생합니다.
Key
Value
:store
스토어 클래스의 이름
:size
정리 전 캐시의 엔트리 개수
{
store: "ActiveSupport::Cache::MemoryStore" ,
size: 9001
}
Copy
이 이벤트는 MemoryStore
를 사용할 때만 발생합니다.
Key
Value
:store
store 클래스의 이름
:key
캐시의 목표 크기(바이트)
:from
정리(prune) 이전의 캐시 크기(바이트)
{
store: "ActiveSupport::Cache::MemoryStore" ,
key: 5000 ,
from: 9001
}
Copy
Key
Value
:key
store에서 사용되는 Key
:store
store 클래스의 이름
{
key: "name-of-complicated-computation" ,
store: "ActiveSupport::Cache::MemCacheStore"
}
Copy
Key
Value
:serializer
기본(의도된) serializer
:fallback
대체(실제) serializer
:serialized
직렬화된 문자열
:deserialized
역직렬화된 값
{
serializer: :json_allow_marshal ,
fallback: :marshal ,
serialized: " \x04\b { \x06 I \"\n Hello \x06 : \x06 ETI \"\n World \x06 ; \x00 T" ,
deserialized: { "Hello" => "World" },
}
Copy
Key
Value
:adapter
job을 처리하는 QueueAdapter 객체
:job
Job 객체
Key
Value
:adapter
job을 처리하는 QueueAdapter 객체
:job
Job 객체
Key
Value
:job
Job 객체
:adapter
job을 처리하는 QueueAdapter 객체
:error
재시도의 원인이 된 에러
:wait
재시도 지연 시간
Key
Value
:adapter
job을 처리하는 QueueAdapter 객체
:jobs
Job 객체의 배열
Key
Value
:adapter
job을 처리하는 QueueAdapter 객체
:job
Job 객체
Key
Value
:adapter
job을 처리하는 QueueAdapter 객체
:job
Job 객체
:db_runtime
데이터베이스 쿼리 실행에 소요된 시간(ms)
| Key | Value |
| ------------ | -------------------------------------- |
| :adapter
| job을 처리하는 QueueAdapter 객체 |
| :job
| Job 객체 |
| :error
| retry를 발생시킨 에러 |
Key
Value
:adapter
job을 처리하는 QueueAdapter 객체
:job
Job 객체
:error
discard를 발생시킨 에러
Key
Value
:channel_class
channel 클래스의 이름
:action
액션
:data
데이터의 해시
Key
Value
:channel_class
channel 클래스의 이름
:data
데이터의 해시
:via
Via
Key
Value
:channel_class
channel 클래스의 이름
Key
Value
:channel_class
channel 클래스의 이름
Key
Value
:broadcasting
명명된 broadcasting
:message
메시지의 해시
:coder
coder
Key
Value
:analyzer
분석기 이름 (예: ffprobe)
Key
Value
:key
보안 토큰
:service
서비스 이름
:checksum
무결성 보장을 위한 체크섬
Key
Value
:key
보안 토큰
:service
서비스 이름
Key
Value
:key
보안 토큰
:service
서비스 이름
:range
읽기를 시도한 바이트 범위
Key
Value
:key
보안 토큰
:service
서비스 이름
Key
Value
:key
보안 토큰
:service
서비스 이름
Key
Value
:prefix
키 접두사
:service
서비스 이름
| :key
| 보안 토큰 |
| :service
| 서비스의 이름 |
| :exist
| 파일이나 블롭의 존재 여부 |
Key
Value
:key
보안 토큰
:service
서비스의 이름
:url
생성된 URL
이 이벤트는 Google Cloud Storage 서비스를 사용할 때만 발생합니다.
Key
Value
:key
보안 토큰
:service
서비스의 이름
:content_type
HTTP Content-Type
필드
:disposition
HTTP Content-Disposition
필드
키
값
: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"
}
}
Copy
Key
Value
:initializer
config/initializers
에서 로드된 initializer의 경로
Key
Value
:message
deprecation 경고
:callstack
deprecation이 발생한 위치
:gem_name
deprecation을 보고하는 gem의 이름
:deprecation_horizon
deprecated된 동작이 제거될 버전
instrumentation 도중 예외가 발생하면, payload에 관련 정보가 포함됩니다.
Key
Value
:exception
두 요소로 구성된 배열. 예외 클래스 이름과 메시지
:exception_object
예외 객체
자신만의 이벤트를 추가하는 것도 쉽습니다. Active Support가 모든 복잡한 작업을 처리해줄 것입니다. 단순히 ActiveSupport::Notifications.instrument
을 name
, payload
, 그리고 블록과 함께 호출하기만 하면 됩니다.
블록이 반환된 후에 notification이 전송됩니다. Active Support가 시작 및 종료 시간을 생성하고,
instrumenter의 고유 ID를 추가합니다. instrument
호출에 전달된 모든 데이터는
payload에 포함됩니다.
다음은 예시입니다:
ActiveSupport :: Notifications . instrument "my.custom.event" , this: :data do
# 여기에 사용자 정의 작업을 작성하세요
end
Copy
이제 다음과 같이 이벤트를 리스닝할 수 있습니다:
ActiveSupport :: Notifications . subscribe "my.custom.event" do | name , started , finished , unique_id , data |
puts data . inspect # {:this=>:data}
end
Copy
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
Copy
이름, 시작, 종료, 고유 ID와 함께 페이로드 데이터는 subscribe 블록으로 전달됩니다.
Rails 컨벤션에 따라 자신만의 event를 정의해야 합니다. 형식은 다음과 같습니다: event.library
.
만약 애플리케이션이 Tweet을 보내고 있다면, tweet.twitter
라는 이름의 event를 생성해야 합니다.