rookout 0.1.0

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +12 -0
  3. data/bin/rookout +30 -0
  4. data/lib/rookout.rb +18 -0
  5. data/lib/rookout/augs/actions/action.rb +11 -0
  6. data/lib/rookout/augs/actions/action_run_processor.rb +29 -0
  7. data/lib/rookout/augs/aug.rb +121 -0
  8. data/lib/rookout/augs/aug_factory.rb +69 -0
  9. data/lib/rookout/augs/aug_rate_limiter.rb +96 -0
  10. data/lib/rookout/augs/augs_manager.rb +77 -0
  11. data/lib/rookout/augs/conditions/condition.rb +15 -0
  12. data/lib/rookout/augs/locations/location.rb +11 -0
  13. data/lib/rookout/augs/locations/location_file_line.rb +26 -0
  14. data/lib/rookout/com_ws/agent_com_ws.rb +221 -0
  15. data/lib/rookout/com_ws/backoff.rb +35 -0
  16. data/lib/rookout/com_ws/command_handler.rb +22 -0
  17. data/lib/rookout/com_ws/git.rb +53 -0
  18. data/lib/rookout/com_ws/information.rb +85 -0
  19. data/lib/rookout/com_ws/output.rb +135 -0
  20. data/lib/rookout/com_ws/token_bucket.rb +36 -0
  21. data/lib/rookout/config.rb +55 -0
  22. data/lib/rookout/exceptions.rb +140 -0
  23. data/lib/rookout/interface.rb +140 -0
  24. data/lib/rookout/logger.rb +158 -0
  25. data/lib/rookout/processor/namespace_serializer.rb +26 -0
  26. data/lib/rookout/processor/namespaces/container_namespace.rb +45 -0
  27. data/lib/rookout/processor/namespaces/frame_namespace.rb +65 -0
  28. data/lib/rookout/processor/namespaces/namespace.rb +28 -0
  29. data/lib/rookout/processor/namespaces/noop_namespace.rb +32 -0
  30. data/lib/rookout/processor/namespaces/ruby_object_namespace.rb +97 -0
  31. data/lib/rookout/processor/namespaces/ruby_object_serializer.rb +208 -0
  32. data/lib/rookout/processor/namespaces/ruby_utils_namespace.rb +65 -0
  33. data/lib/rookout/processor/namespaces/stack_namespace.rb +30 -0
  34. data/lib/rookout/processor/namespaces/traceback_namespace.rb +40 -0
  35. data/lib/rookout/processor/operations/operation.rb +11 -0
  36. data/lib/rookout/processor/operations/set_operation.rb +48 -0
  37. data/lib/rookout/processor/paths/arithmetic_path.rb +84 -0
  38. data/lib/rookout/processor/paths/canopy/actions.rb +184 -0
  39. data/lib/rookout/processor/paths/canopy/consts.rb +82 -0
  40. data/lib/rookout/processor/paths/canopy/maps.rb +2837 -0
  41. data/lib/rookout/processor/paths/canopy/markers.rb +186 -0
  42. data/lib/rookout/processor/paths/path.rb +15 -0
  43. data/lib/rookout/processor/processor.rb +34 -0
  44. data/lib/rookout/processor/processor_factory.rb +23 -0
  45. data/lib/rookout/processor/rook_error.rb +45 -0
  46. data/lib/rookout/protobuf/.gitignore +0 -0
  47. data/lib/rookout/protobuf/agent_info_pb.rb +68 -0
  48. data/lib/rookout/protobuf/controller_info_pb.rb +29 -0
  49. data/lib/rookout/protobuf/envelope_pb.rb +21 -0
  50. data/lib/rookout/protobuf/messages_pb.rb +189 -0
  51. data/lib/rookout/protobuf/variant_pb.rb +139 -0
  52. data/lib/rookout/rookout_singleton.rb +73 -0
  53. data/lib/rookout/services/position.rb +163 -0
  54. data/lib/rookout/services/tracer.rb +83 -0
  55. data/lib/rookout/trigger_services.rb +34 -0
  56. data/lib/rookout/user_warnings.rb +25 -0
  57. data/lib/rookout/version.rb +4 -0
  58. metadata +269 -0
