application_insights 0.5.5 → 0.5.6

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