Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Telemetry:add app-client-configuration-change event #2977

Merged
merged 3 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/datadog/core/telemetry/client.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require_relative 'emitter'
require_relative 'heartbeat'
require_relative '../utils/forking'
Expand Down Expand Up @@ -65,6 +67,13 @@ def integrations_change!
@emitter.request(:'app-integrations-change')
end

# Report configuration changes caused by Dynamic Configuration.
def dynamic_configuration_change!(changes)
marcotc marked this conversation as resolved.
Show resolved Hide resolved
return if !@enabled || forked?

@emitter.request('app-client-configuration-change', data: { changes: changes, origin: 'remote_config' })
end

private

def heartbeat!
Expand Down
6 changes: 4 additions & 2 deletions lib/datadog/core/telemetry/emitter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ def initialize(http_transport: Datadog::Core::Telemetry::Http::Transport.new)

# Retrieves and emits a TelemetryRequest object based on the request type specified
# @param request_type [String] the type of telemetry request to collect data for
def request(request_type)
# @param data [Object] arbitrary object to be passed to the respective `request_type` handler
def request(request_type, data: nil)
begin
request = Datadog::Core::Telemetry::Event.new.telemetry_request(
request_type: request_type,
seq_id: self.class.sequence.next
seq_id: self.class.sequence.next,
data: data,
).to_h
@http_transport.request(request_type: request_type.to_s, payload: request.to_json)
rescue StandardError => e
Expand Down
23 changes: 19 additions & 4 deletions lib/datadog/core/telemetry/event.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# frozen_string_literal: true

require_relative 'collector'
require_relative 'v1/app_event'
require_relative 'v1/telemetry_request'
require_relative 'v2/app_client_configuration_change'

module Datadog
module Core
Expand All @@ -9,7 +12,7 @@ module Telemetry
class Event
include Telemetry::Collector

API_VERSION = 'v1'.freeze
API_VERSION = 'v1'

