application_insights 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +40 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +106 -0
  6. data/Rakefile +10 -0
  7. data/application_insights.gemspec +25 -0
  8. data/lib/application_insights.rb +2 -0
  9. data/lib/application_insights/channel/contracts/application.rb +35 -0
  10. data/lib/application_insights/channel/contracts/data.rb +47 -0
  11. data/lib/application_insights/channel/contracts/data_point.rb +125 -0
  12. data/lib/application_insights/channel/contracts/data_point_type.rb +16 -0
  13. data/lib/application_insights/channel/contracts/dependency_kind.rb +22 -0
  14. data/lib/application_insights/channel/contracts/dependency_source_type.rb +19 -0
  15. data/lib/application_insights/channel/contracts/device.rb +243 -0
  16. data/lib/application_insights/channel/contracts/envelope.rb +254 -0
  17. data/lib/application_insights/channel/contracts/event_data.rb +77 -0
  18. data/lib/application_insights/channel/contracts/exception_data.rb +105 -0
  19. data/lib/application_insights/channel/contracts/exception_details.rb +125 -0
  20. data/lib/application_insights/channel/contracts/internal.rb +51 -0
  21. data/lib/application_insights/channel/contracts/json_serializable.rb +59 -0
  22. data/lib/application_insights/channel/contracts/location.rb +35 -0
  23. data/lib/application_insights/channel/contracts/message_data.rb +76 -0
  24. data/lib/application_insights/channel/contracts/metric_data.rb +60 -0
  25. data/lib/application_insights/channel/contracts/operation.rb +83 -0
  26. data/lib/application_insights/channel/contracts/page_view_data.rb +109 -0
  27. data/lib/application_insights/channel/contracts/remote_dependency_data.rb +218 -0
  28. data/lib/application_insights/channel/contracts/request_data.rb +173 -0
  29. data/lib/application_insights/channel/contracts/session.rb +67 -0
  30. data/lib/application_insights/channel/contracts/severity_level.rb +25 -0
  31. data/lib/application_insights/channel/contracts/stack_frame.rb +91 -0
  32. data/lib/application_insights/channel/contracts/user.rb +83 -0
  33. data/lib/application_insights/channel/queue_base.rb +48 -0
  34. data/lib/application_insights/channel/sender_base.rb +46 -0
  35. data/lib/application_insights/channel/synchronous_queue.rb +28 -0
  36. data/lib/application_insights/channel/synchronous_sender.rb +13 -0
  37. data/lib/application_insights/channel/telemetry_channel.rb +81 -0
  38. data/lib/application_insights/channel/telemetry_context.rb +49 -0
  39. data/lib/application_insights/telemetry_client.rb +111 -0
  40. data/lib/application_insights/version.rb +3 -0
  41. data/test/application_insights.rb +9 -0
  42. data/test/application_insights/channel/contracts/test_application.rb +31 -0
  43. data/test/application_insights/channel/contracts/test_data.rb +44 -0
  44. data/test/application_insights/channel/contracts/test_data_point.rb +109 -0
  45. data/test/application_insights/channel/contracts/test_device.rb +200 -0
  46. data/test/application_insights/channel/contracts/test_envelope.rb +209 -0
  47. data/test/application_insights/channel/contracts/test_event_data.rb +62 -0
  48. data/test/application_insights/channel/contracts/test_exception_data.rb +85 -0
  49. data/test/application_insights/channel/contracts/test_exception_details.rb +106 -0
  50. data/test/application_insights/channel/contracts/test_internal.rb +44 -0
  51. data/test/application_insights/channel/contracts/test_location.rb +31 -0
  52. data/test/application_insights/channel/contracts/test_message_data.rb +66 -0
  53. data/test/application_insights/channel/contracts/test_metric_data.rb +50 -0
  54. data/test/application_insights/channel/contracts/test_operation.rb +70 -0
  55. data/test/application_insights/channel/contracts/test_page_view_data.rb +88 -0
  56. data/test/application_insights/channel/contracts/test_remote_dependency_data.rb +183 -0
  57. data/test/application_insights/channel/contracts/test_request_data.rb +153 -0
  58. data/test/application_insights/channel/contracts/test_session.rb +57 -0
  59. data/test/application_insights/channel/contracts/test_stack_frame.rb +83 -0
  60. data/test/application_insights/channel/contracts/test_user.rb +70 -0
  61. data/test/application_insights/channel/test_queue_base.rb +88 -0
  62. data/test/application_insights/channel/test_sender_base.rb +96 -0
  63. data/test/application_insights/channel/test_synchronous_queue.rb +42 -0
  64. data/test/application_insights/channel/test_synchronous_sender.rb +11 -0
  65. data/test/application_insights/channel/test_telemetry_channel.rb +102 -0
  66. data/test/application_insights/channel/test_telemetry_context.rb +72 -0
  67. data/test/application_insights/test_telemetry_client.rb +107 -0
  68. metadata +166 -0
