application_insights 0.5.5 → 0.5.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34054217c68c9151aca2ca20c8fd90eb395b16bd
4
- data.tar.gz: f710e913094d5f367301f1625b05cad33833670d
3
+ metadata.gz: 6ff736cf446fd2077d43ff10cc8601b94a7a7f77
4
+ data.tar.gz: 59eae3793dbcb520e31b1237f387e22826eed167
5
5
  SHA512:
6
- metadata.gz: 0e654c167f8964cf88f4a800cc250611435e674a6e52e36431929489f4f4c2381342fe4a1066f87b304f9b00e594b7f54cb0df4d516c490862f2f21ab6b1bd1e
7
- data.tar.gz: c8f9fecc51ab2db7de24997122df63166f9b840a7cb84696eb9672e5d7db7ea5a8787ee6d9192d15168a1ef85fd3a86ae92f41d3556caca036750f6f5b465f3f
6
+ metadata.gz: cedd551d6b62265d91f1f4168761ec934e7a14368968aa6d9ef168aa52bb873b5b8661130187a76231167b5f32917e3aa8b834970d3492bfadbba648693412e7
7
+ data.tar.gz: 961faba754c0501b03724ebce63a70e22b86c45c1dba49ade47cdbb160b2d1b8cb095c33386c62f4c513a80393a525f28d6bda4d47d82072dc576c3146fd8a3b
@@ -6,6 +6,7 @@ rvm:
6
6
  - 2.2.0
7
7
  - 2.3.3
8
8
  - 2.4.0
9
+ - 2.5.0
9
10
  - ruby-head
10
11
 
11
12
  cache: bundler
@@ -2,10 +2,16 @@
2
2
 
3
3
  This file needs to be updated with every significant pull request. It is used to write down release notes.
4
4
 
5
+ ## Version 0.5.6
6
+ * Expose request id to parent Rack application when using `ApplicationInsights::Rack::TrackRequest` middleware through `env['ApplicationInsights.request.id']`.
7
+ * Implement operation context functionality for `ApplicationInsights::Rack::TrackRequest`.
8
+ * Add functionality to accept a Request-Id header for telemetry correlation.
9
+ * Add operation context to request tracking middleware.
10
+
5
11
  ## Version 0.5.5
6
12
  * Add some basic logging when failed to send telemetry to the server
7
13
  * Add timestamp as an optional parameter to the TelemetryChannel::write() method
8
14
 
9
15
  ## Version 0.5.4
10
16
 
11
- Changelog started after this release.
17
+ Changelog started after this release.
@@ -14,6 +14,44 @@ Make sure you have bundler installed, you can install it by ```sudo gem install
14
14
 
15
15
  Run ```rake test```.
16
16
 
17
+ ## Releasing new version
18
+
19
+ This is for repository maintainers only:
20
+
21
+ 1. Create and merge develop->master PR https://github.com/Microsoft/ApplicationInsights-Ruby/compare/master...develop?expand=1
22
+ 2. Checkout latest `master`
23
+ ```
24
+ git checkout master
25
+ git pull
26
+ ```
27
+
28
+ 3. Remove old gem: `rm *.gem`
29
+ 4. [Build gem](https://github.com/Microsoft/ApplicationInsights-Ruby/blob/develop/CONTRIBUTING.md#build-gem)
30
+ 5. Push gem: `gem push application_insights-0.5.5.gem`
31
+ 6. Check gem on [rubygems](https://rubygems.org/gems/application_insights)
32
+ 7. Tag code:
33
+ ```
34
+ git tag -a v0.5.5
35
+ git push origin v0.5.5
36
+ ```
37
+ 8. Update description of [release](https://github.com/Microsoft/ApplicationInsights-Ruby/releases/edit/v0.5.5) from [CHANGELOG.md](https://github.com/Microsoft/ApplicationInsights-Ruby/blob/master/CHANGELOG.md)
38
+ 9. Create a branch off `develop` branch
39
+ ```
40
+ git checkout develop
41
+ git pull
42
+ git checkout -b releaseUpdates
43
+ git push --set-upstream origin releaseUpdates
44
+ ```
45
+ 10. Update version in `/lib/application_insights/version.rb`
46
+ 11. Create new entry for the next release in `/CHANGELOG.md`
47
+ 12. Push changes
48
+ ```
49
+ git add -A
50
+ git commit -m "post release updates"
51
+ git push
52
+ ```
53
+ 13. Submit releaseUpdates->develop PR: https://github.com/Microsoft/ApplicationInsights-Ruby/compare/develop...releaseUpdates?expand=1
54
+
17
55
  ## Contributing
18
56
 
19
57
  This project welcomes contributions and suggestions. Most contributions require you to
data/README.md CHANGED
@@ -145,3 +145,17 @@ use ApplicationInsights::Rack::TrackRequest, '<YOUR INSTRUMENTATION KEY GOES HER
145
145
  # For rails, suggest to set up this middleware in application.rb so that unhandled exceptions from controllers are also collected
146
146
  config.middleware.use 'ApplicationInsights::Rack::TrackRequest', '<YOUR INSTRUMENTATION KEY GOES HERE>', <buffer size>
147
147
  ```