attr_reader \
:api_version
Expand All @@ -21,12 +24,13 @@ def initialize
# Forms a TelemetryRequest object based on the event request_type
# @param request_type [String] the type of telemetry request to collect data for
# @param seq_id [Integer] the ID of the request; incremented each time a telemetry request is sent to the API
def telemetry_request(request_type:, seq_id:)
# @param data [Object] arbitrary object to be passed to the respective `request_type` handler
def telemetry_request(request_type:, seq_id:, data: nil)
Telemetry::V1::TelemetryRequest.new(
api_version: @api_version,
application: application,
host: host,
payload: payload(request_type),
payload: payload(request_type, data),
request_type: request_type,
runtime_id: runtime_id,
seq_id: seq_id,
Expand All @@ -36,14 +40,16 @@ def telemetry_request(request_type:, seq_id:)

private

def payload(request_type)
def payload(request_type, data)
case request_type
when :'app-started'
app_started
when :'app-closing', :'app-heartbeat'
{}
when :'app-integrations-change'
app_integrations_change
when 'app-client-configuration-change'
app_client_configuration_change(data)
else
raise ArgumentError, "Request type invalid, received request_type: #{@request_type}"
end
Expand All @@ -61,6 +67,15 @@ def app_started
def app_integrations_change
Telemetry::V1::AppEvent.new(integrations: integrations)
end

# DEV: During the transition from V1 to V2, the backend accepts many V2
# DEV: payloads through the V1 transport protocol.
# DEV: The `app-client-configuration-change` payload is one of them.
# DEV: Once V2 is fully implemented, `Telemetry::V2::AppClientConfigurationChange`
# DEV: should be reusable without major modifications.
def app_client_configuration_change(data)
Telemetry::V2::AppClientConfigurationChange.new(data[:changes], origin: data[:origin])
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

require_relative 'request'

module Datadog
module Core
module Telemetry
module V2
# Telemetry 'app-client-configuration-change' event.
# This request should contain client library configuration that have changes since the app-started event.
class AppClientConfigurationChange < Request
def initialize(configuration_changes, origin: 'unknown')
super('app-client-configuration-change')

@configuration_changes = configuration_changes
@origin = origin
end

# @see [Request#to_h]
def to_h
super.merge(payload: payload)
end

private

def payload
{
configuration: @configuration_changes.map do |name, value|
{
name: name,
value: value,
origin: @origin,
}
end
}
end
end
end
end
end
end
29 changes: 29 additions & 0 deletions lib/datadog/core/telemetry/v2/request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module Datadog
module Core
module Telemetry
module V2
# Base request object for Telemetry V2.
#
# `#to_h` is the main API, which returns a Ruby
# Hash that will be serialized as JSON.
class Request
# @param [String] request_type the Telemetry request type, which dictates how the Hash payload should be processed
def initialize(request_type)
@request_type = request_type
end

# Converts this request to a Hash that will
# be serialized as JSON.
# @return [Hash]
def to_h
{
request_type: @request_type
}
end
end
end
end
end
end
2 changes: 2 additions & 0 deletions sig/datadog/core/telemetry/client.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ module Datadog

def disable!: () -> untyped

def dynamic_configuration_change!: (Enumerable[[String, Numeric | bool | String]] changes) -> void

def started!: () -> (nil | untyped)

def emit_closing!: () -> (nil | untyped)
Expand Down
2 changes: 1 addition & 1 deletion sig/datadog/core/telemetry/emitter.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Datadog

extend Core::Utils::Forking
def initialize: (?http_transport: untyped) -> void
def request: (untyped request_type) -> untyped
def request: (String request_type, data: Object?) -> Datadog::Core::Telemetry::Http::Adapters::Net::Response
def self.sequence: () -> untyped
end
end
Expand Down
2 changes: 2 additions & 0 deletions sig/datadog/core/telemetry/event.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ module Datadog

private

def app_client_configuration_change: (Enumerable[[String, Numeric | bool | String]] changes)-> Datadog::Core::Telemetry::V2::AppClientConfigurationChange

def payload: (untyped request_type) -> untyped

def app_started: () -> untyped
Expand Down
2 changes: 1 addition & 1 deletion sig/datadog/core/telemetry/http/adapters/net.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module Datadog

def open: () ?{ () -> untyped } -> untyped

def post: (untyped env) -> untyped
def post: (untyped env) -> Datadog::Core::Telemetry::Http::Adapters::Net::Response
class Response
include Datadog::Core::Telemetry::Http::Response

Expand Down
2 changes: 1 addition & 1 deletion sig/datadog/core/telemetry/http/transport.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Datadog

def initialize: () -> void

def request: (request_type: untyped, payload: untyped) -> untyped
def request: (request_type: String, payload: String) -> Datadog::Core::Telemetry::Http::Adapters::Net::Response

private

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Datadog
module Core
module Telemetry
module V2
class AppClientConfigurationChange < Request
@configuration_changes: Enumerable[[String, Numeric | bool | String]]

@origin: String

def initialize: (Enumerable[[String, Numeric | bool | String]] configuration_changes, ?origin: String) -> void

def to_h: () -> Hash[Symbol, Object]

private

def payload: () -> Hash[Symbol, Array[Hash[Symbol, Numeric | bool | String]]]
end
end
end
end
end
15 changes: 15 additions & 0 deletions sig/datadog/core/telemetry/v2/request.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Datadog
module Core
module Telemetry
module V2
class Request
@request_type: String

def initialize: (String request_type) -> void

def to_h: () -> Hash[Symbol, Object]
end
end
end
end
end
43 changes: 43 additions & 0 deletions spec/datadog/core/telemetry/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,47 @@
end
end
end

describe '#dynamic_configuration_change!' do
subject(:dynamic_configuration_change!) { client.dynamic_configuration_change!(changes) }
let(:changes) { double('changes') }

after do
client.worker.stop(true)
client.worker.join
end

context 'when disabled' do
let(:enabled) { false }
it do
dynamic_configuration_change!
expect(emitter).to_not have_received(:request)
end
end

context 'when enabled' do
let(:enabled) { true }
it do
dynamic_configuration_change!
expect(emitter).to have_received(:request).with(
'app-client-configuration-change',
data: { changes: changes, origin: 'remote_config' }
)
end

it { is_expected.to be(response) }
end

context 'when in fork' do
before { skip 'Fork not supported on current platform' unless Process.respond_to?(:fork) }

it do
client
expect_in_fork do
client.started!
expect(emitter).to_not have_received(:request)
end
end
end
end
end
11 changes: 11 additions & 0 deletions spec/datadog/core/telemetry/emitter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@
end
end
end

context 'with data' do
subject(:request) { emitter.request(request_type, data: data) }
let(:data) { { changes: ['test-data'] } }
let(:request_type) { 'app-client-configuration-change' }

it 'sends data to the emitter' do
marcotc marked this conversation as resolved.
Show resolved Hide resolved
request
expect(http_transport).to have_received(:request).with(payload: include('test-data'), request_type: anything)
end
marcotc marked this conversation as resolved.
Show resolved Hide resolved
end
end

describe 'when initialized multiple times' do
Expand Down
15 changes: 14 additions & 1 deletion spec/datadog/core/telemetry/event_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
end

describe '#telemetry_request' do
subject(:telemetry_request) { event.telemetry_request(request_type: request_type, seq_id: seq_id) }
subject(:telemetry_request) { event.telemetry_request(request_type: request_type, seq_id: seq_id, data: data) }

let(:request_type) { :'app-started' }
let(:seq_id) { 1 }
let(:data) { nil }

it { is_expected.to be_a_kind_of(Datadog::Core::Telemetry::V1::TelemetryRequest) }
it { expect(telemetry_request.api_version).to eql('v1') }
Expand Down Expand Up @@ -49,6 +50,18 @@
it { expect(telemetry_request.payload).to be_a_kind_of(Datadog::Core::Telemetry::V1::AppEvent) }
end

context 'is app-client-configuration-change' do
let(:request_type) { 'app-client-configuration-change' }
let(:data) { { changes: [double('my-changes')], origin: 'my-origin' } }

it { expect(telemetry_request.payload).to be_a_kind_of(Datadog::Core::Telemetry::V2::AppClientConfigurationChange) }
it { expect(telemetry_request.request_type).to eq(request_type) }

it 'passes data to the event object' do
expect(telemetry_request.payload.to_h.to_json).to include('my-changes') & include('my-origin')
end
end

context 'is nil' do
let(:request_type) { nil }
it { expect { telemetry_request }.to raise_error(ArgumentError) }
Expand Down
Loading