skylight 0.9.3 → 0.9.4

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: 1564afa767483c0b2e5ce584e197f5ea3f72933a
4
- data.tar.gz: f20f7f995bfff398ee791d73066cd0495a8bef7d
3
+ metadata.gz: 3a889a7a09786acddcdf866faaf84ed4862909f4
4
+ data.tar.gz: 5e2f48fcfcea4166894f19422b435eb82d809856
5
5
  SHA512:
6
- metadata.gz: 61bb59ac6c2c256370adcc09962b3555c1b68bdedf6590926283f4398900a3a83c852b7686f693d4068666b11f6145fa70b0e3c8e82e66436c5fdf490a2dc728
7
- data.tar.gz: e1dc0699037df80f0127058b15263cf718f7e1e9fe441a00296e0edd5200d1f938afb9c1e2a823c3fad5fc2a97aed9c4e15ce3566d853db85ad7488851824680
6
+ metadata.gz: 59381efc987947df3b1a8bff022beb0a61288c02b41124b861ea3923db126a4b38fb74100ad67b8efac86b694843fa7c70545bef02be511c9b1892d265a998fa
7
+ data.tar.gz: 7c433c0b9b74b350955aa27641b3ae1ac6d1a2ddd8d018f71f94302078d04e1a000c697b8f339663d8af12d2417df4b973c5f786386cb14a426865eaa9b9ea95
@@ -1,3 +1,13 @@
1
+ ## 0.9.4 (November 23, 2015)
2
+
3
+ * [FEATURE] Added instrumentation for official Mongo Ruby Driver (utilized by Mongoid 5+). Add 'mongo' to probes list to enable.
4
+ * [BUGFIX] SQL lexer now handles indentifiers beginning with underscores.
5
+ * [BUGFIX] Excon instrumentation now works correctly.
6
+ * [BUGFIX] Graceful handling of native agent failures on old OS X versions.
7
+ * [IMPROVEMENT] Freeze some more strings for (likely very minor) performance improvements.
8
+ * [IMPROVEMENT] Better error messages when sockdir is an NFS mount.
9
+ * [IMPROVEMENT] On OS X, ensure that Xcode license has been approved before trying to build native agent.
10
+
1
11
  ## 0.9.3 (November 17, 2015)
2
12
 
3
13
  * [BUGFIX] Update SQL lexer to handle more common queries
@@ -46,6 +46,21 @@ def fail(msg, type=:error)
46
46
  end
47
47
  end
48
48
 
49
+ # Check that Xcode license has been approved
50
+ # Based on Homebrew's implementation
51
+ # https://github.com/Homebrew/homebrew/blob/03708b016755847facc4f19a43ee9f7a44141ed7/Library/Homebrew/cmd/doctor.rb#L1183
52
+ if Platform::OS == 'darwin'
53
+ # If the user installs Xcode-only, they have to approve the
54
+ # license or no "xc*" tool will work.
55
+ if `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$?.success?
56
+ fail <<-EOS
57
+ You have not agreed to the Xcode license and so we are unable to build the native agent.
58
+ To resolve this, you can agree to the license by opening Xcode.app or running:
59
+ sudo xcodebuild -license
60
+ EOS
61
+ end
62
+ end
63
+
49
64
  #
50
65
  # === Setup paths
51
66
  #
@@ -1,6 +1,6 @@
1
1
  ---
2
- version: "0.7.0-629fc27"
2
+ version: "0.7.0-ffe066b"
3
3
  checksums:
4
- x86-linux: "cd601750d0250d9e2cfed96fa9d4ac642a1b22053cf5ee5a7523da2f583fdf2d"
5
- x86_64-linux: "eef6301799be9e1e6e70f71c59ac1449f52f65cf1dfe5c996762a42f31e08d5f"
6
- x86_64-darwin: "62b19c0f34e983d8d752b1b9514d427cc019cfdf2f3f6b2f1424cf06710330d8"
4
+ x86-linux: "311cf34d25383d1a0c7f4611919ac78e49cbd2e227532ea9aa2b308212e9fca9"
5
+ x86_64-linux: "9d4969487f16e0f1d2ed6410dc4ea408e28ec4ea157c1a56c93b0bf596600461"
6
+ x86_64-darwin: "4e40e9a08efa8953ed1afca07c684385fa374833a057136057bf36ef41cce55e"
@@ -39,6 +39,7 @@ module Skylight
39
39
 
40
40
  # == Instrumenter ==
41
41
  "IGNORED_ENDPOINT" => :'ignored_endpoint',
42
+ "IGNORED_ENDPOINTS" => :'ignored_endpoints',
42
43
  "SQL_MODE" => :'sql_mode',
43
44
 
44
45
  # == Skylight Remote ==
@@ -268,6 +269,8 @@ module Skylight
268
269
  end
269
270
  end
270
271
 
272
+ # FIXME: Why not set the sockdir_path and pidfile_path explicitly?
273
+ # That way we don't have to keep this in sync with the Rust repo.
271
274
  sockdir_path = self[:'daemon.sockdir_path'] || File.expand_path('.')
272
275
  pidfile_path = self[:'daemon.pidfile_path'] || File.expand_path('skylight.pid', sockdir_path)
273
276
 
@@ -283,11 +286,11 @@ module Skylight
283
286
  FileUtils.mkdir_p sockdir_path
284
287
 
285
288
  if File.exist?(pidfile)
286
- if !FileTest.writable?(pidfile)
289
+ unless FileTest.writable?(pidfile)
287
290
  raise ConfigError, "File `#{pidfile}` not writable. Please set daemon.pidfile_path or daemon.sockdir_path in your config to a writable path"