148
+
149
+ #### Rerieving the Request-Id value from ApplicationInsights ####
150
+ ```ruby
151
+ # from time to time you may need to access a request's id from within your app
152
+ application_insights_request_id = env['ApplicationInsights.request.id']
153
+
154
+ # this can be used for a number of different purposes, including telemetry correlation
155
+ uri = URI('http://api.example.com/search/?q=test')
156
+
157
+ req = Net::HTTP::Get.new(uri)
158
+ req['Request-Id'] = "#{application_insights_request_id}1" if application_insights_request_id
159
+
160
+ Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(req) }
161
+ ```
@@ -25,4 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'redcarpet', '~> 3.2.2'
26
26
  spec.add_development_dependency 'rack', '>= 1.0.0'
27
27
  spec.add_development_dependency 'test-unit', '~> 3.0.8'
28
+ spec.add_development_dependency 'mocha', '~> 1.5.0'
29
+
28
30
  end
@@ -13,7 +13,7 @@ module ApplicationInsights::Channel::Contracts
13
13
  root_id: 'ai.operation.rootId',
14
14
  synthetic_source: 'ai.operation.syntheticSource',
15
15
  is_synthetic: 'ai.operation.isSynthetic',
16
- correlation_vector: "ai.operation.correlationVector"
16
+ correlation_vector: 'ai.operation.correlationVector'
17
17
  )
18
18
  end
19
19
  end
@@ -13,8 +13,7 @@ module ApplicationInsights
13
13
  def initialize(sender)
14
14
  @queue = Queue.new
15
15
  @max_queue_length = 500
16
- @sender = sender
17
- @sender.queue = self if sender
16
+ self.sender = sender
18
17
  end
19
18
 
20
19
  # The maximum number of items that will be held by the queue before the
@@ -27,6 +26,15 @@ module ApplicationInsights
27
26
  # @return [SenderBase] the sender object.
28
27
  attr_reader :sender
29
28
 
29
+ # Change the sender that is associated with this queue.
30
+ # @param [SenderBase] sender the sender object.
31
+ # @return [SenderBase] the sender object.
32
+ def sender=(sender)
33
+ @sender = sender
34
+ @sender.queue = self if sender
35
+ @sender
36
+ end
37
+
30
38
  # Adds the passed in item object to the queue and calls {#flush} if the
31
39
  # size of the queue is larger than {#max_queue_length}. This method does
32
40
  # nothing if the passed in item is nil.
@@ -1,4 +1,5 @@
1
1
  require 'rack'
2
+ require 'securerandom'
2
3
  require_relative '../channel/contracts/request_data'
3
4
  require_relative '../telemetry_client'
4
5
 
@@ -19,11 +20,23 @@ module ApplicationInsights
19
20
  @instrumentation_key = instrumentation_key
20
21
  @buffer_size = buffer_size
21
22
  @send_interval = send_interval
23
+
24
+ @sender = Channel::AsynchronousSender.new
25
+ @sender.send_interval = @send_interval
26
+ queue = Channel::AsynchronousQueue.new @sender
27
+ queue.max_queue_length = @buffer_size
28
+ @channel = Channel::TelemetryChannel.new nil, queue
29
+
30
+ @client = TelemetryClient.new @instrumentation_key, @channel
22
31
  end
23
32
 
24
33
  # Track requests and send data to Application Insights asynchronously.
25
34
  # @param [Hash] env the rack environment.
26
35
  def call(env)
36
+ # Build a request ID, incorporating one from our request if one exists.
37
+ request_id = request_id_header(env['HTTP_REQUEST_ID'])
38
+ env['ApplicationInsights.request.id'] = request_id
39
+
27
40
  start = Time.now
28
41
  begin
29
42
  status, headers, response = @app.call(env)
@@ -33,28 +46,17 @@ module ApplicationInsights
33
46
  end
34
47
  stop = Time.now
35
48
 
