application_insights 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|