288
291
  end
289
292
  else
290
- if !FileTest.writable?(pidfile_root)
293
+ unless FileTest.writable?(pidfile_root)
291
294
  raise ConfigError, "Directory `#{pidfile_root}` not writable. Please set daemon.pidfile_path or daemon.sockdir_path in your config to a writable path"
292
295
  end
293
296
  end
@@ -295,6 +298,10 @@ module Skylight
295
298
  unless FileTest.writable?(sockdir_path)
296
299
  raise ConfigError, "Directory `#{sockdir_path}` not writable. Please set daemon.sockdir_path in your config to a writable path"
297
300
  end
301
+
302
+ if check_nfs(pidfile)
303
+ raise ConfigError, "Directory `#{sockdir_path}` is an NFS mount and will not allow sockets. Please set daemon.sockdir_path in your config to a non-NFS path."
304
+ end
298
305
  end
299
306
 
300
307
  def key?(key)
@@ -420,8 +427,16 @@ authentication: #{self[:authentication]}
420
427
  def ignored_endpoints
421
428
  @ignored_endpoints ||=
422
429
  begin
430
+ ignored_endpoints = get(:'ignored_endpoints')
431
+
432
+ # If, for some odd reason you have a comma in your endpoint name, use the
433
+ # YML config instead.
434
+ if ignored_endpoints.is_a?(String)
435
+ ignored_endpoints = ignored_endpoints.split(/\s*,\s*/)
436
+ end
437
+
423
438
  val = Array(get(:'ignored_endpoint'))
424
- val.concat(Array(get(:'ignored_endpoints')))
439
+ val.concat(Array(ignored_endpoints))
425
440
  val
426
441
  end
427
442
  end
@@ -470,6 +485,11 @@ authentication: #{self[:authentication]}
470
485
 
471
486
  private
472
487
 
488
+ def check_nfs(path)
489
+ # Should work on most *nix, though not on OS X
490
+ `stat -f -L -c %T #{path} 2>&1`.strip == 'nfs'
491
+ end
492
+
473
493
  def load_logger
474
494
  unless l = @logger
475
495
  out = get(:'log_file')
@@ -86,7 +86,7 @@ module Skylight
86
86
  end
87
87
 
88
88
  # Instrument
89
- def self.instrument(opts = DEFAULT_OPTIONS)
89
+ def self.instrument(opts = DEFAULT_OPTIONS, &block)
90
90
  unless inst = Instrumenter.instance
91
91
  return yield if block_given?
92
92
  return
@@ -105,11 +105,7 @@ module Skylight
105
105
  desc = nil
106
106
  end
107
107
 
108
- if block_given?
109
- inst.instrument(category, title, desc) { yield }
110
- else
111
- inst.instrument(category, title, desc)
112
- end
108
+ inst.instrument(category, title, desc, &block)
113
109
  end
114
110
 
115
111
  # Temporarily disable
@@ -32,6 +32,9 @@ module Skylight
32
32
  raise LoadError, "Cannot find native extensions in #{libskylight_path}"
33
33
  end
34
34
  end
35
+ rescue RuntimeError => e
36
+ # Old versions of OS X can have dlerrors, just treat it like a missing native
37
+ raise if skylight_required || e.message !~ /dlerror/
35
38
  rescue LoadError => e
36
39
  raise if skylight_required
37
40
  end
@@ -13,11 +13,11 @@ module Skylight
13
13
 
14
14
  def normalize(trace, name, payload)
