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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NWFiNjNiNjljMTc2ODI1NzQ4M2VhNTlmODU2MTcxZTFlZjliMTA2Yw==
4
+ ODhmYmI4Y2ExYTdmNTAyOGY3MDAyNGM3NDI0N2ViMmM0NmMzYTkzNg==
5
5
  data.tar.gz: !binary |-
6
- NGM3NzEwNDZkYzI1MWEwNjRiNTMwNWMyOGJlYTkwODA5MjM2MmI2Yw==
6
+ NjY2MmVkYzcwYzFmZjYzYzMyZjYzZmIwMTBjOTliZjBlNzc2NGUyZA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- MTU5OTgzY2QzMWMwOTJmYTM3YTZiZWNlYTg4NmQ0NWM4ODkzYjk2ZTI4MTQ4
10
- NzJmMDJiNTQxNTA0YzkwNjBmYTEwYTc3ZjBhNmU5YjQxZTRmODA2ZTU4Zjgz
11
- ZWJhMzAzZWI2NmYyNzRjMzg0ZDNmZmQwNjZkNmY3MGU2NjYzN2U=
9
+ YTc3NGQyNDM1N2I4MjNkYmUxOTRlMTNhNDdkNWU0MGY1ZjZlMDNhZDQ5NmFi
10
+ MGQ4MmViMGE0YzU1MTRmMjI5M2Q2NmE0YTcyNmNmMjRjNTQzNWY2ZWFkNGE4
11
+ MTlhNGMwMDBkYTc1NjczN2I0YWViOTQyNWRjZjRiM2QwM2I1ZDc=
12
12
  data.tar.gz: !binary |-
13
- NjA1MDU4ZWY2Mjc0MDZiZTYxM2Y0ZTk3MzQ2MjI2NzdjZDU1Y2FkZTY0MGY5
14
- ZDBkNDQ1ZGQ4Zjk5M2MwNThmM2VjNjg4ZWNlNjE3ZjMxMjdkMWI3NDRiYmMy
15
- YTc5ZDcxYmUwZWM1MzNjMDVhYTU1ZmEwYTFjNDUxOTUxMDYyY2M=
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
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency 'rake', '~> 10.0'
25
25
  spec.add_development_dependency 'yard', '~> 0.8.7.6'
26
26
  spec.add_development_dependency 'redcarpet', '~> 3.2.2'
27
+ spec.add_development_dependency 'rack', '>= 1.0.0'
27
28
  end
@@ -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
- @lock_send_remaining_time = Mutex.new
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
- @lock_send_remaining_time.synchronize do
45
- # only maintain one working thread at one time
46
- if !@work_thread
47
- local_send_interval = (@send_interval < 0.1) ? 0.1 : @send_interval
48
- @send_remaining_time = (@send_time < local_send_interval) ? local_send_interval : @send_time
49
- @work_thread = Thread.new do
50
- run
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
- stop
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
- @lock_send_remaining_time.synchronize do
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
- @lock_send_remaining_time.synchronize do
110
- @send_remaining_time -= local_send_interval
111
- # Check queue as well to avoid missing any 'start' notification occurred during waiting
112
- if @send_remaining_time <= 0 && local_queue.empty?
113
- @work_thread = nil
114
- break
115
- end
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
@@ -53,7 +53,7 @@ module ApplicationInsights
53
53
  end
54
54
 
55
55
  # Indicates whether the queue is empty.
56
- # @return [true, false] true if the queue is empty
56
+ # @return [Boolean] true if the queue is empty
57
57
  def empty?
58
58
  @queue.empty?
59
59
  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
- compressed_data = compress(data_to_send.to_json)
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.getutc.strftime('%FT%T.%6NZ'),
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
- attr_reader :properties
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
- :message => name || 'Null',
168
- :properties => options.fetch(:properties) { {} }
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.context.instrumentation_key = instrumentation_key
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,3 +1,3 @@
1
1
  module ApplicationInsights
2
- VERSION = '0.3.0'
2
+ VERSION = '0.5.0'
3
3
  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
@@ -36,7 +36,7 @@ class TestAsynchronousSender < Test::Unit::TestCase
36
36
  sender.invoke_base_start = true
37
37
  queue.push 1
38
38
  assert_not_nil sender.work_thread
39
- sleep 3.0
39
+ sleep 5.0
40
40
  assert_nil sender.work_thread
41
41
  end
42
42
 
@@ -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.data
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 = MockSender.new
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.3.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-01-15 00:00:00.000000000 Z
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: