application_insights 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +9 -9
  2. data/LICENSE.txt +7 -18
  3. data/README.md +42 -7
  4. data/Rakefile +6 -1
  5. data/application_insights.gemspec +2 -0
  6. data/lib/application_insights.rb +1 -0
  7. data/lib/application_insights/channel/asynchronous_queue.rb +51 -0
  8. data/lib/application_insights/channel/asynchronous_sender.rb +121 -0
  9. data/lib/application_insights/channel/contracts/application.rb +3 -2
  10. data/lib/application_insights/channel/contracts/data.rb +3 -2
  11. data/lib/application_insights/channel/contracts/data_point.rb +15 -10
  12. data/lib/application_insights/channel/contracts/device.rb +42 -28
  13. data/lib/application_insights/channel/contracts/envelope.rb +39 -27
  14. data/lib/application_insights/channel/contracts/event_data.rb +6 -6
  15. data/lib/application_insights/channel/contracts/exception_data.rb +9 -8
  16. data/lib/application_insights/channel/contracts/exception_details.rb +15 -11
  17. data/lib/application_insights/channel/contracts/internal.rb +6 -4
  18. data/lib/application_insights/channel/contracts/location.rb +3 -2
  19. data/lib/application_insights/channel/contracts/message_data.rb +6 -5
  20. data/lib/application_insights/channel/contracts/metric_data.rb +3 -3
  21. data/lib/application_insights/channel/contracts/operation.rb +12 -8
  22. data/lib/application_insights/channel/contracts/page_view_data.rb +12 -10
  23. data/lib/application_insights/channel/contracts/remote_dependency_data.rb +27 -19
  24. data/lib/application_insights/channel/contracts/request_data.rb +15 -12
  25. data/lib/application_insights/channel/contracts/session.rb +9 -6
  26. data/lib/application_insights/channel/contracts/stack_frame.rb +9 -6
  27. data/lib/application_insights/channel/contracts/user.rb +12 -8
  28. data/lib/application_insights/channel/event.rb +64 -0
  29. data/lib/application_insights/channel/queue_base.rb +24 -10
  30. data/lib/application_insights/channel/sender_base.rb +38 -9
  31. data/lib/application_insights/channel/synchronous_queue.rb +16 -5
  32. data/lib/application_insights/channel/synchronous_sender.rb +4 -2
  33. data/lib/application_insights/channel/telemetry_channel.rb +26 -7
  34. data/lib/application_insights/channel/telemetry_context.rb +29 -10
  35. data/lib/application_insights/telemetry_client.rb +84 -15
  36. data/lib/application_insights/unhandled_exception.rb +48 -0
  37. data/lib/application_insights/version.rb +1 -1
  38. data/test/application_insights/channel/test_asynchronous_queue.rb +68 -0
  39. data/test/application_insights/channel/test_asynchronous_sender.rb +81 -0
  40. data/test/application_insights/channel/test_event.rb +53 -0
  41. data/test/application_insights/channel/test_queue_base.rb +10 -9
  42. data/test/application_insights/channel/test_sender_base.rb +0 -9
  43. data/test/application_insights/test_telemetry_client.rb +5 -0
  44. data/test/application_insights/test_unhandled_exception.rb +42 -0
  45. metadata +44 -3
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- Nzg1ZmY4ZmVkNGVmNzY4MTY1ZmU5MTgxMmM0NGRmNGJlMGUwZTAxYw==
4
+ NWFiNjNiNjljMTc2ODI1NzQ4M2VhNTlmODU2MTcxZTFlZjliMTA2Yw==
5
5
  data.tar.gz: !binary |-
6
- NzY2YjU4OTM1NzAzNGE4ZDk4NzhlZDgwYWI3Y2VmYzMzOGVhOWM0OA==
7
- SHA512:
6
+ NGM3NzEwNDZkYzI1MWEwNjRiNTMwNWMyOGJlYTkwODA5MjM2MmI2Yw==
7
+ !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZjI2ZTM5MWUyZDViNjFhYzY0MjUzZmQyZTI1YTFhYTE1MjYwYzYxNTA4ZjU3
10
- MDU2Zjk4NWRhODkzY2IzNTA5NjM1M2Q2Njg0OTkwNjliNDY4YjE0NjQyN2Vi
11
- ZDg4YzI5ZTg2NjYwZGQyNjUzMzc4NDYxMmYxMGY1OWE0ZGJiYTU=
9
+ MTU5OTgzY2QzMWMwOTJmYTM3YTZiZWNlYTg4NmQ0NWM4ODkzYjk2ZTI4MTQ4
10
+ NzJmMDJiNTQxNTA0YzkwNjBmYTEwYTc3ZjBhNmU5YjQxZTRmODA2ZTU4Zjgz
11
+ ZWJhMzAzZWI2NmYyNzRjMzg0ZDNmZmQwNjZkNmY3MGU2NjYzN2U=
12
12
  data.tar.gz: !binary |-
13
- MDc0MTc2ZTdjODBjY2IzM2NiZGU2MjI1MWIzN2ZkYjdkMWE4NmVmODRlMGYw
14
- ZGJjYmM5NWQ0MDgzZWM1Nzg4ZDY0ZDFhNzEyMTBmMzdhNGEwNTFlNzhkMmI0
15
- M2MzNjFlYzU5NDk4OTRiNGIyMDEzZWNjNDU3MTcwMDkwYjZlN2M=
13
+ NjA1MDU4ZWY2Mjc0MDZiZTYxM2Y0ZTk3MzQ2MjI2NzdjZDU1Y2FkZTY0MGY5
14
+ ZDBkNDQ1ZGQ4Zjk5M2MwNThmM2VjNjg4ZWNlNjE3ZjMxMjdkMWI3NDRiYmMy
15
+ YTc5ZDcxYmUwZWM1MzNjMDVhYTU1ZmEwYTFjNDUxOTUxMDYyY2M=
data/LICENSE.txt CHANGED
@@ -1,22 +1,11 @@
1
- The MIT License (MIT)
1
+ AppInsights-Ruby
2
+ Copyright (c) Microsoft Corporation
3
+ All rights reserved.
2
4
 
3
- Copyright (c) 2014 Microsoft
5
+ MIT License
4
6
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
11
8
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
9
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
22
10
 