15
15
  case payload[:name]
16
- when "SCHEMA", "CACHE"
16
+ when "SCHEMA".freeze, "CACHE".freeze
17
17
  return :skip
18
18
  else
19
19
  name = CAT
20
- title = payload[:name] || "SQL"
20
+ title = payload[:name] || "SQL".freeze
21
21
  end
22
22
 
23
23
  binds = payload[:binds]
@@ -41,9 +41,9 @@ module Skylight
41
41
 
42
42
  def extract_binds(payload, precalculated)
43
43
  case config[:sql_mode]
44
- when 'rust'
44
+ when 'rust'.freeze
45
45
  extract_rust(payload)
46
- when 'ruby'
46
+ when 'ruby'.freeze
47
47
  extract_ruby(payload, precalculated)
48
48
  else
49
49
  raise "Unrecognized sql_mode: #{config.sql_mode}"
@@ -15,11 +15,11 @@ module Skylight
15
15
  type = operation && operation.class.to_s =~ /^Moped::Protocol::(.+)$/ ? $1 : nil
16
16
 
17
17
  case type
18
- when "Query" then normalize_query(operation)
19
- when "GetMore" then normalize_get_more(operation)
20
- when "Insert" then normalize_insert(operation)
21
- when "Update" then normalize_update(operation)
22
- when "Delete" then normalize_delete(operation)
18
+ when "Query".freeze then normalize_query(operation)
19
+ when "GetMore".freeze then normalize_get_more(operation)
20
+ when "Insert".freeze then normalize_insert(operation)
21
+ when "Update".freeze then normalize_update(operation)
22
+ when "Delete".freeze then normalize_delete(operation)
23
23
  else :skip
24
24
  end
25
25
  end
@@ -27,7 +27,7 @@ module Skylight
27
27
  private
28
28
 
29
29
  def normalize_query(operation)
30
- title = normalize_title("QUERY", operation)
30
+ title = normalize_title("QUERY".freeze, operation)
31
31
 
32
32
  hash = extract_binds(operation.selector)
33
33
  description = hash.to_json
@@ -36,19 +36,19 @@ module Skylight
36
36
  end
37
37
 
38
38
  def normalize_get_more(operation)
39
- title = normalize_title("GET_MORE", operation)
39
+ title = normalize_title("GET_MORE".freeze, operation)
40
40
 
41
41
  [CAT, title, nil]
42
42
  end
43
43
 
44
44
  def normalize_insert(operation)
45
- title = normalize_title("INSERT", operation)
45
+ title = normalize_title("INSERT".freeze, operation)
46
46
 
47
47
  [CAT, title, nil]
48
48
  end
49
49
 
50
50
  def normalize_update(operation)
51
- title = normalize_title("UPDATE", operation)
51
+ title = normalize_title("UPDATE".freeze, operation)
52
52
 
53
53
  selector_hash = extract_binds(operation.selector)
54
54
  update_hash = extract_binds(operation.update)
@@ -59,7 +59,7 @@ module Skylight
59
59
  end
60
60
 
61
61
  def normalize_delete(operation)
62
- title = normalize_title("DELETE", operation)
62
+ title = normalize_title("DELETE".freeze, operation)
63
63
 
64
64
  hash = extract_binds(operation.selector)
65
65
  description = hash.to_json
@@ -78,17 +78,13 @@ module Skylight
78
78
  if v.is_a?(Hash)
79
79
  ret[k] = extract_binds(v)
80
80
  else
81
- ret[k] = '?'
81
+ ret[k] = '?'.freeze
82
82
  end
83
83
  end
84
84
 
85
85
  ret
86
86
  end
87
87
 
88
- def stringify(value)
89
- value.is_a?(Regexp) ? value.inspect : value.to_s
90
- end
91
-
92
88
  end
93
89
  end
94
90
  end
@@ -53,8 +53,9 @@ module Skylight
53
53
  end
54
54
 
55
55
  def end_instrumentation(datum)
56
- @requests[datum.object_id].done
57
- @requests.delete(datum)
56
+ if request = @requests.delete(datum.object_id)
57
+ Skylight.done(request)
58
+ end
58
59
  rescue Exception => e
59
60
  error "failed to end instrumentation for Excon; msg=%s", e.message
60
61
  end
@@ -0,0 +1,159 @@
1
+ module Skylight
2
+ module Probes
3
+ module Mongo
4
+ class Probe
5
+ def install
6
+ ::Mongo::Monitoring::Global.subscribe(::Mongo::Monitoring::COMMAND, Subscriber.new)
7
+ end
8
+ end
9
+
10
+ class Subscriber
11
+ include Skylight::Util::Logging
12
+
13
+ COMMANDS = [:insert, :find, :count, :distinct, :update, :findandmodify, :delete].freeze
14
+
15
+ COMMAND_NAMES = {
16
+ findandmodify: 'findAndModify'.freeze
17
+ }.freeze
18
+
19
+ def initialize
20
+ @events = {}
21
+ end
22
+
23
+ def started(event)
24
+ begin_instrumentation(event)
25
+ end
26
+
27
+ def succeeded(event)
28
+ end_instrumentation(event)
29
+ end
30
+
31
+ def failed(event)
32
+ end_instrumentation(event)
33
+ end
34
+
35
+ # For logging
36
+ def config
37
+ instrumenter = Skylight::Instrumenter.instance
38
+ instrumenter ? instrumenter.config : nil
39
+ end
40
+
41
+ private
42
+
43
+ def begin_instrumentation(event)
44
+ return unless COMMANDS.include?(event.command_name.to_sym)
45
+
46
+ command_name = COMMAND_NAMES[event.command_name] || event.command_name
47
+
48
+ title = "#{event.database_name}.#{command_name}"
49
+
50
+ command = event.command
51
+
52
+ # Not sure if this will always exist
53
+ # Delete so the description will be less redundant
54
+ if target = command[event.command_name]
55
+ title << " #{target}"
56
+ end
57
+
58
+ payload = {}
59
+
60
+ # Ruby Hashes are ordered based on insertion so do the most important ones first
61
+
62
+ add_value('key'.freeze, command, payload)
63
+ add_bound('query'.freeze, command, payload)
64
+ add_bound('filter'.freeze, command, payload)
65
+ add_value('sort'.freeze, command, payload)
66
+
67
+ if event.command_name == :findandmodify
68
+ add_bound('update'.freeze, command, payload)
69
+ end
70
+
71
+ add_value('remove'.freeze, command, payload)
72
+ add_value('new'.freeze, command, payload)
73
+
74
+ if updates = command['updates'.freeze]
75
+ # AFAICT the gem generally just sends one item in the updates array
76
+ update = updates[0]
77
+ update_payload = {}
78
+ add_bound('q'.freeze, update, update_payload)
79
+ add_bound('u'.freeze, update, update_payload)
80
+ add_value('multi'.freeze, update, update_payload)
81
+ add_value('upsert'.freeze, update, update_payload)
82
+
83
+ payload['updates'.freeze] = [update_payload]
84
+
85
+ if updates.length > 1
86
+ payload['updates'.freeze] << '...'
87
+ end
88
+ end
89
+
90
+ if deletes = command['deletes'.freeze]
91
+ # AFAICT the gem generally just sends one item in the updates array
92
+ delete = deletes[0]
93
+ delete_payload = {}
94
+ add_bound('q'.freeze, delete, delete_payload)
95
+ add_value('limit'.freeze, delete, delete_payload)
96
+
97
+ payload['deletes'.freeze] = [delete_payload]
98
+
99
+ if deletes.length > 1
100
+ payload['deletes'.freeze] << '...'
101
+ end
102
+ end
103
+
104
+
105
+ # We're ignoring documents from insert because they could have completely inconsistent
106
+ # format which would make it hard to merge.
107
+
108
+ opts = {
109
+ category: "db.mongo.command".freeze,
110
+ title: title,
111
+ description: payload.empty? ? nil : payload.to_json
112
+ }
113
+
114
+ @events[event.operation_id] = Skylight.instrument(opts)
115
+ rescue Exception => e
116
+ error "failed to begin instrumentation for Mongo; msg=%s", e.message
117
+ end
118
+
119
+ def end_instrumentation(event)
120
+ if original_event = @events.delete(event.operation_id)
121
+ Skylight.done(original_event)
122
+ end
123
+ rescue Exception => e
124
+ error "failed to end instrumentation for Mongo; msg=%s", e.message
125
+ end
126
+
127
+ def add_value(key, command, payload)
128
+ if command.has_key?(key)
129
+ value = command[key]
130
+ payload[key] = value
131
+ end
132
+ end
133
+
134
+ def add_bound(key, command, payload)
135
+ if value = command[key]
136
+ payload[key] = extract_binds(value)
137
+ end
138
+ end
139
+
140
+ def extract_binds(hash)
141
+ ret = {}
142
+
143
+ hash.each do |k,v|
144
+ if v.is_a?(Hash)
145
+ ret[k] = extract_binds(v)
146
+ else
147
+ ret[k] = '?'.freeze
148
+ end
149
+ end
150
+
151
+ ret
152
+ end
153
+
154
+ end
155
+ end
156
+
157
+ register("Mongo", "mongo", Mongo::Probe.new)
158
+ end
159
+ end
@@ -0,0 +1,21 @@
1
+ module Skylight
2
+ module Probes
3
+ module Mongoid
4
+ class Probe
5
+
6
+ def install
7
+ require 'mongoid/version'
8
+ version = Gem::Version.new(::Mongoid::VERSION)
9
+
10
+ if version < Gem::Version.new("5.0")
11
+ require 'skylight/probes/moped'
12
+ else
13
+ require 'skylight/probes/mongo'
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ register("Mongoid", "mongoid", Mongoid::Probe.new)
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ module Skylight
2
+ module Probes
3
+ module Moped
4
+ class Probe
5
+
6
+ def install
7
+ ::Moped::Instrumentable.module_eval do
8
+ alias instrument_without_sk instrument
9
+
10
+ def instrument(*args, &block)
11
+ # Mongoid sets the instrumenter to AS::N
12
+ if instrumenter == ActiveSupport::Notifications
13
+ asn_block = block
14
+ else
15
+ # If the instrumenter hasn't been changed to AS::N use both
16
+ asn_block = Proc.new do
17
+ ActiveSupport::Notifications.instrument(*args, &block)
18
+ end
19
+ end
20
+
21
+ instrument_without_sk(*args, &asn_block)
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ register("Moped", "moped", Moped::Probe.new)
30
+ end
31
+ end
@@ -66,12 +66,12 @@ module Skylight
66
66
  end