@@ -0,0 +1,46 @@
1
+ require 'json'
2
+ require 'net/http'
3
+
4
+ module ApplicationInsights
5
+ module Channel
6
+ # The base class for all of our senders.
7
+ class SenderBase
8
+ # Initializes a new instance of the sender class.
9
+ def initialize(service_endpoint_uri)
10
+ raise ArgumentError, 'Service endpoint URI was required but not provided' unless service_endpoint_uri
11
+ @service_endpoint_uri = service_endpoint_uri
12
+ @queue = nil
13
+ @send_buffer_size = 100
14
+ end
15
+
16
+ # Gets the service endpoint URI property. This is where we send data to.
17
+ attr_accessor :service_endpoint_uri
18
+
19
+ # The queue that we will be draining
20
+ attr_accessor :queue
21
+
22
+ # Gets or sets the buffer size for a single batch of telemetry. This is the maximum number of items in a single service request we are going to send.
23
+ attr_accessor :send_buffer_size
24
+
25
+ # Sends the data to send list to the service immediately.
26
+ def send(data_to_send)
27
+ uri = URI(@service_endpoint_uri)
28
+ request = Net::HTTP::Post.new(uri.path, { 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8' })
29
+ request.body = data_to_send.to_json
30
+
31
+ response = Net::HTTP.start(uri.hostname, uri.port) do |http|
32
+ http.request(request)
33
+ end
34
+
35
+ case response
36
+ when Net::HTTPSuccess, Net::HTTPRedirection, Net::HTTPBadRequest
37
+ return
38
+ else
39
+ data_to_send.each do |item|
40
+ @queue.push item
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'queue_base'
2
+
3
+ module ApplicationInsights
4
+ module Channel
5
+ # A synchronous queue for use in conjunction with the SynchronousSender. The queue will flush either when it reaches max_queue_length or when someone calls flush().
6
+ class SynchronousQueue < QueueBase
7
+ # Initializes a new instance of the synchronous queue class.
8
+ def initialize(sender)
9
+ super sender
10
+ end
11
+
12
+ # Flushes the current queue to the passed in sender.
13
+ def flush
14
+ while TRUE
15
+ # get at most send_buffer_size items and send them
16
+ data = []
17
+ while data.length < @sender.send_buffer_size
18
+ item = pop()
19
+ break if not item
20
+ data.push item
21
+ end
22
+ break if data.length == 0
23
+ @sender.send(data)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'sender_base'
2
+
3
+ module ApplicationInsights
4
+ module Channel
5
+ # A synchronous sender that works in conjunction with SynchronousQueue.
6
+ class SynchronousSender < SenderBase
7
+ # Initializes a new instance of the synchronous sender class.
8
+ def initialize(service_endpoint_uri='http://dc.services.visualstudio.com/v2/track')
9
+ super service_endpoint_uri
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,81 @@
1
+ require_relative 'telemetry_context'
2
+ require_relative 'synchronous_queue'
3
+ require_relative 'synchronous_sender'
4
+ require_relative 'contracts/envelope'
5
+ require_relative 'contracts/data'
6
+ require_relative 'contracts/internal'
7
+
8
+ module ApplicationInsights
9
+ module Channel
10
+ # The telemetry channel is responsible for constructing an envelope an sending it.
11
+ class TelemetryChannel
12
+ # Initializes a new instance of the telemetry channel class.
13
+ def initialize(context=nil, queue=nil)
14
+ @context = context || TelemetryContext.new
15
+ @queue = queue || SynchronousQueue.new(SynchronousSender.new)
16
+ end
17
+
18
+ # Gets the context associated with this channel.
19
+ attr_reader :context
20
+
21
+ # Gets the queue associated with this channel.
22
+ attr_reader :queue
23
+
24
+ # Gets the sender associated with this channel.
25
+ def sender
26
+ @queue.sender
27
+ end
28
+
29
+ # Flushes the current queue.
30
+ def flush
31
+ @queue.flush
32
+ end
33
+
34
+ # Writes the passed in data to the sending queue.
35
+ def write(data, context=nil)
36
+ local_context = context || @context
37
+ raise ArgumentError, 'Context was required but not provided' unless local_context
38
+ data_type = data.class.name.gsub(/^.*::/, '')
39
+ set_properties data, local_context
40
+ data_attributes = {
41
+ :base_type => data_type,
42
+ :base_data => data
43
+ }
44
+ envelope_attributes = {
45
+ :name => 'Microsoft.ApplicationInsights.' + data_type[0..-5],
46
+ :time => Time.now.getutc.strftime('%FT%T.%6NZ'),
47
+ :i_key => local_context.instrumentation_key,
48
+ :tags => get_tags(local_context),
49
+ :data => Contracts::Data.new(data_attributes)
50
+ }
51
+ envelope = Contracts::Envelope.new envelope_attributes
52
+ @queue.push(envelope)
53
+ end
54
+
55
+ private
56
+
57
+ def get_tags(context)
58
+ hash = {}
59
+ internal_context_attributes = {
60
+ :sdk_version => 'rb:0.1.0'
61
+ }
62
+ internal_context = Contracts::Internal.new internal_context_attributes
63
+ contexts = [ internal_context, context.application, context.device, context.user, context.session, context.location, context.operation ]
64
+ contexts.each { |c| hash.merge!(c.to_h) if c }
65
+ hash
66
+ end
67
+
68
+ def set_properties(data, context)
69
+ if context.properties
70
+ properties = data.properties || {}
71
+ context.properties.each do |key, value|
72
+ unless properties.key?(key)
73
+ properties[key] = value
74
+ end
75
+ end
76
+ data.properties = properties
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,49 @@
1
+ require_relative 'contracts/application'
2
+ require_relative 'contracts/device'
3
+ require_relative 'contracts/user'
4
+ require_relative 'contracts/session'
5
+ require_relative 'contracts/operation'
6
+ require_relative 'contracts/location'
7
+
8
+ module ApplicationInsights
9
+ module Channel
10
+ # Represents a context for sending telemetry to the Application Insights service.
11
+ class TelemetryContext
12
+ # Initializes a new instance of the TelemetryContext class.
13
+ def initialize
14
+ @instrumentation_key = nil
15
+ @application = Contracts::Application.new
16
+ @device = Contracts::Device.new
17
+ @user = Contracts::User.new
18
+ @session = Contracts::Session.new
19
+ @operation = Contracts::Operation.new
20
+ @location = Contracts::Location.new
21
+ @properties = {}
22
+ end
23
+
24
+ # Gets or sets the instrumentation key.
25
+ attr_accessor :instrumentation_key
26
+
27
+ # Gets or sets the application context.
28
+ attr_accessor :application
29
+
30
+ # Gets or sets the device context.
31
+ attr_accessor :device
32
+
33
+ # Gets or sets the user context.
34
+ attr_accessor :user
35
+
36
+ # Gets or sets the session context.
37
+ attr_accessor :session
38
+
39
+ # Gets or sets the operation context.
40
+ attr_accessor :operation
41
+
42
+ # Gets or sets the location context.
43
+ attr_accessor :location
44
+
45
+ # Gets a dictionary of application-defined property values.
46
+ attr_reader :properties
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,111 @@
1
+ require_relative 'channel/telemetry_context'
2
+ require_relative 'channel/telemetry_channel'
3
+ require_relative 'channel/contracts/page_view_data'
4
+ require_relative 'channel/contracts/exception_data'
5
+ require_relative 'channel/contracts/exception_details'
6
+ require_relative 'channel/contracts/event_data'
7
+ require_relative 'channel/contracts/data_point'
8
+ require_relative 'channel/contracts/data_point_type'
9
+ require_relative 'channel/contracts/metric_data'
10
+ require_relative 'channel/contracts/message_data'
11
+
12
+ module ApplicationInsights
13
+ # The telemetry client used for sending all types of telemetry.
14
+ class TelemetryClient
15
+ # Initializes a new instance of the TelemetryClient class.
16
+ def initialize(telemetry_channel = nil)
17
+ @context = Channel::TelemetryContext.new
18
+ @channel = telemetry_channel || Channel::TelemetryChannel.new
19
+ end
20
+
21
+ # Gets the context associated with this telemetry client.
22
+ attr_reader :context
23
+
24
+ # Gets the channel associated with this telemetry client.
25
+ attr_reader :channel
26
+
27
+ # Send information about the page viewed in the application.
28
+ def track_page_view(name, url, options={})
29
+ data_attributes = {
30
+ :name => name || 'Null',
31
+ :url => url,
32
+ :duration => options[:duration],
33
+ :properties => options.fetch(:properties) { {} },
34
+ :measurements => options.fetch(:measurements) { {} }
35
+ }
36
+ data = Channel::Contracts::PageViewData.new data_attributes
37
+ self.channel.write(data, self.context)
38
+ end
39
+
40
+ # Send an ExceptionTelemetry object for display in Diagnostic Search.
41
+ def track_exception(exception, options={})
42
+ if exception.is_a? Exception
43
+ details_attributes = {
44
+ :id => 1,
45
+ :outer_id => 0,
46
+ :type_name => exception.class,
47
+ :message => exception.message,
48
+ :has_full_stack => true,
49
+ :stack => exception.backtrace.join("\n")
50
+ }
51
+ details = Channel::Contracts::ExceptionDetails.new details_attributes
52
+
53
+ data_attributes = {
54
+ :handled_at => 'UserCode',
55
+ :exceptions => [ details ],
56
+ :properties => options.fetch(:properties) { {} },
57
+ :measurements => options.fetch(:measurements) { {} }
58
+ }
59
+ data = Channel::Contracts::ExceptionData.new data_attributes
60
+ self.channel.write(data, self.context)
61
+ end
62
+ end
63
+
64
+ # Send an EventTelemetry object for display in Diagnostic Search and aggregation in Metrics Explorer.
65
+ def track_event(name, options={})
66
+ data_attributes = {
67
+ :name => name || 'Null',
68
+ :properties => options.fetch(:properties) { {} },
69
+ :measurements => options.fetch(:measurements) { {} }
70
+ }
71
+ data = Channel::Contracts::EventData.new data_attributes
72
+ self.channel.write(data, self.context)
73
+ end
74
+
75
+ # Send a MetricTelemetry object for aggregation in Metric Explorer.
76
+ def track_metric(name, value, options={})
77
+ data_point_attributes = {
78
+ :name => name || 'Null',
79
+ :value => value || 0,
80
+ :kind => options.fetch(:type) { Channel::Contracts::DataPointType::AGGREGATION },
81
+ :count => options[:count],
82
+ :min => options[:min],
83
+ :max => options[:max],
84
+ :std_dev => options[:std_dev]
85
+ }
86
+ data_point = Channel::Contracts::DataPoint.new data_point_attributes
87
+
88
+ data_attributes = {
89
+ :metrics => [ data_point ],
90
+ :properties => options.fetch(:measurements) { {} }
91
+ }
92
+ data = Channel::Contracts::MetricData.new data_attributes
93
+ self.channel.write(data, self.context)
94
+ end
95
+
96
+ # Send a trace message for display in Diagnostic Search.
97
+ def track_trace(name, options={})
98
+ data_attributes = {
99
+ :message => name || 'Null',
100
+ :properties => options.fetch(:measurements) { {} }
101
+ }
102
+ data = Channel::Contracts::MessageData.new data_attributes
103
+ self.channel.write(data, self.context)
104
+ end
105
+
106
+ # Flushes the current queue.
107
+ def flush
108
+ self.channel.flush
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ module ApplicationInsights
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,9 @@
1
+ require 'find'
2
+
3
+ test = File.expand_path('../application_insights', __FILE__)
4
+ Find.find test do |path|
5
+ if FileTest.file? path
6
+ require path
7
+ end
8
+ end
9
+
@@ -0,0 +1,31 @@
1
+ require_relative '../../../../lib/application_insights/channel/contracts/application'
2
+ require 'test/unit'
3
+
4
+ include ApplicationInsights::Channel
5
+
6
+ class TestApplication < Test::Unit::TestCase
7
+ def test_initialize
8
+ item = Contracts::Application.new
9
+ assert_not_nil item
10
+ end
11
+
12
+ def test_ver_works_as_expected
13
+ expected = 'Test string'
14
+ item = Contracts::Application.new
15
+ item.ver = expected
16
+ actual = item.ver
17
+ assert_equal expected, actual
18
+ expected = 'Other string'
19
+ item.ver = expected
20
+ actual = item.ver
21
+ assert_equal expected, actual
22
+ end
23
+
24
+ def test_to_json_works_as_expected
25
+ item = Contracts::Application.new
26
+ item.ver = 'Test string'
27
+ actual = item.to_json
28
+ expected = '{"ai.application.ver":"Test string"}'
29
+ assert_equal expected, actual
30
+ end
31
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../../../../lib/application_insights/channel/contracts/data'
2
+ require 'test/unit'
3
+
4
+ include ApplicationInsights::Channel
5
+
6
+ class TestData < Test::Unit::TestCase
7
+ def test_initialize
8
+ item = Contracts::Data.new
9
+ assert_not_nil item
10
+ end
11
+
12
+ def test_base_type_works_as_expected
13
+ expected = 'Test string'
14
+ item = Contracts::Data.new
15
+ item.base_type = expected
16
+ actual = item.base_type
17
+ assert_equal expected, actual
18
+ expected = 'Other string'
19
+ item.base_type = expected
20
+ actual = item.base_type
21
+ assert_equal expected, actual
22
+ end
23
+
24
+ def test_base_data_works_as_expected
25
+ expected = { 'key' => 'value' }
26
+ item = Contracts::Data.new
27
+ item.base_data = expected
28
+ actual = item.base_data
29
+ assert_equal expected, actual
30
+ expected = { 'key' => 'value' }
31
+ item.base_data = expected
32
+ actual = item.base_data
33
+ assert_equal expected, actual
34
+ end
35
+
36
+ def test_to_json_works_as_expected
37
+ item = Contracts::Data.new
38
+ item.base_type = 'Test string'
39
+ item.base_data = { 'key' => 'value' }
40
+ actual = item.to_json
41
+ expected = '{"baseType":"Test string","baseData":{"key":"value"}}'
42
+ assert_equal expected, actual
43
+ end
44
+ end
@@ -0,0 +1,109 @@
1
+ require_relative '../../../../lib/application_insights/channel/contracts/data_point'
2
+ require 'test/unit'
3
+
4
+ include ApplicationInsights::Channel
5
+
6
+ class TestDataPoint < Test::Unit::TestCase
7
+ def test_initialize
8
+ item = Contracts::DataPoint.new
9
+ assert_not_nil item
10
+ end
11
+
12
+ def test_name_works_as_expected
13
+ expected = 'Test string'
14
+ item = Contracts::DataPoint.new
15
+ item.name = expected
16
+ actual = item.name
17
+ assert_equal expected, actual
18
+ expected = 'Other string'
19
+ item.name = expected
20
+ actual = item.name
21
+ assert_equal expected, actual
22
+ end
23
+
24
+ def test_kind_works_as_expected
25
+ expected = { 'key' => 'value' }
26
+ item = Contracts::DataPoint.new
27
+ item.kind = expected
28
+ actual = item.kind
29
+ assert_equal expected, actual
30
+ expected = { 'key' => 'value' }
31
+ item.kind = expected
32
+ actual = item.kind
33
+ assert_equal expected, actual
34
+ end
35
+
36
+ def test_value_works_as_expected
37
+ expected = 1.5
38
+ item = Contracts::DataPoint.new
39
+ item.value = expected
40
+ actual = item.value
41
+ assert_equal expected, actual
42
+ expected = 4.8
43
+ item.value = expected
44
+ actual = item.value
45
+ assert_equal expected, actual
46
+ end
47
+
48
+ def test_count_works_as_expected
49
+ expected = 42
50
+ item = Contracts::DataPoint.new
51
+ item.count = expected
52
+ actual = item.count
53
+ assert_equal expected, actual
54
+ expected = 13
55
+ item.count = expected
56
+ actual = item.count
57
+ assert_equal expected, actual
58
+ end
59
+
60
+ def test_min_works_as_expected
61
+ expected = 1.5
62
+ item = Contracts::DataPoint.new
63
+ item.min = expected
64
+ actual = item.min
65
+ assert_equal expected, actual
66
+ expected = 4.8
67
+ item.min = expected
68
+ actual = item.min
69
+ assert_equal expected, actual
70
+ end
71
+
72
+ def test_max_works_as_expected
73
+ expected = 1.5
74
+ item = Contracts::DataPoint.new
75
+ item.max = expected
76
+ actual = item.max
77
+ assert_equal expected, actual
78
+ expected = 4.8
79
+ item.max = expected
80
+ actual = item.max
81
+ assert_equal expected, actual
82
+ end
83
+
84
+ def test_std_dev_works_as_expected
85
+ expected = 1.5
86
+ item = Contracts::DataPoint.new
87
+ item.std_dev = expected
88
+ actual = item.std_dev
89
+ assert_equal expected, actual
90
+ expected = 4.8
91
+ item.std_dev = expected
92
+ actual = item.std_dev
93
+ assert_equal expected, actual
94
+ end
95
+
96
+ def test_to_json_works_as_expected
97
+ item = Contracts::DataPoint.new
98
+ item.name = 'Test string'
99
+ item.kind = { 'key' => 'value' }
100
+ item.value = 1.5
101
+ item.count = 42
102
+ item.min = 1.5
103
+ item.max = 1.5
104
+ item.std_dev = 1.5
105
+ actual = item.to_json
106
+ expected = '{"name":"Test string","kind":{"key":"value"},"value":1.5,"count":42,"min":1.5,"max":1.5,"stdDev":1.5}'
107
+ assert_equal expected, actual
108
+ end
109
+ end