@@ -0,0 +1,135 @@
1
+ module Rookout
2
+ module ComWs
3
+ require_relative "../logger"
4
+ require_relative "../config"
5
+
6
+ require_relative "../protobuf/messages_pb"
7
+
8
+ require_relative "../processor/namespaces/ruby_object_namespace"
9
+ require_relative "../processor/namespace_serializer"
10
+
11
+ require_relative "token_bucket"
12
+
13
+ class Output
14
+ def initialize
15
+ @agent_id = nil
16
+ @agent_com = nil
17
+
18
+ @rule_status_update_bucket = TokenBucket.new Config.output_max_status_updates,
19
+ Config.output_bucket_refresh_rate do
20
+ Logger.instance.error "Limit reached, dropping status updates"
21
+ end
22
+
23
+ @user_message_bucket = TokenBucket.new Config.output_max_aug_messages,
24
+ Config.output_bucket_refresh_rate do
25
+ Logger.instance.error "Limit reached, dropping aug report messages"
26
+ end
27
+
28
+ @log_message_bucket = TokenBucket.new Config.output_max_log_items,
29
+ Config.output_bucket_refresh_rate do
30
+ internal_send_log_message 3,
31
+ Time.new,
32
+ __FILE__,
33
+ 0,
34
+ text,
35
+ "Limit reached, dropping log messages",
36
+ "Limit reached, dropping log messages"
37
+ end
38
+
39
+ Logger.instance.register_output self
40
+
41
+ @closing = false
42
+ end
43
+
44
+ attr_accessor :agent_id, :agent_com
45
+
46
+ def close
47
+ @closing = true
48
+
49
+ Logger.instance.remove_output self
50
+ end
51
+
52
+ def send_warning rule_id, error
53
+ send_rule_status rule_id, :Warning, error
54
+ end
55
+
56
+ def send_rule_status rule_id, active, error
57
+ return if @closing || !@agent_com
58
+
59
+ @rule_status_update_bucket.if_available do
60
+ protobuf_error = nil
61
+ if error
62
+ protobuf_error = Com::Rookout::Error.new message: error.messsage,
63
+ type: error.type
64
+ protobuf_error.parameters.copy_from error.parameters
65
+ protobuf_error.exc.copy_from error.exc
66
+ protobuf_error.traceback.copy_from error.traceback
67
+ end
68
+
69
+ status = Com::Rookout::RuleStatusMessage.new agent_id: @agent_id,
70
+ rule_id: rule_id,
71
+ active: active,
72
+ error: protobuf_error
73
+ @agent_com.add status
74
+ end
75
+ end
76
+
77
+ def send_user_message aug_id, report_id, arguments
78
+ return if @closing || !@agent_com
79
+
80
+ @user_message_bucket.if_available do
81
+ if arguments.nil? || arguments.call_method("size", "") == 0
82
+ protobuf_arguments = nil
83
+ else
84
+ protobuf_arguments = Processor::NamespaceSerializer.dump arguments
85
+ end
86
+
87
+ msg = Com::Rookout::AugReportMessage.new agent_id: @agent_id,
88
+ aug_id: aug_id,
89
+ report_id: report_id,
90
+ arguments: protobuf_arguments
91
+ @agent_com.add msg
92
+ end
93
+ end
94
+
95
+ def send_log_message level, time, filename, lineno, text, formatted_message, arguments
96
+ return if @closing || !@agent_com
97
+ @log_message_bucket.if_available do
98
+ internal_send_log_message level, time, filename, lineno, text, formatted_message, arguments
99
+ end
100
+ end
101
+
102
+ def flush_messages
103
+ return unless @agent_com
104
+ @agent_com.flush
105
+ end
106
+
107
+ private
108
+
109
+ def internal_send_log_message level, time, filename, lineno, text, formatted_message, arguments
110
+ return if @closing || !@agent_com
111
+
112
+ timestamp = Google::Protobuf::Timestamp.new
113
+ timestamp.from_time time
114
+
115
+ if arguments.nil? || arguments.empty?
116
+ protobuf_arguments = nil
117
+ else
118
+ protobuf_arguments = Processor::NamespaceSerializer.dump(
119
+ Processor::Namespaces::RubyObjectNamespace.new(arguments)
120
+ )
121
+ end
122
+
123
+ msg = Com::Rookout::LogMessage.new timestamp: timestamp,
124
+ agent_id: @agent_id,
125
+ level: level,
126
+ filename: filename,
127
+ line: lineno,
128
+ text: text,
129
+ formatted_message: formatted_message,
130
+ legacy_arguments: protobuf_arguments
131
+ @agent_com.add msg
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,36 @@
1
+ module Rookout
2
+ module ComWs
3
+ class TokenBucket
4
+ def initialize limit, interval_seconds, &do_once_when_exhausted
5
+ @initial_limit = limit
6
+ @remaining = limit
7
+ @last_reset = Time.new
8
+ @interval = interval_seconds
9
+ @do_once_when_exhausted = do_once_when_exhausted
10
+ @do_once_when_exhausted_performed = false
11
+ end
12
+
13
+ def exhausted?
14
+ if Time.new - @last_reset > @interval
15
+ @last_reset = Time.new
16
+ @remaining = @initial_limit
17
+ @do_once_when_exhausted_performed = false
18
+ end
19
+
20
+ @remaining < 0
21
+ end
22
+
23
+ def if_available
24
+ @remaining -= 1
25
+ if exhausted?
26
+ return if @do_once_when_exhausted_performed || @do_once_when_exhausted.nil?
27
+
28
+ @do_once_when_exhausted.call
29
+ @do_once_when_exhausted_performed = true
30
+ else
31
+ yield
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ module Rookout
2
+ require_relative "version"
3
+
4
+ module Config
5
+ # Magic to allow for module variables to be easily accessible
6
+ class << self
7
+ attr_accessor :debug
8
+ Rookout::Config.debug = false
9
+
10
+ attr_accessor :logger_filename, :logger_log_to_stderr, :logger_log_level
11
+ Rookout::Config.logger_filename = "rookout/ruby-rook.log".freeze
12
+ Rookout::Config.logger_log_to_stderr = false
13
+ Rookout::Config.logger_log_level = :info
14
+
15
+ attr_accessor :agent_com_configuration_command_thread_name,
16
+ :agent_com_max_message_limit,
17
+ :agent_com_timeout,
18
+ :agent_com_ping_interval,
19
+ :agent_com_ping_timeout,
20
+ :agent_com_flush_timeout,
21
+ :agent_com_max_queued_messages
22
+ Rookout::Config.agent_com_max_message_limit = 1024 * 1024
23
+ Rookout::Config.agent_com_timeout = 3
24
+ Rookout::Config.agent_com_ping_interval = 10
25
+ Rookout::Config.agent_com_ping_timeout = 30
26
+ Rookout::Config.agent_com_flush_timeout = 3
27
+ Rookout::Config.agent_com_max_queued_messages = 500
28
+
29
+ attr_accessor :backoff_factor, :backoff_reset_time, :backoff_max_time
30
+ Rookout::Config.backoff_factor = 0.2
31
+ Rookout::Config.backoff_max_time = 60
32
+ Rookout::Config.backoff_reset_time = 3 * 60.0
33
+
34
+ attr_accessor :output_max_status_updates,
35
+ :output_max_aug_messages,
36
+ :output_max_log_items,
37
+ :output_bucket_refresh_rate
38
+ Rookout::Config.output_max_status_updates = 200
39
+ Rookout::Config.output_max_aug_messages = 100
40
+ Rookout::Config.output_max_log_items = 200
41
+ Rookout::Config.output_bucket_refresh_rate = 10
42
+
43
+ attr_accessor :instrumentation_max_aug_time
44
+ Rookout::Config.instrumentation_max_aug_time = 400
45
+
46
+ attr_accessor :user_git_commit, :user_git_origin
47
+ Rookout::Config.user_git_commit = nil
48
+ Rookout::Config.user_git_origin = nil
49
+
50
+ attr_accessor :rookout_version, :rookout_commit
51
+ Rookout::Config.rookout_version = Rookout::VERSION
52
+ Rookout::Config.rookout_commit = Rookout::COMMIT
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,140 @@
1
+ module Rookout
2
+ module Exceptions
3
+ class ToolException < RuntimeError
4
+ def initialize msg, parameters = {}, cause = nil
5
+ super msg
6
+ @parameters = parameters
7
+ @cause = cause
8
+ end
9
+
10
+ attr_reader :parameters
11
+ end
12
+
13
+ class RookInterfaceException < ToolException
14
+ def initialize message
15
+ super message
16
+ end
17
+ end
18
+
19
+ class RookVersionNotSupported < ToolException
20
+ def initialize parameter, value
21
+ super "Rook is not supported in this #{parameter}: #{value}"
22
+ end
23
+ end
24
+
25
+ class RookAugInvalidKey < ToolException
26
+ def initialize key, configuration
27
+ super "Failed to get key #{key} from configuration #{configuration}",
28
+ { key: key, configuration: configuration }
29
+ end
30
+ end
31
+
32
+ class RookAttributeNotFound < ToolException
33
+ def initialize attribute
34
+ super "Failed to get attribute #{attribute}", { attribute: attribute }
35
+ end
36
+ end
37
+
38
+ class RookKeyNotFound < ToolException
39
+ def initialize key
40
+ super "Failed to get key #{key}", { key: key }
41
+ end
42
+ end
43
+
44
+ class RookWriteAttributeNotSupported < ToolException
45
+ def initialize namespace_type, attribute
46
+ super "Namespace #{namespace_type} does not support write",
47
+ { namespace: namespace_type, attribute: attribute }
48
+ end
49
+ end
50
+
51
+ class RookMethodNotFound < ToolException
52
+ def initialize namespace_type, method
53
+ super "Namespace method not found #{method}",
54
+ { namespace: namespace_type, method: method }
55
+ end
56
+ end
57
+
58
+ class RookInvalidObjectForAccess < ToolException
59
+ def initialize object_type, access
60
+ super "Object #{object_type} does not support access #{access}",
61
+ { object: object_type, access: access }
62
+ end
63
+ end
64
+
65
+ class RookInvalidArithmeticPath < ToolException
66
+ def initialize path, cause = nil
67
+ super "Invalid arithmetic path configuration. configuration: #{path}, innerException: #{cause}",
68
+ { configuration: path },
69
+ cause
70
+ end
71
+ end
72
+
73
+ class RookOperationReadOnly < ToolException
74
+ def initialize type
75
+ super "Operation does not support write: #{type}", { operation: type }
76
+ end
77
+ end
78
+
79
+ class RookExceptionEvaluationFailed < ToolException
80
+ def initialize expression, cause
81
+ super "Failed to evaluate expression %s: #{expression}", { expression: expression }, cause
82
+ end
83
+ end
84
+
85
+ class RookNonPrimitiveObjectType < ToolException
86
+ def initialize path
87
+ super "Object %s must be of primitive type, such as: string, int, long etc: #{path}", { path: path }
88
+ end
89
+ end
90
+
91
+ class RookMessageSizeExceeded < ToolException
92
+ def initialize message_size, max_message_size
93
+ super "Message size of #{message_size} exceeds max size limit of #{max_message_size}. " \
94
+ "Change the depth of collection or change the default by setting ROOKOUT_MAX_MESSAGE_SIZE " \
95
+ "as environment variable or system property",
96
+ { message_size: message_size, max_message_size: max_message_size }
97
+ end
98
+ end
99
+
100
+ class RookRuleRateLimited < ToolException
101
+ def initialize
102
+ super "Breakpoint was disabled due to rate-limiting. " \
103
+ "For more information: https://docs.rookout.com/docs/breakpoints-tasks.html#rate-limiting"
104
+ end
105
+ end
106
+
107
+ class RookInvalidToken < ToolException
108
+ def initialize token
109
+ super "The Rookout token supplied #{token[0..6]} is not valid; please check the token and try again",
110
+ { token: token[0...6] }
111
+ end
112
+ end
113
+
114
+ class RookSourceFilePathSuggestion < ToolException
115
+ def initialize wanted_path, matching_path
116
+ super "Rookout found alternative file path: #{matching_path}",
117
+ { wanted_path: wanted_path, matching_path: matching_path }
118
+ end
119
+ end
120
+
121
+ class RookSetTracepointFailed < ToolException
122
+ def initialize target_line, error
123
+ super "Failed to set TracePoint on line #{target_line} due to #{error.message}",
124
+ { target_line: target_line, error: error }
125
+ end
126
+ end
127
+
128
+ class RookObjectCannotBeSerialized < ToolException
129
+ def initialize object, message
130
+ super message, { "class" => object.class.to_s }
131
+ end
132
+ end
133
+
134
+ class RookMissingToken < ToolException
135
+ def initialize
136
+ super "No Rookout token was supplied. Make sure to pass the Rookout Token when starting the rook"
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,140 @@
1
+ module Rookout
2
+ class Interface
3
+ require "singleton"
4
+ include Singleton
5
+
6
+ require_relative "config"
7
+ require_relative "exceptions"
8
+
9
+ def initialize
10
+ @rook = nil
11
+ end
12
+
13
+ def start options = {}
14
+ return unless @rook.nil?
15
+ throw_errors = options[:throw_errors] == true
16
+ Config.debug = evaluate_flag options[:debug], "ROOKOUT_DEBUG"
17
+
18
+ begin
19
+ require_relative "rookout_singleton"
20
+
21
+ configure_logging options
22
+ labels = options[:labels] || parse_labels(ENV["ROOKOUT_LABELS"])
23
+ validate_labels labels
24
+
25
+ configure_git options
26
+
27
+ com = configure_com options
28
+
29
+ async_start = true? ENV["ROOKOUT_ASYNC_START"]
30
+ fork = evaluate_flag options[:fork], "ROOKOUT_ENABLE_FORK"
31
+
32
+ print_config com, labels: labels, async_start: async_start, fork: fork if Config.debug
33
+
34
+ rook = RookoutSingleton.instance
35
+ rook.connect com[:token], com[:host], com[:port], com[:proxy], labels, async_start, fork
36
+
37
+ # TODO: ADD DETAILED PROCESSING FOR SPECIFIC ERRORS
38
+ rescue Exception => e
39
+ puts e.full_message if Config.debug
40
+ raise if throw_errors
41
+ end
42
+ end
43
+
44
+ def flush
45
+ @rook.flush unless @rook.nil?
46
+ end
47
+
48
+ def stop
49
+ return if @rook.nil?
50
+
51
+ @rook.stop
52
+ @rook = nil
53
+ end
54
+
55
+ private
56
+
57
+ TRUE_VALUE = [true, "y", "Y", "yes", "Yes", "YES", "true", "True", "TRUE", "1"].freeze
58
+
59
+ def configure_logging options
60
+ if Config.debug
61
+ log_to_stderr = true
62
+ log_level = "DEBUG"
63
+ else
64
+ log_to_stderr = evaluate_flag options[:log_to_stderr], "ROOKOUT_LOG_TO_STDERR"
65
+ log_level = options[:log_level] || ENV["ROOKOUT_LOG_FILE"]
66
+ end
67
+
68
+ log_file = options[:log_file] || ENV["ROOKOUT_LOG_FILE"]
69
+
70
+ Config.logger_log_to_stderr = log_to_stderr unless log_to_stderr.nil?
71
+ Config.logger_filename = log_file unless log_file.nil?
72
+ Config.logger_log_level = log_level unless log_level.nil?
73
+ end
74
+
75
+ def configure_git options
76
+ Config.user_git_origin = options[:git_origin] if options[:git_origin]
77
+ Config.user_git_commit = options[:git_commit] if options[:git_commit]
78
+ end
79
+
80
+ def configure_com options
81
+ host = evaluate_config options[:host], "ROOKOUT_CONTROLLER_HOST", "wss://control.rookout.com"
82
+ port = evaluate_config options[:port], "ROOKOUT_CONTROLLER_PORT", 443
83
+ proxy = evaluate_config options[:proxy], "ROOKOUT_PROXY"
84
+ token = evaluate_config options[:token], "ROOKOUT_TOKEN"
85
+
86
+ raise Exceptions::RookMissingToken if token.nil? && host == "wss://control.rookout.com"
87
+ verify_token token if token
88
+
89
+ { host: host, port: port, proxy: proxy, token: token }
90
+ end
91
+
92
+ def evaluate_flag argument, env_var_name
93
+ return true? argument unless argument.nil?
94
+ true? ENV[env_var_name]
95
+ end
96
+
97
+ def true? value
98
+ TRUE_VALUE.include? value
99
+ end
100
+
101
+ def evaluate_config argument, env_var_name, default = nil
102
+ return argument unless argument.nil?
103
+ return ENV[env_var_name] unless ENV[env_var_name].nil?
104
+ default
105
+ end
106
+
107
+ def parse_labels raw_labels
108
+ labels = {}
109
+ return labels if raw_labels.nil?
110
+
111
+ raw_labels.split(",").each do |raw_label|
112
+ keyvalue = raw_label.gsub(/^[( "')*]/, "").gsub(/[( "')*]$/, "").split(":")
113
+ if keyvalue.length == 2
114
+ labels[keyvalue[0]] = keyvalue[1]
115
+ end
116
+ end
117
+ labels
118
+ end
119
+
120
+ def validate_labels labels
121
+ labels.each do |label_name, _|
122
+ if label_name.start_with? "$"
123
+ raise Exceptions::RookInvalidLabel, label_name
124
+ end
125
+ end
126
+ end
127
+
128
+ def verify_token token
129
+ raise Exceptions::RookInvalidOptions, "Rookout token should be a String" unless token.is_a? String
130
+ raise Exceptions::RookInvalidOptions, "Rookout token should be 64 characters" unless token.length == 64
131
+ raise Exceptions::RookInvalidOptions, "Rookout token must consist of only hexadecimal characters" unless
132
+ token.match(/^[0-9a-zA-Z]{0,64}$/)
133
+ end
134
+
135
+ def print_config com, options
136
+ puts "[Rookout] Communication Options: #{com}"
137
+ puts "[Rookout] Other Options #{options}"
138
+ end
139
+ end
140
+ end