36
- unless @client
37
- sender = @sender || Channel::AsynchronousSender.new
38
- sender.send_interval = @send_interval
39
- queue = Channel::AsynchronousQueue.new sender
40
- queue.max_queue_length = @buffer_size
41
- channel = Channel::TelemetryChannel.new nil, queue
42
-
43
- @client = TelemetryClient.new @instrumentation_key, channel
44
- end
45
-
46
- request = ::Rack::Request.new env
47
- id = rand(16**32).to_s(16)
48
49
  start_time = start.iso8601(7)
49
50
  duration = format_request_duration(stop - start)
50
51
  success = status.to_i < 400
51
- options = {
52
- :name => "#{request.request_method} #{request.path}",
53
- :http_method => request.request_method,
54
- :url => request.url
55
- }
56
52
 
57
- @client.track_request id, start_time, duration, status, success, options
53
+ request = ::Rack::Request.new env
54
+ options = options_hash(request)
55
+
56
+ data = request_data(request_id, start_time, duration, status, success, options)
57
+ context = telemetry_context(request_id, env['HTTP_REQUEST_ID'])
58
+
59
+ @client.channel.write data, context, start_time
58
60
 
59
61
  if exception
60
62
  @client.track_exception exception, handled_at: 'Unhandled'
@@ -67,7 +69,10 @@ module ApplicationInsights
67
69
  private
68
70
 
69
71
  def sender=(sender)
70
- @sender = sender if sender.is_a? Channel::AsynchronousSender
72
+ if sender.is_a? Channel::AsynchronousSender
73
+ @sender = sender
74
+ @client.channel.queue.sender = @sender
75
+ end
71
76
  end
72
77
 
73
78
  def client
@@ -82,6 +87,68 @@ module ApplicationInsights
82
87
 
83
88
  Time.at(duration_seconds).gmtime.strftime("00.%H:%M:%S.%7N")
84
89
  end
90
+
91
+ def request_id_header(request_id)
92
+ valid_request_id_header = valid_request_id(request_id)
93
+
94
+ length = valid_request_id_header ? 5 : 10
95
+ id = SecureRandom.base64(length)
96
+
97
+ if valid_request_id_header
98
+ request_id_has_end = %w[. _].include?(request_id[-1])
99
+ request_id << '.' unless request_id_has_end
100
+
101
+ return "#{request_id}#{id}_"
102
+ end
103
+
104
+ "|#{id}."
105
+ end
106
+
107
+ def valid_request_id(request_id)
108
+ request_id && request_id[0] == '|'
109
+ end
110
+
111
+ def operation_id(id)
112
+ # Returns the root ID from the '|' to the first '.' if any.
113
+ root_start = id[0] == '|' ? 1 : 0
114
+
115
+ root_end = id.index('.')
116
+ root_end = root_end ? root_end - 1 : id.length - root_start
117
+
118
+ id[root_start..root_end]
119
+ end
120
+
121
+ def options_hash(request)
122
+ {
123
+ name: "#{request.request_method} #{request.path}",
124
+ http_method: request.request_method,
125
+ url: request.url
126
+ }
127
+ end
128
+
129
+ def request_data(request_id, start_time, duration, status, success, options)
130
+ Channel::Contracts::RequestData.new(
131
+ :id => request_id || 'Null',
132
+ :start_time => start_time || Time.now.iso8601(7),
133
+ :duration => duration || '0:00:00:00.0000000',
134
+ :response_code => status || 200,
135
+ :success => success == nil ? true : success,
136
+ :name => options[:name],
137
+ :http_method => options[:http_method],
138
+ :url => options[:url],
139
+ :properties => options[:properties] || {},
140
+ :measurements => options[:measurements] || {}
141
+ )
142
+ end
143
+
144
+ def telemetry_context(request_id, request_id_header)
145
+ context = Channel::TelemetryContext.new
146
+ context.instrumentation_key = @instrumentation_key
147
+ context.operation.id = operation_id(request_id)
148
+ context.operation.parent_id = request_id_header
149
+
150
+ context
151
+ end
85
152
  end
86
153
  end
87
154
  end
@@ -1,3 +1,3 @@
1
1
  module ApplicationInsights
2
- VERSION = '0.5.5'
2
+ VERSION = '0.5.6'.freeze
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'test/unit'
2
+ require 'mocha/test_unit'
2
3
  require 'rack/mock'
3
4
  require_relative '../mock_sender'
4
5
  require_relative '../../../lib/application_insights/rack/track_request'
