Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit e62c433

Browse files
yantang-msftSergey Kanzhelev
authored andcommitted
Log failed request and expose timestamp as an optional parameter (#49)
* Add basic logging when failed to send telemetry * Log error when async sender thread terminated abnormally * Lower log level to warning if failed to send request to the server * Add timestamp to the options of the track methods, which helps in the cases of high precision or delayed tracking calls. * Add test cases for custom timestamp in track methods * Add test logger for unit tests * Expose timestamp as an optional parameter only at telemetry channel but not at the track methods * Update Changelog.md * Make the optional timestamp parameter either of String type or Time type.
1 parent 280d716 commit e62c433

File tree

9 files changed

+100
-14
lines changed

9 files changed

+100
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
This file needs to be updated with every significant pull request. It is used to write down release notes.
44

55
## Version 0.5.5
6+
* Add some basic logging when failed to send telemetry to the server
7+
* Add timestamp as an optional parameter to the TelemetryChannel::write() method
68

79
## Version 0.5.4
810

lib/application_insights/channel/asynchronous_sender.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,10 @@ def run
122122
break
123123
end
124124
end
125-
rescue
125+
rescue Exception => e
126126
# Make sure work_thread sets to nil when it terminates abnormally
127127
@work_thread = nil
128+
@logger.error('application_insights') { "Asynchronous sender work thread terminated abnormally: #{e.to_s}" }
128129
end
129130
end
130131
end

lib/application_insights/channel/sender_base.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'openssl'
44
require 'stringio'
55
require 'zlib'
6+
require 'logger'
67

78
module ApplicationInsights
89
module Channel
@@ -20,6 +21,7 @@ def initialize(service_endpoint_uri)
2021
@service_endpoint_uri = service_endpoint_uri
2122
@queue = nil
2223
@send_buffer_size = 100
24+
@logger = Logger.new(STDOUT)
2325
end
2426

2527
# The service endpoint URI where this sender will send data to.
@@ -36,6 +38,9 @@ def initialize(service_endpoint_uri)
3638
# @return [Fixnum] the maximum number of items in a telemetry batch.
3739
attr_accessor :send_buffer_size
3840

41+
# The logger for the sender.
42+
attr_accessor :logger
43+
3944
# Immediately sends the data passed in to {#service_endpoint_uri}. If the
4045
# service request fails, the passed in items are pushed back to the {#queue}.
4146
# @param [Array<Contracts::Envelope>] data_to_send an array of
@@ -63,6 +68,10 @@ def send(data_to_send)
6368

6469
response = http.request(request)
6570
http.finish if http.started?
71+
72+
if !response.kind_of? Net::HTTPSuccess
73+
@logger.warn('application_insights') { "Failed to send data: #{response.message}" }
74+
end
6675
end
6776

6877
private

lib/application_insights/channel/telemetry_channel.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,20 @@ def flush
6161
# an {Contracts::Envelope} before being enqueued to the {#queue}.
6262
# @param [TelemetryContext] context the override context to use when
6363
# constructing the {Contracts::Envelope}.
64-
def write(data, context=nil)
64+
# @param [Time|String] time the timestamp of the telemetry used to construct the
65+
# {Contracts::Envelope}.
66+
def write(data, context=nil, time=nil)
6567
local_context = context || @context
6668
raise ArgumentError, 'Context was required but not provided' unless local_context
69+
70+
if time && time.is_a?(String)
71+
local_time = time
72+
elsif time && time.is_a?(Time)
73+
local_time = time.iso8601(7)
74+
else
75+
local_time = Time.now.iso8601(7)
76+
end
77+
6778
data_type = data.class.name.gsub(/^.*::/, '')
6879
set_properties data, local_context
6980
data_attributes = {
@@ -72,7 +83,7 @@ def write(data, context=nil)
7283
}
7384
envelope_attributes = {
7485
:name => 'Microsoft.ApplicationInsights.' + data_type[0..-5],
75-
:time => Time.now.iso8601(7),
86+
:time => local_time,
7687
:i_key => local_context.instrumentation_key,
7788
:tags => get_tags(local_context),
7889
:data => Contracts::Data.new(data_attributes)

lib/application_insights/telemetry_client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def track_request(id, start_time, duration, response_code, success, options={})
217217
:measurements => options[:measurements] || {}
218218
)
219219

220-
self.channel.write(data, self.context)
220+
self.channel.write(data, self.context, start_time)
221221
end
222222

223223
# Flushes data in the queue. Data in the queue will be sent either immediately

test/application_insights/channel/test_sender_base.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require_relative '../../../lib/application_insights/channel/queue_base'
22
require_relative '../../../lib/application_insights/channel/sender_base'
3+
require_relative '../test_logger'
34
require 'socket'
45
require 'test/unit'
56
require 'thread'
@@ -38,20 +39,26 @@ def test_send_buffer_size_works_as_expected
3839

3940
def test_send_works_as_expected_with_400_code
4041
thread, port = execute_server '400 BadRequest'
42+
test_logger = TestLogger.new
4143
sender = SenderBase.new 'http://localhost:' + port.to_s + '/track'
44+
sender.logger = test_logger
4245
sender.queue = []
4346
sender.send([1, 2])
4447
thread.join
4548
assert_equal [], sender.queue
49+
assert_true test_logger.messages.include?('BadRequest')
4650
end
4751

4852
def test_send_works_as_expected_with_500_code
4953
thread, port = execute_server '500 InternalServerError'
54+
test_logger = TestLogger.new
5055
sender = SenderBase.new 'http://localhost:' + port.to_s + '/track'
56+
sender.logger = test_logger
5157
sender.queue = []
5258
sender.send([1, 2])
5359
thread.join
5460
assert_equal [], sender.queue
61+
assert_true test_logger.messages.include?('InternalServerError')
5562
end
5663

5764
def execute_server(code)

test/application_insights/channel/test_telemetry_channel.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require_relative '../../../lib/application_insights/channel/synchronous_queue'
44
require_relative '../../../lib/application_insights/channel/synchronous_sender'
55
require 'test/unit'
6+
require 'time'
67

78
include ApplicationInsights::Channel
89

@@ -72,6 +73,46 @@ def test_write_works_as_expected
7273
assert_same expected, actual.data.base_data
7374
end
7475

76+
def test_write_custom_timestamp
77+
queue = MockTelemetryChannelQueue.new SynchronousSender.new
78+
context = TelemetryContext.new
79+
context.instrumentation_key = 'instrumentation key'
80+
channel = TelemetryChannel.new context, queue
81+
data = MockTelemetryItemData.new
82+
timestamp = (Time.now - 5).iso8601(7)
83+
84+
channel.write data
85+
actual = queue.queue[0]
86+
assert_not_equal timestamp, actual.time
87+
88+
channel.write data, nil, timestamp
89+
actual = queue.queue[1]
90+
assert_equal timestamp, actual.time
91+
end
92+
93+
def test_write_custom_timestamp_accept_string_time_type
94+
queue = MockTelemetryChannelQueue.new SynchronousSender.new
95+
context = TelemetryContext.new
96+
context.instrumentation_key = 'instrumentation key'
97+
channel = TelemetryChannel.new context, queue
98+
data = MockTelemetryItemData.new
99+
100+
timestamp_string = (Time.now - 5).iso8601(7)
101+
channel.write data, nil, timestamp_string
102+
actual = queue.queue[0]
103+
assert_equal timestamp_string, actual.time
104+
105+
timestamp_time = Time.now - 5
106+
channel.write data, nil, timestamp_time
107+
actual = queue.queue[1]
108+
assert_equal timestamp_time.iso8601(7), actual.time
109+
110+
timestamp_invalid = {:invalid => "invalid timestamp"}
111+
channel.write data, nil, timestamp_invalid
112+
actual = queue.queue[2]
113+
assert_not_equal timestamp_invalid, actual.time
114+
end
115+
75116
def test_get_tags_works_as_expected
76117
queue = MockTelemetryChannelQueue.new SynchronousSender.new
77118
context = TelemetryContext.new
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class TestLogger < Logger
2+
def initialize
3+
@strio = StringIO.new
4+
super(@strio)
5+
end
6+
7+
def messages
8+
@strio.string
9+
end
10+
end

test/application_insights/test_telemetry_client.rb

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require_relative '../../lib/application_insights/channel/telemetry_channel'
55
require 'json'
66
require 'test/unit'
7+
require 'time'
78

89
include ApplicationInsights
910

@@ -60,7 +61,7 @@ def test_track_exception_works_as_expected
6061
assert_equal expected, actual
6162
end
6263

63-
def test_track_event_view_works_as_expected
64+
def test_track_event_works_as_expected
6465
client, sender = self.create_client
6566
client.track_event 'test'
6667
client.flush
@@ -70,7 +71,7 @@ def test_track_event_view_works_as_expected
7071
assert_equal expected, actual
7172
end
7273

73-
def test_track_metric_view_works_as_expected
74+
def test_track_metric_works_as_expected
7475
client, sender = self.create_client
7576
client.track_metric 'test', 42
7677
client.flush
@@ -90,22 +91,26 @@ def test_track_trace_view_works_as_expected
9091
assert_equal expected, actual
9192
end
9293

93-
def test_track_request_view_works_as_expected
94+
def test_track_request_works_as_expected
95+
start_time = Time.now.iso8601
9496
client, sender = self.create_client
95-
client.track_request 'test', '2015-01-24T23:10:22.7411910-08:00', '0:00:00:02.0000000','200', true
97+
client.track_request 'test', start_time, '0:00:00:02.0000000','200', true
9698
client.flush
97-
expected = '[{"ver":1,"name":"Microsoft.ApplicationInsights.Request","time":"TIME_PLACEHOLDER","sampleRate":100.0,"tags":{"ai.internal.sdkVersion":"rb:__version__"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"test","startTime":"2015-01-24T23:10:22.7411910-08:00","duration":"0:00:00:02.0000000","responseCode":"200","success":true}}}]'.gsub!(/__version__/, ApplicationInsights::VERSION)
98-
sender.data_to_send[0].time = 'TIME_PLACEHOLDER'
99+
expected = '[{"ver":1,"name":"Microsoft.ApplicationInsights.Request","time":"__time__","sampleRate":100.0,"tags":{"ai.internal.sdkVersion":"rb:__version__"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"test","startTime":"__time__","duration":"0:00:00:02.0000000","responseCode":"200","success":true}}}]'
100+
.gsub!(/__version__/, ApplicationInsights::VERSION)
101+
.gsub!(/__time__/, start_time)
99102
actual = sender.data_to_send.to_json
100103
assert_equal expected, actual
101104
end
102105

103-
def test_track_request_view_works_as_expected_when_request_is_failed
106+
def test_track_request_works_as_expected_when_request_is_failed
107+
start_time = Time.now.iso8601
104108
client, sender = self.create_client
105-
client.track_request 'test', '2015-01-24T23:10:22.7411910-08:00', '0:00:00:02.0000000','200', false
109+
client.track_request 'test', start_time, '0:00:00:02.0000000','200', false
106110
client.flush
107-
expected = '[{"ver":1,"name":"Microsoft.ApplicationInsights.Request","time":"TIME_PLACEHOLDER","sampleRate":100.0,"tags":{"ai.internal.sdkVersion":"rb:__version__"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"test","startTime":"2015-01-24T23:10:22.7411910-08:00","duration":"0:00:00:02.0000000","responseCode":"200","success":false}}}]'.gsub!(/__version__/, ApplicationInsights::VERSION)
108-
sender.data_to_send[0].time = 'TIME_PLACEHOLDER'
111+
expected = '[{"ver":1,"name":"Microsoft.ApplicationInsights.Request","time":"__time__","sampleRate":100.0,"tags":{"ai.internal.sdkVersion":"rb:__version__"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"test","startTime":"__time__","duration":"0:00:00:02.0000000","responseCode":"200","success":false}}}]'
112+
.gsub!(/__version__/, ApplicationInsights::VERSION)
113+
.gsub!(/__time__/, start_time)
109114
actual = sender.data_to_send.to_json
110115
assert_equal expected, actual
111116
end

0 commit comments

Comments
 (0)