application_insights 0.1.0

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