@@ -20,8 +21,12 @@ class TestTrackRequest < Test::Unit::TestCase
20
21
  track_request = TrackRequest.new app, instrumentation_key, 500, 1
21
22
  track_request.send(:sender=, sender)
22
23
  start_time = Time.now
24
+
25
+ SecureRandom.expects(:base64).with(10).returns('y0NM2eOY/fnQPw==')
23
26
  result = track_request.call(env)
24
- assert_equal app.call(env), result
27
+
28
+ app_result = app.call(env)
29
+ assert_equal app_result, result
25
30
  sleep(sender.send_interval)
26
31
 
27
32
  assert_equal 1, sender.buffer.count
@@ -89,20 +94,14 @@ class TestTrackRequest < Test::Unit::TestCase
89
94
  send_interval = 5
90
95
  track_request = TrackRequest.new app, 'key', buffer_size, send_interval
91
96
  client = track_request.send(:client)
92
- # test lazy initialization
93
- assert_nil client
97
+ # test client initialization
98
+ assert_equal ApplicationInsights::TelemetryClient, client.class
94
99
 
95
100
  track_request.call(env)
96
101
  client = track_request.send(:client)
97
102
  channel = client.channel
98
103
  assert_equal buffer_size, channel.queue.max_queue_length
99
104
  assert_equal send_interval, channel.sender.send_interval
100
-
101
- track_request.call(env)
102
- client2 = track_request.send(:client)
103
- channel2 = client2.channel
104
- assert_same client, client2
105
- assert_same channel, channel2
106
105
  end
107
106
 
108
107
  def test_format_request_duration_less_than_a_day
@@ -139,4 +138,45 @@ class TestTrackRequest < Test::Unit::TestCase
139
138
  assert_equal 0, match['second'].to_i
140
139
  assert_equal 0, match['fraction'].to_i
141
140
  end
141
+
142
+ def test_request_id_is_generated_correctly
143
+ app = Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
144
+ url = "http://localhost:8080/foo?a=b"
145
+ http_method = 'PUT'
146
+ env = Rack::MockRequest.env_for(url, :method => http_method)
147
+ instrumentation_key = 'key'
148
+ sender = MockAsynchronousSender.new
149
+ track_request = TrackRequest.new app, instrumentation_key, 500, 0
150
+ track_request.send(:sender=, sender)
151
+
152
+ # ignores ids that don't begin with | (16 chars)
153
+ env['HTTP_REQUEST_ID'] = 'ab456_1.ea6741a'
154
+ SecureRandom.expects(:base64).with(10).returns('y0NM2eOY/fnQPw==')
155
+ track_request.call(env)
156
+ assert_equal '|y0NM2eOY/fnQPw==.', env['ApplicationInsights.request.id']
157
+
158
+ # appends to ids with a dot (8 chars)
159
+ env['HTTP_REQUEST_ID'] = '|1234.'
160
+ SecureRandom.expects(:base64).with(5).returns('eXsMFHs=')
161
+ track_request.call(env)
162
+ assert_equal '|1234.eXsMFHs=_', env['ApplicationInsights.request.id']
163
+
164
+ # appends to ids with an underscore (8 chars)
165
+ env['HTTP_REQUEST_ID'] = '|1234_'
166
+ SecureRandom.expects(:base64).with(5).returns('eXsMFHs=')
167
+ track_request.call(env)
168
+ assert_equal '|1234_eXsMFHs=_', env['ApplicationInsights.request.id']
169
+
170
+ # appends a dot if neither a dot or underscore are present (8 chars)
171
+ env['HTTP_REQUEST_ID'] = '|ab456_1.ea6741a'
172
+ SecureRandom.expects(:base64).with(5).returns('eXsMFHs=')
173
+ track_request.call(env)
174
+ assert_equal '|ab456_1.ea6741a.eXsMFHs=_', env['ApplicationInsights.request.id']
175
+
176
+ # generates a stand-alone id if one is not provided (16 chars)
177
+ env.delete('HTTP_REQUEST_ID')
178
+ SecureRandom.expects(:base64).with(10).returns('y0NM2eOY/fnQPw==')
179
+ track_request.call(env)
180
+ assert_equal '|y0NM2eOY/fnQPw==.', env['ApplicationInsights.request.id']
181
+ end
142
182
  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.5.5
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Microsoft
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-26 00:00:00.000000000 Z
11
+ date: 2018-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 3.0.8
83
+ - !ruby/object:Gem::Dependency
84
+ name: mocha
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.5.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.5.0
83
97
  description: This project extends the Application Insights API surface to support
84
98
  Ruby.
85
99
  email: