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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +7 -1
- data/CONTRIBUTING.md +38 -0
- data/README.md +14 -0
- data/application_insights.gemspec +2 -0
- data/lib/application_insights/channel/contracts/operation.rb +1 -1
- data/lib/application_insights/channel/queue_base.rb +10 -2
- data/lib/application_insights/rack/track_request.rb +86 -19
- data/lib/application_insights/version.rb +1 -1
- data/test/application_insights/rack/test_track_request.rb +49 -9
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ff736cf446fd2077d43ff10cc8601b94a7a7f77
|
4
|
+
data.tar.gz: 59eae3793dbcb520e31b1237f387e22826eed167
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cedd551d6b62265d91f1f4168761ec934e7a14368968aa6d9ef168aa52bb873b5b8661130187a76231167b5f32917e3aa8b834970d3492bfadbba648693412e7
|
7
|
+
data.tar.gz: 961faba754c0501b03724ebce63a70e22b86c45c1dba49ade47cdbb160b2d1b8cb095c33386c62f4c513a80393a525f28d6bda4d47d82072dc576c3146fd8a3b
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -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.
|
data/CONTRIBUTING.md
CHANGED
@@ -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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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,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
|
-
|
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
|
93
|
-
|
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.
|
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-
|
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:
|