application_insights 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/README.md +8 -14
- data/application_insights.gemspec +1 -0
- data/lib/application_insights.rb +7 -1
- data/lib/application_insights/channel/asynchronous_sender.rb +30 -28
- data/lib/application_insights/channel/queue_base.rb +1 -1
- data/lib/application_insights/channel/sender_base.rb +3 -1
- data/lib/application_insights/channel/telemetry_channel.rb +2 -1
- data/lib/application_insights/channel/telemetry_context.rb +1 -1
- data/lib/application_insights/rack/track_request.rb +84 -0
- data/lib/application_insights/telemetry_client.rb +42 -5
- data/lib/application_insights/unhandled_exception.rb +2 -3
- data/lib/application_insights/version.rb +1 -1
- data/test/application_insights/channel/test_asynchronous_queue.rb +1 -22
- data/test/application_insights/channel/test_asynchronous_sender.rb +1 -1
- data/test/application_insights/channel/test_synchronous_queue.rb +3 -17
- data/test/application_insights/channel/test_telemetry_context.rb +3 -0
- data/test/application_insights/mock_sender.rb +37 -0
- data/test/application_insights/rack/test_track_request.rb +139 -0
- data/test/application_insights/test_telemetry_client.rb +15 -4
- data/test/application_insights/test_unhandled_exception.rb +2 -20
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ODhmYmI4Y2ExYTdmNTAyOGY3MDAyNGM3NDI0N2ViMmM0NmMzYTkzNg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NjY2MmVkYzcwYzFmZjYzYzMyZjYzZmIwMTBjOTliZjBlNzc2NGUyZA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YTc3NGQyNDM1N2I4MjNkYmUxOTRlMTNhNDdkNWU0MGY1ZjZlMDNhZDQ5NmFi
|
10
|
+
MGQ4MmViMGE0YzU1MTRmMjI5M2Q2NmE0YTcyNmNmMjRjNTQzNWY2ZWFkNGE4
|
11
|
+
MTlhNGMwMDBkYTc1NjczN2I0YWViOTQyNWRjZjRiM2QwM2I1ZDc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YTYyMGU4MzkxYjUwZTljM2Y3ZGYxZGI1MTUxZGI0ZDc3OGI4YTMyNDQyNTBl
|
14
|
+
NTIxMDM4YTc0ZWM0ODU5ZmE3MWQ0ZDI4ODEzZDAzMmVlYTlhOGFmMGNmYmQy
|
15
|
+
YzRkNjg0OTM0MGYyYzYzNGM1MDMyYjMwNzE5ODZmYjgyMmM4M2I=
|
data/README.md
CHANGED
@@ -30,8 +30,7 @@ Once installed, you can send telemetry to Application Insights. Here are a few s
|
|
30
30
|
###Sending a simple event telemetry item###
|
31
31
|
```ruby
|
32
32
|
require 'application_insights'
|
33
|
-
tc = ApplicationInsights::TelemetryClient.new
|
34
|
-
tc.context.instrumentation_key = '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
33
|
+
tc = ApplicationInsights::TelemetryClient.new '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
35
34
|
tc.track_event 'My event'
|
36
35
|
tc.flush
|
37
36
|
```
|
@@ -39,8 +38,7 @@ tc.flush
|
|
39
38
|
###Sending an event telemetry item with custom properties and measurements###
|
40
39
|
```ruby
|
41
40
|
require 'application_insights'
|
42
|
-
tc = ApplicationInsights::TelemetryClient.new
|
43
|
-
tc.context.instrumentation_key = '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
41
|
+
tc = ApplicationInsights::TelemetryClient.new '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
44
42
|
tc.track_event 'My event', :properties => { 'custom property' => 'some value' }, :measurements => { 'custom metric' => 13 }
|
45
43
|
tc.flush
|
46
44
|
```
|
@@ -48,8 +46,7 @@ tc.flush
|
|
48
46
|
###Sending a trace telemetry item with custom properties###
|
49
47
|
```ruby
|
50
48
|
require 'application_insights'
|
51
|
-
tc = ApplicationInsights::TelemetryClient.new
|
52
|
-
tc.context.instrumentation_key = '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
49
|
+
tc = ApplicationInsights::TelemetryClient.new '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
53
50
|
tc.track_trace 'My trace statement', :properties => { 'custom property' => 'some value' }
|
54
51
|
tc.flush
|
55
52
|
```
|
@@ -57,8 +54,7 @@ tc.flush
|
|
57
54
|
###Sending a metric telemetry item (without and with optional values)###
|
58
55
|
```ruby
|
59
56
|
require 'application_insights'
|
60
|
-
tc = ApplicationInsights::TelemetryClient.new
|
61
|
-
tc.context.instrumentation_key = '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
57
|
+
tc = ApplicationInsights::TelemetryClient.new '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
62
58
|
tc.track_metric 'My metric', 42
|
63
59
|
# with all optional values set
|
64
60
|
tc.track_metric 'My metric', 42, :kind => ApplicationInsights::Channel::Contracts::DataPointType::AGGREGATION, :count => 3, :min => 1, :max => 100, :std_dev => 10, :properties => { 'custom property' => 'some value' }
|
@@ -68,8 +64,7 @@ tc.flush
|
|
68
64
|
###Sending an exception telemetry item with custom properties and measurements###
|
69
65
|
```ruby
|
70
66
|
require 'application_insights'
|
71
|
-
tc = ApplicationInsights::TelemetryClient.new
|
72
|
-
tc.context.instrumentation_key = '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
67
|
+
tc = ApplicationInsights::TelemetryClient.new '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
73
68
|
begin
|
74
69
|
raise ArgumentError, 'Something has gone wrong!'
|
75
70
|
rescue => e
|
@@ -81,8 +76,7 @@ tc.flush
|
|
81
76
|
###Configuring context for a telemetry client instance###
|
82
77
|
```ruby
|
83
78
|
require 'application_insights'
|
84
|
-
tc = ApplicationInsights::TelemetryClient.new
|
85
|
-
tc.context.instrumentation_key = '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
79
|
+
tc = ApplicationInsights::TelemetryClient.new '<YOUR INSTRUMENTATION KEY GOES HERE>'
|
86
80
|
tc.context.application.id = 'My application'
|
87
81
|
tc.context.application.ver = '1.2.3'
|
88
82
|
tc.context.device.id = 'My current device'
|
@@ -110,7 +104,7 @@ require 'application_insights'
|
|
110
104
|
sender = ApplicationInsights::Channel::AsynchronousSender.new
|
111
105
|
queue = ApplicationInsights::Channel::AsynchronousQueue.new sender
|
112
106
|
channel = ApplicationInsights::Channel::TelemetryChannel.new nil, queue
|
113
|
-
tc = ApplicationInsights::TelemetryClient.new channel
|
107
|
+
tc = ApplicationInsights::TelemetryClient.new '<YOUR INSTRUMENTATION KEY GOES HERE>', channel
|
114
108
|
# Note: the event will be sent on a separate thread; if the app finishes before
|
115
109
|
# the thread finishes, the data is lost
|
116
110
|
tc.track_event 'My event'
|
@@ -122,7 +116,7 @@ require 'application_insights'
|
|
122
116
|
sender = ApplicationInsights::Channel::AsynchronousSender.new
|
123
117
|
queue = ApplicationInsights::Channel::AsynchronousQueue.new sender
|
124
118
|
channel = ApplicationInsights::Channel::TelemetryChannel.new nil, queue
|
125
|
-
tc = ApplicationInsights::TelemetryClient.new channel
|
119
|
+
tc = ApplicationInsights::TelemetryClient.new '<YOUR INSTRUMENTATION KEY GOES HERE>', channel
|
126
120
|
# flush telemetry if we have 10 or more telemetry items in our queue
|
127
121
|
tc.channel.queue.max_queue_length = 10
|
128
122
|
# send telemetry to the service in batches of 5
|
data/lib/application_insights.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
require_relative 'application_insights/telemetry_client'
|
2
2
|
require_relative 'application_insights/unhandled_exception'
|
3
|
-
require_relative 'application_insights/version'
|
3
|
+
require_relative 'application_insights/version'
|
4
|
+
|
5
|
+
module ApplicationInsights
|
6
|
+
module Rack
|
7
|
+
autoload :TrackRequest, "application_insights/rack/track_request"
|
8
|
+
end
|
9
|
+
end
|
@@ -20,8 +20,9 @@ module ApplicationInsights
|
|
20
20
|
@send_interval = 1.0
|
21
21
|
@send_remaining_time = 0
|
22
22
|
@send_time = 3.0
|
23
|
-
@
|
23
|
+
@lock_work_thread = Mutex.new
|
24
24
|
@work_thread = nil
|
25
|
+
@start_notification_processed = true
|
25
26
|
super service_endpoint_uri
|
26
27
|
end
|
27
28
|
|
@@ -41,40 +42,37 @@ module ApplicationInsights
|
|
41
42
|
# a total duration of {#send_time} seconds for new items. If a worker thread has already been created, calling
|
42
43
|
# this method does nothing.
|
43
44
|
def start
|
44
|
-
@
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
45
|
+
@start_notification_processed = false
|
46
|
+
# Maintain one working thread at one time
|
47
|
+
if !@work_thread
|
48
|
+
@lock_work_thread.synchronize do
|
49
|
+
if !@work_thread
|
50
|
+
local_send_interval = (@send_interval < 0.1) ? 0.1 : @send_interval
|
51
|
+
@send_remaining_time = (@send_time < local_send_interval) ? local_send_interval : @send_time
|
52
|
+
@work_thread = Thread.new do
|
53
|
+
run
|
54
|
+
end
|
55
|
+
@work_thread.abort_on_exception = false
|
51
56
|
end
|
52
|
-
@work_thread.abort_on_exception = false
|
53
57
|
end
|
54
58
|
end
|
55
59
|
end
|
56
60
|
|
57
|
-
# Calling this method will shut down the worker thread that checks the {#queue} for new items. The thread will be
|
58
|
-
# allowed to shut down gracefully.
|
59
|
-
def stop
|
60
|
-
@lock_send_remaining_time.synchronize do
|
61
|
-
@send_remaining_time = 0
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
61
|
private
|
66
62
|
|
67
63
|
def run
|
68
64
|
# save the queue locally
|
69
65
|
local_queue = @queue
|
70
66
|
if local_queue == nil
|
71
|
-
|
67
|
+
@work_thread = nil
|
72
68
|
return
|
73
69
|
end
|
74
70
|
|
71
|
+
begin
|
75
72
|
# fix up the send interval (can't be lower than 100ms)
|
76
73
|
local_send_interval = (@send_interval < 0.1) ? 0.1 : @send_interval
|
77
74
|
while TRUE
|
75
|
+
@start_notification_processed = true
|
78
76
|
while TRUE
|
79
77
|
# get at most @send_buffer_size items from the queue
|
80
78
|
counter = @send_buffer_size
|
@@ -90,9 +88,7 @@ module ApplicationInsights
|
|
90
88
|
break if data.length == 0
|
91
89
|
|
92
90
|
# reset the send time
|
93
|
-
@
|
94
|
-
@send_remaining_time = @send_time
|
95
|
-
end
|
91
|
+
@send_remaining_time = @send_time
|
96
92
|
|
97
93
|
# finally send the data
|
98
94
|
send data
|
@@ -106,15 +102,21 @@ module ApplicationInsights
|
|
106
102
|
end
|
107
103
|
|
108
104
|
# decrement the remaining time
|
109
|
-
@
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
105
|
+
@send_remaining_time -= local_send_interval
|
106
|
+
# If remaining time <=0 and there is no start notification unprocessed, then stop the working thread
|
107
|
+
if @send_remaining_time <= 0 && @start_notification_processed
|
108
|
+
# Note: there is still a chance some start notification could be missed, e.g., the start method
|
109
|
+
# got triggered between the above and following line. However the data is not lost as it would be processed
|
110
|
+
# later when next start notification comes after the worker thread stops. The cost to ensure no
|
111
|
+
# notification miss is high where a lock is required each time the start method calls.
|
112
|
+
@work_thread = nil
|
113
|
+
break
|
116
114
|
end
|
117
115
|
end
|
116
|
+
rescue
|
117
|
+
# Make sure work_thread sets to nil when it terminates abnormally
|
118
|
+
@work_thread = nil
|
119
|
+
end
|
118
120
|
end
|
119
121
|
end
|
120
122
|
end
|
@@ -43,7 +43,9 @@ module ApplicationInsights
|
|
43
43
|
'Content-Encoding' => 'gzip'
|
44
44
|
}
|
45
45
|
request = Net::HTTP::Post.new(uri.path, headers)
|
46
|
-
|
46
|
+
# Use JSON.generate instead of to_json, otherwise it will default to ActiveSupport::JSON.encode for Rails app
|
47
|
+
json = JSON.generate(data_to_send)
|
48
|
+
compressed_data = compress(json)
|
47
49
|
request.body = compressed_data
|
48
50
|
|
49
51
|
http = Net::HTTP.new uri.hostname, uri.port
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'time'
|
1
2
|
require_relative 'asynchronous_queue'
|
2
3
|
require_relative 'asynchronous_sender'
|
3
4
|
require_relative 'telemetry_context'
|
@@ -62,7 +63,7 @@ module ApplicationInsights
|
|
62
63
|
}
|
63
64
|
envelope_attributes = {
|
64
65
|
:name => 'Microsoft.ApplicationInsights.' + data_type[0..-5],
|
65
|
-
:time => Time.now.
|
66
|
+
:time => Time.now.iso8601(7),
|
66
67
|
:i_key => local_context.instrumentation_key,
|
67
68
|
:tags => get_tags(local_context),
|
68
69
|
:data => Contracts::Data.new(data_attributes)
|
@@ -62,7 +62,7 @@ module ApplicationInsights
|
|
62
62
|
|
63
63
|
# The property context. This contains free-form properties that you can add to your telemetry.
|
64
64
|
# @return [Hash<String, String>] the context object.
|
65
|
-
|
65
|
+
attr_accessor :properties
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require_relative '../channel/contracts/request_data'
|
3
|
+
require_relative '../telemetry_client'
|
4
|
+
|
5
|
+
module ApplicationInsights
|
6
|
+
module Rack
|
7
|
+
# Track every request and sends the request data to Application Insights.
|
8
|
+
class TrackRequest
|
9
|
+
# Initializes a new instance of the class.
|
10
|
+
# @param [Object] app the inner rack application.
|
11
|
+
# @param [String] instrumentation_key to identify which Application Insights application this data is for.
|
12
|
+
# @param [Fixnum] buffer_size the buffer size and the buffered requests would send to Application Insights
|
13
|
+
# when buffer is full.
|
14
|
+
# @param [Fixnum] send_interval the frequency (in seconds) to check buffer and send buffered requests to Application
|
15
|
+
# Insights if any.
|
16
|
+
def initialize(app, instrumentation_key, buffer_size=500, send_interval=60)
|
17
|
+
@app = app
|
18
|
+
@instrumentation_key = instrumentation_key
|
19
|
+
@buffer_size = buffer_size
|
20
|
+
@send_interval = send_interval
|
21
|
+
end
|
22
|
+
|
23
|
+
# Track requests and send data to Application Insights asynchronously.
|
24
|
+
# @param [Hash] env the rack environment.
|
25
|
+
def call(env)
|
26
|
+
start = Time.now
|
27
|
+
begin
|
28
|
+
status, headers, response = @app.call(env)
|
29
|
+
rescue Exception => ex
|
30
|
+
status = 500
|
31
|
+
exception = ex
|
32
|
+
end
|
33
|
+
stop = Time.now
|
34
|
+
|
35
|
+
if !@client
|
36
|
+
sender = @sender || Channel::AsynchronousSender.new
|
37
|
+
sender.send_interval = @send_interval
|
38
|
+
queue = Channel::AsynchronousQueue.new sender
|
39
|
+
queue.max_queue_length = @buffer_size
|
40
|
+
channel = Channel::TelemetryChannel.new nil, queue
|
41
|
+
@client = TelemetryClient.new @instrumentation_key, channel
|
42
|
+
end
|
43
|
+
|
44
|
+
request = ::Rack::Request.new env
|
45
|
+
id = rand(16**32).to_s(16)
|
46
|
+
start_time = start.iso8601(7)
|
47
|
+
duration = format_request_duration(stop - start)
|
48
|
+
success = status.to_i < 400
|
49
|
+
options = {
|
50
|
+
:name => "#{request.request_method} #{request.path}",
|
51
|
+
:http_method => request.request_method,
|
52
|
+
:url => request.url
|
53
|
+
}
|
54
|
+
@client.track_request id, start_time, duration, status, success, options
|
55
|
+
|
56
|
+
if exception != nil
|
57
|
+
@client.track_exception exception, handled_at: 'Unhandled'
|
58
|
+
raise exception
|
59
|
+
end
|
60
|
+
|
61
|
+
[status, headers, response]
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def sender=(sender)
|
67
|
+
@sender = sender if sender.is_a? Channel::AsynchronousSender
|
68
|
+
end
|
69
|
+
|
70
|
+
def client
|
71
|
+
@client
|
72
|
+
end
|
73
|
+
|
74
|
+
def format_request_duration(duration_seconds)
|
75
|
+
if duration_seconds >= 86400
|
76
|
+
# just return 1 day when it takes more than 1 day which should not happen for requests.
|
77
|
+
return "%d:%02d:%02d:%02d.%07d" % [1, 0, 0, 0, 0]
|
78
|
+
end
|
79
|
+
|
80
|
+
Time.at(duration_seconds).gmtime.strftime("0:%H:%M:%S.%7N")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -9,16 +9,20 @@ require_relative 'channel/contracts/data_point_type'
|
|
9
9
|
require_relative 'channel/contracts/metric_data'
|
10
10
|
require_relative 'channel/contracts/message_data'
|
11
11
|
require_relative 'channel/contracts/stack_frame'
|
12
|
+
require_relative 'channel/contracts/request_data'
|
13
|
+
require_relative 'channel/contracts/severity_level'
|
12
14
|
|
13
15
|
module ApplicationInsights
|
14
16
|
# The telemetry client used for sending all types of telemetry. It serves as the main entry point for
|
15
17
|
# interacting with the Application Insights service.
|
16
18
|
class TelemetryClient
|
17
19
|
# Initializes a new instance of the class.
|
20
|
+
# @param [String] instrumentation_key to identify which Application Insights application this data is for.
|
18
21
|
# @param [Channel::TelemetryChannel] telemetry_channel the optional telemetry channel to be used instead of
|
19
22
|
# constructing a default one.
|
20
|
-
def initialize(telemetry_channel = nil)
|
23
|
+
def initialize(instrumentation_key = nil, telemetry_channel = nil)
|
21
24
|
@context = Channel::TelemetryContext.new
|
25
|
+
@context.instrumentation_key = instrumentation_key
|
22
26
|
@channel = telemetry_channel || Channel::TelemetryChannel.new
|
23
27
|
end
|
24
28
|
|
@@ -83,7 +87,7 @@ module ApplicationInsights
|
|
83
87
|
details_attributes = {
|
84
88
|
:id => 1,
|
85
89
|
:outer_id => 0,
|
86
|
-
:type_name => exception.class,
|
90
|
+
:type_name => exception.class.name,
|
87
91
|
:message => exception.message,
|
88
92
|
:has_full_stack => exception.backtrace != nil,
|
89
93
|
:stack => (exception.backtrace.join("\n") if exception.backtrace),
|
@@ -159,18 +163,51 @@ module ApplicationInsights
|
|
159
163
|
|
160
164
|
# Sends a single trace statement.
|
161
165
|
# @param [String] name the trace statement.
|
166
|
+
# @param [Channel::Contracts::SeverityLevel] severity_level the severity level.
|
162
167
|
# @param [Hash] options the options to create the {Channel::Contracts::EventData} object.
|
163
168
|
# @option options [Hash] :properties the set of custom properties the client wants attached to this
|
164
169
|
# data item. (defaults to: {})
|
165
|
-
def track_trace(name, options={})
|
170
|
+
def track_trace(name, severity_level = Channel::Contracts::SeverityLevel::INFORMATION, options={})
|
166
171
|
data_attributes = {
|
167
|
-
|
168
|
-
|
172
|
+
:message => name || 'Null',
|
173
|
+
:severity_level => severity_level || Channel::Contracts::SeverityLevel::INFORMATION,
|
174
|
+
:properties => options.fetch(:properties) { {} }
|
169
175
|
}
|
170
176
|
data = Channel::Contracts::MessageData.new data_attributes
|
171
177
|
self.channel.write(data, self.context)
|
172
178
|
end
|
173
179
|
|
180
|
+
# Sends a single request.
|
181
|
+
# @param [String] id the unique identifier of the request.
|
182
|
+
# @param (String) start_time the start time of the request.
|
183
|
+
# @param [String] duration the duration to process the request.
|
184
|
+
# @param [String] response_code the response code of the request.
|
185
|
+
# @param [Boolean] success indicates whether the request succeeds or not.
|
186
|
+
# @param [Hash] options the options to create the {Channel::Contracts::RequestData} object.
|
187
|
+
# @option options [String] :name the name of the request.
|
188
|
+
# @option options [String] :http_method the http method used for the request.
|
189
|
+
# @option options [String] :url the url of the request.
|
190
|
+
# @option options [Hash] :properties the set of custom properties the client wants attached to this
|
191
|
+
# data item. (defaults to: {})
|
192
|
+
# @option options [Hash] :measurements the set of custom measurements the client wants to attach to
|
193
|
+
# this data item (defaults to: {})
|
194
|
+
def track_request(id, start_time, duration, response_code, success, options={})
|
195
|
+
data_attributes = {
|
196
|
+
:id => id || 'Null',
|
197
|
+
:start_time => start_time || Time.now.iso8601(7),
|
198
|
+
:duration => duration || '0:00:00:00.0000000',
|
199
|
+
:response_code => response_code || 200,
|
200
|
+
:success => success = nil ? true : success,
|
201
|
+
:name => options[:name],
|
202
|
+
:http_method => options[:http_method],
|
203
|
+
:url => options[:url],
|
204
|
+
:properties => options.fetch(:properties) { {} },
|
205
|
+
:measurements => options.fetch(:measurements) { {} }
|
206
|
+
}
|
207
|
+
data = Channel::Contracts::RequestData.new data_attributes
|
208
|
+
self.channel.write(data, self.context)
|
209
|
+
end
|
210
|
+
|
174
211
|
# Flushes data in the queue. Data in the queue will be sent either immediately irrespective of what sender is
|
175
212
|
# being used.
|
176
213
|
def flush
|
@@ -37,9 +37,8 @@ module ApplicationInsights
|
|
37
37
|
|
38
38
|
queue = Channel::SynchronousQueue.new @sender
|
39
39
|
channel = Channel::TelemetryChannel.new nil, queue
|
40
|
-
client = TelemetryClient.new channel
|
41
|
-
client.
|
42
|
-
client.track_exception($!, {:handled_at => 'Unhandled'})
|
40
|
+
client = TelemetryClient.new instrumentation_key, channel
|
41
|
+
client.track_exception($!, handled_at: 'Unhandled')
|
43
42
|
client.flush
|
44
43
|
end
|
45
44
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative '../../../lib/application_insights/channel/sender_base'
|
2
2
|
require_relative '../../../lib/application_insights/channel/asynchronous_queue'
|
3
|
+
require_relative '../mock_sender'
|
3
4
|
require 'test/unit'
|
4
5
|
|
5
6
|
include ApplicationInsights::Channel
|
@@ -43,26 +44,4 @@ class TestAsynchronousQueue < Test::Unit::TestCase
|
|
43
44
|
result = queue.flush_notification.wait
|
44
45
|
assert_equal true, result
|
45
46
|
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class MockAsynchronousSender
|
49
|
-
def initialize
|
50
|
-
@send_buffer_size = 2
|
51
|
-
@data = []
|
52
|
-
@queue = nil
|
53
|
-
@start_call_count = 0
|
54
|
-
end
|
55
|
-
|
56
|
-
attr_accessor :start_call_count
|
57
|
-
attr_accessor :send_buffer_size
|
58
|
-
attr_accessor :data
|
59
|
-
attr_accessor :queue
|
60
|
-
|
61
|
-
def start
|
62
|
-
@start_call_count += 1
|
63
|
-
end
|
64
|
-
|
65
|
-
def send(data_to_send)
|
66
|
-
@data.push data_to_send
|
67
|
-
end
|
68
47
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative '../../../lib/application_insights/channel/sender_base'
|
2
2
|
require_relative '../../../lib/application_insights/channel/synchronous_queue'
|
3
|
+
require_relative '../mock_sender'
|
3
4
|
require 'test/unit'
|
4
5
|
|
5
6
|
include ApplicationInsights::Channel
|
@@ -11,32 +12,17 @@ class TestSynchronousQueue < Test::Unit::TestCase
|
|
11
12
|
|
12
13
|
def test_flush_works_as_expected
|
13
14
|
sender = MockSynchronousSender.new
|
15
|
+
sender.send_buffer_size = 2
|
14
16
|
queue = SynchronousQueue.new sender
|
15
17
|
queue.max_queue_length = 3
|
16
18
|
(1..7).to_a.each do |i|
|
17
19
|
queue.push i
|
18
20
|
end
|
19
|
-
assert_equal [[1, 2], [3], [4, 5], [6]], sender.
|
21
|
+
assert_equal [[1, 2], [3], [4, 5], [6]], sender.buffer
|
20
22
|
temp = []
|
21
23
|
queue.instance_variable_get('@queue').length.times do |i|
|
22
24
|
temp.push queue.instance_variable_get('@queue').pop
|
23
25
|
end
|
24
26
|
assert_equal [7], temp
|
25
27
|
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class MockSynchronousSender
|
29
|
-
def initialize
|
30
|
-
@send_buffer_size = 2
|
31
|
-
@data = []
|
32
|
-
@queue = nil
|
33
|
-
end
|
34
|
-
|
35
|
-
attr_accessor :send_buffer_size
|
36
|
-
attr_accessor :data
|
37
|
-
attr_accessor :queue
|
38
|
-
|
39
|
-
def send(data_to_send)
|
40
|
-
@data.push data_to_send
|
41
|
-
end
|
42
28
|
end
|
@@ -68,5 +68,8 @@ class TestTelemetryContext < Test::Unit::TestCase
|
|
68
68
|
context = TelemetryContext.new
|
69
69
|
assert_not_nil context.properties
|
70
70
|
assert_equal 0, context.properties.count
|
71
|
+
expected = {:a => 'a', :b => 'b'}
|
72
|
+
context.properties = expected
|
73
|
+
assert_same expected, context.properties
|
71
74
|
end
|
72
75
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative '../../lib/application_insights/channel/synchronous_sender'
|
2
|
+
require_relative '../../lib/application_insights/channel/asynchronous_sender'
|
3
|
+
|
4
|
+
include ApplicationInsights::Channel
|
5
|
+
|
6
|
+
class MockSynchronousSender < SynchronousSender
|
7
|
+
def initialize
|
8
|
+
@buffer = []
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :buffer
|
13
|
+
|
14
|
+
def send(data)
|
15
|
+
@buffer << data
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class MockAsynchronousSender < AsynchronousSender
|
20
|
+
def initialize
|
21
|
+
@buffer = []
|
22
|
+
@start_call_count = 0
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_accessor :start_call_count
|
27
|
+
attr_accessor :buffer
|
28
|
+
|
29
|
+
def start
|
30
|
+
@start_call_count += 1
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def send(data)
|
35
|
+
@buffer << data
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rack/mock'
|
3
|
+
require_relative '../mock_sender'
|
4
|
+
require_relative '../../../lib/application_insights/rack/track_request'
|
5
|
+
|
6
|
+
include ApplicationInsights::Rack
|
7
|
+
|
8
|
+
class TestTrackRequest < Test::Unit::TestCase
|
9
|
+
def test_call_works_as_expected
|
10
|
+
response_code = rand(200..204)
|
11
|
+
app = Proc.new {|env| sleep(2); [response_code, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
|
12
|
+
url = "http://localhost:8080/foo?a=b"
|
13
|
+
http_method = 'PUT'
|
14
|
+
env = Rack::MockRequest.env_for(url, :method => http_method)
|
15
|
+
instrumentation_key = 'key'
|
16
|
+
sender = MockAsynchronousSender.new
|
17
|
+
track_request = TrackRequest.new app, instrumentation_key, 500, 1
|
18
|
+
track_request.send(:sender=, sender)
|
19
|
+
start_time = Time.now
|
20
|
+
result = track_request.call(env)
|
21
|
+
assert_equal app.call(env), result
|
22
|
+
sleep(sender.send_interval)
|
23
|
+
|
24
|
+
assert_equal 1, sender.buffer.count
|
25
|
+
payload = sender.buffer[0]
|
26
|
+
assert_equal instrumentation_key, payload[0].i_key
|
27
|
+
|
28
|
+
request_data = payload[0].data.base_data
|
29
|
+
assert_equal "#{http_method} /foo", request_data.name
|
30
|
+
assert_equal response_code, request_data.response_code
|
31
|
+
assert_equal true, request_data.success
|
32
|
+
assert_equal http_method, request_data.http_method
|
33
|
+
assert_equal url, request_data.url
|
34
|
+
assert_equal true, request_data.duration.start_with?("0:00:00:02")
|
35
|
+
assert_in_epsilon Time.parse(request_data.start_time) - start_time, 0, 0.01
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_call_with_failed_request
|
39
|
+
response_code = rand(400..600)
|
40
|
+
app = Proc.new {|env| [response_code, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
|
41
|
+
url = "http://localhost:8080/foo"
|
42
|
+
http_method = 'PUT'
|
43
|
+
env = Rack::MockRequest.env_for(url, :method => http_method)
|
44
|
+
instrumentation_key = 'key'
|
45
|
+
sender = MockAsynchronousSender.new
|
46
|
+
track_request = TrackRequest.new app, instrumentation_key, 500, 1
|
47
|
+
track_request.send(:sender=, sender)
|
48
|
+
track_request.call(env)
|
49
|
+
sleep(sender.send_interval)
|
50
|
+
|
51
|
+
payload = sender.buffer[0]
|
52
|
+
request_data = payload[0].data.base_data
|
53
|
+
assert_equal false, request_data.success
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_call_with_unhandled_exception
|
57
|
+
app = Proc.new {|env| raise StandardError, 'Boom!'}
|
58
|
+
env = Rack::MockRequest.env_for( "http://localhost:8080/foo", :method => "GET")
|
59
|
+
instrumentation_key = 'key'
|
60
|
+
sender = MockAsynchronousSender.new
|
61
|
+
track_request = TrackRequest.new app, instrumentation_key, 500, 1
|
62
|
+
track_request.send(:sender=, sender)
|
63
|
+
|
64
|
+
begin
|
65
|
+
track_request.call(env)
|
66
|
+
rescue => ex
|
67
|
+
assert_equal 'Boom!', ex.message
|
68
|
+
end
|
69
|
+
|
70
|
+
sleep(sender.send_interval)
|
71
|
+
payload = sender.buffer[0]
|
72
|
+
assert_equal 2, payload.count
|
73
|
+
request_data = payload[0].data.base_data
|
74
|
+
assert_equal false, request_data.success
|
75
|
+
assert_equal 500, request_data.response_code
|
76
|
+
|
77
|
+
exception_data = payload[1].data.base_data
|
78
|
+
assert_equal instrumentation_key, payload[1].i_key
|
79
|
+
assert_equal 'Unhandled', exception_data.handled_at
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_internal_client
|
83
|
+
app = Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
|
84
|
+
env = Rack::MockRequest.env_for('http://localhost:8080/foo', :method => "GET")
|
85
|
+
buffer_size = 30
|
86
|
+
send_interval = 5
|
87
|
+
track_request = TrackRequest.new app, 'key', buffer_size, send_interval
|
88
|
+
client = track_request.send(:client)
|
89
|
+
# test lazy initialization
|
90
|
+
assert_nil client
|
91
|
+
|
92
|
+
track_request.call(env)
|
93
|
+
client = track_request.send(:client)
|
94
|
+
channel = client.channel
|
95
|
+
assert_equal buffer_size, channel.queue.max_queue_length
|
96
|
+
assert_equal send_interval, channel.sender.send_interval
|
97
|
+
|
98
|
+
track_request.call(env)
|
99
|
+
client2 = track_request.send(:client)
|
100
|
+
channel2 = client2.channel
|
101
|
+
assert_same client, client2
|
102
|
+
assert_same channel, channel2
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_format_request_duration_less_than_a_day
|
106
|
+
app = Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
|
107
|
+
track_request = TrackRequest.new app, 'one'
|
108
|
+
duration_seconds = rand(86400) + rand
|
109
|
+
time_span = track_request.send(:format_request_duration, duration_seconds)
|
110
|
+
time_span_format = /^(?<day>\d{1}):(?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2}).(?<fraction>\d{7})$/
|
111
|
+
match = time_span_format.match time_span
|
112
|
+
assert_not_nil match
|
113
|
+
days = duration_seconds.to_i/86400
|
114
|
+
assert_equal days, match['day'].to_i
|
115
|
+
hours = (duration_seconds - days * 86400).to_i/3600
|
116
|
+
assert_equal hours, match['hour'].to_i
|
117
|
+
minutes = (duration_seconds - days * 86400 - hours * 3600).to_i/60
|
118
|
+
assert_equal minutes, match['minute'].to_i
|
119
|
+
seconds = (duration_seconds - days * 86400 - hours * 3600 - minutes * 60).to_i
|
120
|
+
assert_equal seconds, match['second'].to_i
|
121
|
+
fraction = ((duration_seconds - duration_seconds.to_i) * 10 ** 7).to_i
|
122
|
+
assert_equal fraction, match['fraction'].to_i
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_format_request_duration_more_than_a_day
|
126
|
+
app = Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
|
127
|
+
track_request = TrackRequest.new app, 'one'
|
128
|
+
duration_seconds = rand(86400..240000) + rand
|
129
|
+
time_span = track_request.send(:format_request_duration, duration_seconds)
|
130
|
+
time_span_format = /^(?<day>\d{1}):(?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2}).(?<fraction>\d{7})$/
|
131
|
+
match = time_span_format.match time_span
|
132
|
+
assert_not_nil match
|
133
|
+
assert_equal 1, match['day'].to_i
|
134
|
+
assert_equal 0, match['hour'].to_i
|
135
|
+
assert_equal 0, match['minute'].to_i
|
136
|
+
assert_equal 0, match['second'].to_i
|
137
|
+
assert_equal 0, match['fraction'].to_i
|
138
|
+
end
|
139
|
+
end
|
@@ -14,9 +14,10 @@ class TestTelemetryClient < Test::Unit::TestCase
|
|
14
14
|
assert_not_nil client.channel
|
15
15
|
|
16
16
|
channel = Object.new
|
17
|
-
client = TelemetryClient.new channel
|
17
|
+
client = TelemetryClient.new 'a', channel
|
18
18
|
assert_not_nil client.context
|
19
19
|
assert_same channel, client.channel
|
20
|
+
assert_equal 'a', client.context.instrumentation_key
|
20
21
|
end
|
21
22
|
|
22
23
|
def test_context_property_works_as_expected
|
@@ -81,9 +82,19 @@ class TestTelemetryClient < Test::Unit::TestCase
|
|
81
82
|
|
82
83
|
def test_track_trace_view_works_as_expected
|
83
84
|
client, sender = self.create_client
|
84
|
-
client.track_trace 'test'
|
85
|
+
client.track_trace 'test', Channel::Contracts::SeverityLevel::WARNING
|
85
86
|
client.flush
|
86
|
-
expected = '[{"ver":1,"name":"Microsoft.ApplicationInsights.Message","time":"TIME_PLACEHOLDER","sampleRate":100.0,"tags":{"ai.internal.sdkVersion":"rb:0.1.0"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"test"}}}]'
|
87
|
+
expected = '[{"ver":1,"name":"Microsoft.ApplicationInsights.Message","time":"TIME_PLACEHOLDER","sampleRate":100.0,"tags":{"ai.internal.sdkVersion":"rb:0.1.0"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"test","severityLevel":2}}}]'
|
88
|
+
sender.data_to_send[0].time = 'TIME_PLACEHOLDER'
|
89
|
+
actual = sender.data_to_send.to_json
|
90
|
+
assert_equal expected, actual
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_track_request_view_works_as_expected
|
94
|
+
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
|
96
|
+
client.flush
|
97
|
+
expected = '[{"ver":1,"name":"Microsoft.ApplicationInsights.Request","time":"TIME_PLACEHOLDER","sampleRate":100.0,"tags":{"ai.internal.sdkVersion":"rb:0.1.0"},"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}}}]'
|
87
98
|
sender.data_to_send[0].time = 'TIME_PLACEHOLDER'
|
88
99
|
actual = sender.data_to_send.to_json
|
89
100
|
assert_equal expected, actual
|
@@ -93,7 +104,7 @@ class TestTelemetryClient < Test::Unit::TestCase
|
|
93
104
|
sender = MockTelemetryClientSender.new
|
94
105
|
queue = Channel::SynchronousQueue.new sender
|
95
106
|
channel = Channel::TelemetryChannel.new nil, queue
|
96
|
-
client = TelemetryClient.new channel
|
107
|
+
client = TelemetryClient.new nil, channel
|
97
108
|
return client, sender
|
98
109
|
end
|
99
110
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'test/unit'
|
2
|
+
require_relative 'mock_sender'
|
2
3
|
require_relative '../../lib/application_insights/unhandled_exception'
|
3
4
|
|
4
5
|
include ApplicationInsights
|
@@ -6,7 +7,7 @@ include ApplicationInsights
|
|
6
7
|
class TestUnhandledException < Test::Unit::TestCase
|
7
8
|
|
8
9
|
def test_send
|
9
|
-
sender =
|
10
|
+
sender = MockSynchronousSender.new
|
10
11
|
error = 'Boom!'
|
11
12
|
instrumentation_key = 'one'
|
12
13
|
begin
|
@@ -20,23 +21,4 @@ class TestUnhandledException < Test::Unit::TestCase
|
|
20
21
|
assert_equal instrumentation_key, payload[0].i_key
|
21
22
|
assert_equal 'Unhandled', payload[0].data.base_data.handled_at
|
22
23
|
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class MockSender
|
26
|
-
def initialize
|
27
|
-
@buffer = []
|
28
|
-
@queue = nil
|
29
|
-
@send_buffer_size = 100
|
30
|
-
end
|
31
|
-
|
32
|
-
attr_accessor :buffer
|
33
|
-
attr_accessor :queue
|
34
|
-
attr_accessor :send_buffer_size
|
35
|
-
|
36
|
-
def send(data)
|
37
|
-
@buffer << data
|
38
|
-
end
|
39
|
-
|
40
|
-
def flush
|
41
|
-
end
|
42
24
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: application_insights
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Microsoft
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 3.2.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.0.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.0.0
|
69
83
|
description: This project extends the Application Insights API surface to support
|
70
84
|
Ruby.
|
71
85
|
email:
|
@@ -114,6 +128,7 @@ files:
|
|
114
128
|
- lib/application_insights/channel/synchronous_sender.rb
|
115
129
|
- lib/application_insights/channel/telemetry_channel.rb
|
116
130
|
- lib/application_insights/channel/telemetry_context.rb
|
131
|
+
- lib/application_insights/rack/track_request.rb
|
117
132
|
- lib/application_insights/telemetry_client.rb
|
118
133
|
- lib/application_insights/unhandled_exception.rb
|
119
134
|
- lib/application_insights/version.rb
|
@@ -146,6 +161,8 @@ files:
|
|
146
161
|
- test/application_insights/channel/test_synchronous_sender.rb
|
147
162
|
- test/application_insights/channel/test_telemetry_channel.rb
|
148
163
|
- test/application_insights/channel/test_telemetry_context.rb
|
164
|
+
- test/application_insights/mock_sender.rb
|
165
|
+
- test/application_insights/rack/test_track_request.rb
|
149
166
|
- test/application_insights/test_telemetry_client.rb
|
150
167
|
- test/application_insights/test_unhandled_exception.rb
|
151
168
|
homepage: https://github.com/Microsoft/AppInsights-Ruby
|
@@ -202,6 +219,8 @@ test_files:
|
|
202
219
|
- test/application_insights/channel/test_synchronous_sender.rb
|
203
220
|
- test/application_insights/channel/test_telemetry_channel.rb
|
204
221
|
- test/application_insights/channel/test_telemetry_context.rb
|
222
|
+
- test/application_insights/mock_sender.rb
|
223
|
+
- test/application_insights/rack/test_track_request.rb
|
205
224
|
- test/application_insights/test_telemetry_client.rb
|
206
225
|
- test/application_insights/test_unhandled_exception.rb
|
207
226
|
has_rdoc:
|