11
+ THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -27,7 +27,7 @@ Once installed, you can send telemetry to Application Insights. Here are a few s
27
27
  >**Note**: before you can send data to you will need an instrumentation key. Please see the [Getting an Application Insights Instrumentation Key](https://github.com/Microsoft/AppInsights-Home/wiki#getting-an-application-insights-instrumentation-key) section for more information.
28
28
 
29
29
 
30
- **Sending a simple event telemetry item**
30
+ ###Sending a simple event telemetry item###
31
31
  ```ruby
32
32
  require 'application_insights'
33
33
  tc = ApplicationInsights::TelemetryClient.new
@@ -36,7 +36,7 @@ tc.track_event 'My event'
36
36
  tc.flush
37
37
  ```
38
38
 
39
- **Sending an event telemetry item with custom properties and measurements**
39
+ ###Sending an event telemetry item with custom properties and measurements###
40
40
  ```ruby
41
41
  require 'application_insights'
42
42
  tc = ApplicationInsights::TelemetryClient.new
@@ -45,7 +45,7 @@ tc.track_event 'My event', :properties => { 'custom property' => 'some value' },
45
45
  tc.flush
46
46
  ```
47
47
 
48
- **Sending a trace telemetry item with custom properties**
48
+ ###Sending a trace telemetry item with custom properties###
49
49
  ```ruby
50
50
  require 'application_insights'
51
51
  tc = ApplicationInsights::TelemetryClient.new
@@ -54,7 +54,7 @@ tc.track_trace 'My trace statement', :properties => { 'custom property' => 'some
54
54
  tc.flush
55
55
  ```
56
56
 
57
- **Sending a metric telemetry item (without and with optional values)**
57
+ ###Sending a metric telemetry item (without and with optional values)###
58
58
  ```ruby
59
59
  require 'application_insights'
60
60
  tc = ApplicationInsights::TelemetryClient.new
@@ -65,7 +65,7 @@ tc.track_metric 'My metric', 42, :kind => ApplicationInsights::Channel::Contract
65
65
  tc.flush
66
66
  ```
67
67
 
68
- **Sending an exception telemetry item with custom properties and measurements**
68
+ ###Sending an exception telemetry item with custom properties and measurements###
69
69
  ```ruby
70
70
  require 'application_insights'
71
71
  tc = ApplicationInsights::TelemetryClient.new
@@ -78,7 +78,7 @@ end
78
78
  tc.flush
79
79
  ```
80
80
 
81
- **Configuring context for a telemetry client instance**
81
+ ###Configuring context for a telemetry client instance###
82
82
  ```ruby
83
83
  require 'application_insights'
84
84
  tc = ApplicationInsights::TelemetryClient.new
@@ -94,7 +94,7 @@ tc.track_trace 'My trace with context'
94
94
  tc.flush
95
95
  ```
96
96
 
97
- **Configuring channel related properties**
97
+ ###Configuring synchronous (default) channel properties###
98
98
  ```ruby
99
99
  require 'application_insights'
100
100
  tc = ApplicationInsights::TelemetryClient.new
@@ -104,5 +104,40 @@ tc.channel.queue.max_queue_length = 10
104
104
  tc.channel.sender.send_buffer_size = 5
105
105
  ```
106
106
 
107
+ ###Configuring an asynchronous channel instead of the synchronous default###
108
+ ```ruby
109
+ require 'application_insights'
110
+ sender = ApplicationInsights::Channel::AsynchronousSender.new
111
+ queue = ApplicationInsights::Channel::AsynchronousQueue.new sender
112
+ channel = ApplicationInsights::Channel::TelemetryChannel.new nil, queue
113
+ tc = ApplicationInsights::TelemetryClient.new channel
114
+ # Note: the event will be sent on a separate thread; if the app finishes before
115
+ # the thread finishes, the data is lost
116
+ tc.track_event 'My event'
117
+ ```
107
118
 
119
+ ###Configuring asynchronous channel properties###
120
+ ```ruby
121
+ require 'application_insights'
122
+ sender = ApplicationInsights::Channel::AsynchronousSender.new
123
+ queue = ApplicationInsights::Channel::AsynchronousQueue.new sender
124
+ channel = ApplicationInsights::Channel::TelemetryChannel.new nil, queue
125
+ tc = ApplicationInsights::TelemetryClient.new channel
126
+ # flush telemetry if we have 10 or more telemetry items in our queue
127
+ tc.channel.queue.max_queue_length = 10
128
+ # send telemetry to the service in batches of 5
129
+ tc.channel.sender.send_buffer_size = 5
130
+ # the background worker thread will be active for 5 seconds before it shuts down. if
131
+ # during this time items are picked up from the queue, the timer is reset.
132
+ tc.channel.sender.send_time = 5
133
+ # the background worker thread will poll the queue every 0.5 seconds for new items
134
+ tc.channel.sender.send_interval = 0.5
135
+ ```
108
136
 
137
+ ###Collecting unhandled exceptions###
138
+ ```ruby
139
+ require 'application_insights'
140
+ # setup unhandled exception handler
141
+ ApplicationInsights::UnhandledException.collect('<YOUR INSTRUMENTATION KEY GOES HERE>')
142
+ # raise an exception and this would be send to Application Insights Service
143
+ raise Exception, 'Boom!'
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
2
  require 'rake/testtask'
3
+ require 'yard'
3
4
 
4
5
  Rake::TestTask.new do |test|
5
6
  test.libs << 'test'
@@ -7,4 +8,8 @@ Rake::TestTask.new do |test|
7
8
  test.verbose = true
8
9
  end
9
10
 
10
- task :default => [ :test, :build ]
11
+ YARD::Rake::YardocTask.new do |task|
12
+ task.files = ['lib/**/*.rb', '-', 'LICENSE.txt', 'README.md']
13
+ end
14
+
15
+ task :default => [ :test, :build, :yard ]
@@ -22,4 +22,6 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_development_dependency 'bundler', '~> 1.7'
24
24
  spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'yard', '~> 0.8.7.6'
26
+ spec.add_development_dependency 'redcarpet', '~> 3.2.2'
25
27
  end
@@ -1,2 +1,3 @@
1
1
  require_relative 'application_insights/telemetry_client'
2
+ require_relative 'application_insights/unhandled_exception'
2
3
  require_relative 'application_insights/version'
@@ -0,0 +1,51 @@
1
+ require_relative 'event'
2
+ require_relative 'queue_base'
3
+
4
+ module ApplicationInsights
5
+ module Channel
6
+ # An asynchronous queue for use in conjunction with the {AsynchronousSender}. The queue
7
+ # will notify the sender that it needs to pick up items when it reaches {#max_queue_length}, or when the consumer
8
+ # calls {#flush} via the {#flush_notification} event.
9
+ # @example
10
+ # require 'application_insights'
11
+ # require 'thread'
12
+ # queue = ApplicationInsights::Channel::AsynchronousQueue.new nil
13
+ # Thread.new do
14
+ # sleep 1
15
+ # queue.push 1
16
+ # queue.flush
17
+ # end
18
+ # queue.flush_notification.wait
19
+ # queue.flush_notification.clear
20
+ # result = queue.pop
21
+ class AsynchronousQueue < QueueBase
22
+ # Initializes a new instance of the class.
23
+ # @param [SenderBase] sender the sender object that will be used in conjunction with this queue. In addition to
24
+ # the sender object must support a {AsynchronousSender#start} method which is invoked
25
+ # each time an item is pushed to the queue as well as use the {#flush_notification} event.
26
+ def initialize(sender)
27
+ @flush_notification = Event.new
28
+ super sender
29
+ end
30
+
31
+ # The flush notification {ApplicationInsights::Channel::Event} that the {#sender} will use to get notified
32
+ # that a flush is needed.
33
+ # @return [Event] object that the {#sender} can wait on.
34
+ attr_reader :flush_notification
35
+
36
+ # Adds the passed in item object to the queue and notifies the {#sender} to start an asynchronous send operation
37
+ # by calling {AsynchronousSender#start}.
38
+ # @param [Contracts::Envelope] item the telemetry envelope object to send to the service.
39
+ def push(item)
40
+ super item
41
+ @sender.start if @sender
42
+ end
43
+
44
+ # Flushes the current queue by notifying the {#sender} via the {#flush_notification} event.
45
+ def flush
46
+ @flush_notification.set
47
+ @sender.start if @sender
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,121 @@
1
+ require_relative 'sender_base'
2
+ require 'thread'
3
+
4
+ module ApplicationInsights
5
+ module Channel
6
+ # An asynchronous sender that works in conjunction with the {AsynchronousQueue}. The sender object will start a
7
+ # worker thread that will pull items from the {#queue}. The thread will be created when the client calls {#start} and
8
+ # will check for queue items every {#send_interval} seconds. The worker thread can also be forced to check the queue
9
+ # by setting the {AsynchronousQueue#flush_notification} event.
10
+ #
11
+ # - If no items are found, the thread will go back to sleep.
12
+ # - If items are found, the worker thread will send items to the specified service in batches of {#send_buffer_size}.
13
+ #
14
+ # If no queue items are found for {#send_time} seconds, the worker thread will shut down (and {#start} will
15
+ # need to be called again).
16
+ class AsynchronousSender < SenderBase
17
+ # Initializes a new instance of the class.
18
+ # @param [String] service_endpoint_uri the address of the service to send telemetry data to.
19
+ def initialize(service_endpoint_uri='https://dc.services.visualstudio.com/v2/track')
20
+ @send_interval = 1.0
21
+ @send_remaining_time = 0
22
+ @send_time = 3.0
23
+ @lock_send_remaining_time = Mutex.new
24
+ @work_thread = nil
25
+ super service_endpoint_uri
26
+ end
27
+
28
+ # The time span in seconds at which the the worker thread will check the {#queue} for items (defaults to: 1.0).
29
+ # @return [Fixnum] the interval in seconds.
30
+ attr_accessor :send_interval
31
+
32
+ # The time span in seconds for which the worker thread will stay alive if no items are found in the {#queue} (defaults to 3.0).
33
+ # @return [Fixnum] the interval in seconds.
34
+ attr_accessor :send_time
35
+
36
+ # The worker thread which checks queue items and send data every (#send_interval) seconds or upon flush.
37
+ # @return [Thread] the work thread
38
+ attr_reader :work_thread
39
+
40
+ # Calling this method will create a worker thread that checks the {#queue} every {#send_interval} seconds for
41
+ # a total duration of {#send_time} seconds for new items. If a worker thread has already been created, calling
42
+ # this method does nothing.
43
+ 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
51
+ end
52
+ @work_thread.abort_on_exception = false
53
+ end
54
+ end
55
+ end
56
+
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
+ private
66
+
67
+ def run
68
+ # save the queue locally
69
+ local_queue = @queue
70
+ if local_queue == nil
71
+ stop
72
+ return
73
+ end
74
+
75
+ # fix up the send interval (can't be lower than 100ms)
76
+ local_send_interval = (@send_interval < 0.1) ? 0.1 : @send_interval
77
+ while TRUE
78
+ while TRUE
79
+ # get at most @send_buffer_size items from the queue
80
+ counter = @send_buffer_size
81
+ data = []
82
+ while counter > 0
83
+ item = local_queue.pop
84
+ break if not item
85
+ data.push item
86
+ counter -= 1
87
+ end
88
+
89
+ # if we didn't get any items from the queue, we're done here
90
+ break if data.length == 0
91
+
92
+ # reset the send time
93
+ @lock_send_remaining_time.synchronize do
94
+ @send_remaining_time = @send_time
95
+ end
96
+
97
+ # finally send the data
98
+ send data
99
+ end
100
+
101
+ # wait at most @send_interval ms (or until we get signalled)
102
+ result = local_queue.flush_notification.wait local_send_interval
103
+ if result
104
+ local_queue.flush_notification.clear
105
+ next
106
+ end
107
+
108
+ # 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
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -17,8 +17,9 @@ module ApplicationInsights
17
17
 
18
18
  # Gets the ver property.
19
19
  def ver
20
- return @values['ai.application.ver'] if @values.key?('ai.application.ver')
21
- @defaults['ai.application.ver']
20
+ @values.fetch('ai.application.ver') {
21
+ @values['ai.application.ver'] = nil
22
+ }
22
23
  end
23
24
 
24
25
  # Sets the ver property.
@@ -19,8 +19,9 @@ module ApplicationInsights
19
19
 
20
20
  # Gets the base_type property.
21
21
  def base_type
22
- return @values['baseType'] if @values.key?('baseType')
23
- @defaults['baseType']
22
+ @values.fetch('baseType') {
23
+ @values['baseType'] = nil
24
+ }
24
25
  end
25
26
 
26
27
  # Sets the base_type property.
@@ -37,8 +37,9 @@ module ApplicationInsights
37
37
 
38
38
  # Gets the kind property.
39
39
  def kind
40
- return @values['kind'] if @values.key?('kind')
41
- @defaults['kind']
40
+ @values.fetch('kind') {
41
+ @values['kind'] = DataPointType::MEASUREMENT
42
+ }
42
43
  end
43
44
 
44
45
  # Sets the kind property.
@@ -62,8 +63,9 @@ module ApplicationInsights
62
63
 
63
64
  # Gets the count property.
64
65
  def count
65
- return @values['count'] if @values.key?('count')
66
- @defaults['count']
66
+ @values.fetch('count') {
67
+ @values['count'] = nil
68
+ }
67
69
  end
68
70
 
69
71
  # Sets the count property.
@@ -77,8 +79,9 @@ module ApplicationInsights
77
79
 
78
80
  # Gets the min property.
79
81
  def min
80
- return @values['min'] if @values.key?('min')
81
- @defaults['min']
82
+ @values.fetch('min') {
83
+ @values['min'] = nil
84
+ }
82
85
  end
83
86
 
84
87
  # Sets the min property.
@@ -92,8 +95,9 @@ module ApplicationInsights
92
95
 
93
96
  # Gets the max property.
94
97
  def max
95
- return @values['max'] if @values.key?('max')
96
- @defaults['max']
98
+ @values.fetch('max') {
99
+ @values['max'] = nil
100
+ }
97
101
  end
98
102
 
99
103
  # Sets the max property.
@@ -107,8 +111,9 @@ module ApplicationInsights
107
111
 
108
112
  # Gets the std_dev property.
109
113
  def std_dev
110
- return @values['stdDev'] if @values.key?('stdDev')
111
- @defaults['stdDev']
114
+ @values.fetch('stdDev') {
115
+ @values['stdDev'] = nil
116
+ }
112
117
  end
113
118
 
114
119
  # Sets the std_dev property.