67
67
 
68
68
  config = Config.load(file: path, environment: Rails.env.to_s)
69
- config['root'] = Rails.root
69
+ config[:root] = Rails.root
70
70
 
71
71
  configure_logging(config, app)
72
72
 
73
- config['daemon.sockdir_path'] ||= tmp
74
- config['normalizers.render.view_paths'] = existent_paths(app.config.paths["app/views"]) + [Rails.root.to_s]
73
+ config[:'daemon.sockdir_path'] ||= tmp
74
+ config[:'normalizers.render.view_paths'] = existent_paths(app.config.paths["app/views"]) + [Rails.root.to_s]
75
75
  config.validate!
76
76
  config
77
77
 
@@ -30,9 +30,7 @@ module Skylight
30
30
  @notifications = []
31
31
 
32
32
  # create the root node
33
- @root = native_start_span(native_get_started_at, cat)
34
- native_span_set_title(@root, title) if title
35
- native_span_set_description(@root, desc) if desc
33
+ @root = start(native_get_started_at, cat, title, desc, normalize: false)
36
34
 
37
35
  @gc = config.gc.track unless ENV.key?("SKYLIGHT_DISABLE_GC_TRACKING")
38
36
  end
@@ -140,15 +138,18 @@ module Skylight
140
138
 
141
139
  private
142
140
 
143
- def start(time, cat, title, desc)
144
- sp = native_start_span(self.class.normalize_time(time), cat.to_s)
141
+ def start(time, cat, title, desc, opts={})
142
+ time = self.class.normalize_time(time) unless opts[:normalize] == false
143
+
144
+ sp = native_start_span(time, cat.to_s)
145
145
  native_span_set_title(sp, title.to_s) if title
146
146
  native_span_set_description(sp, desc.to_s) if desc
147
147
  sp
148
148
  end
149
149
 
150
150
  def stop(span, time)
151
- native_stop_span(span, self.class.normalize_time(time))
151
+ time = self.class.normalize_time(time)
152
+ native_stop_span(span, time)
152
153
  nil
153
154
  end
154
155
 
@@ -1,4 +1,4 @@
1
1
  module Skylight
2
- VERSION = '0.9.3'
2
+ VERSION = '0.9.4'
3
3
  end
4
4
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skylight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tilde, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-17 00:00:00.000000000 Z
11
+ date: 2015-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -86,6 +86,9 @@ files:
86
86
  - lib/skylight/probes/excon.rb
87
87
  - lib/skylight/probes/excon/middleware.rb
88
88
  - lib/skylight/probes/grape.rb
89
+ - lib/skylight/probes/mongo.rb
90
+ - lib/skylight/probes/mongoid.rb
91
+ - lib/skylight/probes/moped.rb
89
92
  - lib/skylight/probes/net_http.rb
90
93
  - lib/skylight/probes/redis.rb
91
94
  - lib/skylight/probes/sequel.rb