right_agent 0.5.1

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 (147) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +78 -0
  3. data/Rakefile +86 -0
  4. data/lib/right_agent.rb +66 -0
  5. data/lib/right_agent/actor.rb +163 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +189 -0
  8. data/lib/right_agent/agent.rb +735 -0
  9. data/lib/right_agent/agent_config.rb +403 -0
  10. data/lib/right_agent/agent_identity.rb +209 -0
  11. data/lib/right_agent/agent_tags_manager.rb +213 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/broker_client.rb +683 -0
  14. data/lib/right_agent/command.rb +30 -0
  15. data/lib/right_agent/command/agent_manager_commands.rb +134 -0
  16. data/lib/right_agent/command/command_client.rb +136 -0
  17. data/lib/right_agent/command/command_constants.rb +42 -0
  18. data/lib/right_agent/command/command_io.rb +128 -0
  19. data/lib/right_agent/command/command_parser.rb +87 -0
  20. data/lib/right_agent/command/command_runner.rb +105 -0
  21. data/lib/right_agent/command/command_serializer.rb +63 -0
  22. data/lib/right_agent/console.rb +65 -0
  23. data/lib/right_agent/core_payload_types.rb +42 -0
  24. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  25. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  26. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  27. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  28. data/lib/right_agent/core_payload_types/dev_repositories.rb +90 -0
  29. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  30. data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
  31. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  32. data/lib/right_agent/core_payload_types/login_user.rb +62 -0
  33. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  34. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -0
  35. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  36. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  37. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +73 -0
  38. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  39. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  40. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  41. data/lib/right_agent/daemonize.rb +35 -0
  42. data/lib/right_agent/dispatcher.rb +348 -0
  43. data/lib/right_agent/enrollment_result.rb +217 -0
  44. data/lib/right_agent/exceptions.rb +30 -0
  45. data/lib/right_agent/ha_broker_client.rb +1278 -0
  46. data/lib/right_agent/idempotent_request.rb +140 -0
  47. data/lib/right_agent/log.rb +418 -0
  48. data/lib/right_agent/monkey_patches.rb +29 -0
  49. data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
  50. data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -0
  51. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  52. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  53. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  54. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  55. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  56. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +46 -0
  57. data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -0
  58. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +90 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  64. data/lib/right_agent/multiplexer.rb +91 -0
  65. data/lib/right_agent/operation_result.rb +270 -0
  66. data/lib/right_agent/packets.rb +637 -0
  67. data/lib/right_agent/payload_formatter.rb +104 -0
  68. data/lib/right_agent/pid_file.rb +159 -0
  69. data/lib/right_agent/platform.rb +319 -0
  70. data/lib/right_agent/platform/darwin.rb +227 -0
  71. data/lib/right_agent/platform/linux.rb +268 -0
  72. data/lib/right_agent/platform/windows.rb +1204 -0
  73. data/lib/right_agent/scripts/agent_controller.rb +522 -0
  74. data/lib/right_agent/scripts/agent_deployer.rb +379 -0
  75. data/lib/right_agent/scripts/common_parser.rb +153 -0
  76. data/lib/right_agent/scripts/log_level_manager.rb +193 -0
  77. data/lib/right_agent/scripts/stats_manager.rb +256 -0
  78. data/lib/right_agent/scripts/usage.rb +58 -0
  79. data/lib/right_agent/secure_identity.rb +92 -0
  80. data/lib/right_agent/security.rb +32 -0
  81. data/lib/right_agent/security/cached_certificate_store_proxy.rb +63 -0
  82. data/lib/right_agent/security/certificate.rb +102 -0
  83. data/lib/right_agent/security/certificate_cache.rb +89 -0
  84. data/lib/right_agent/security/distinguished_name.rb +56 -0
  85. data/lib/right_agent/security/encrypted_document.rb +84 -0
  86. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  87. data/lib/right_agent/security/signature.rb +86 -0
  88. data/lib/right_agent/security/static_certificate_store.rb +69 -0
  89. data/lib/right_agent/sender.rb +937 -0
  90. data/lib/right_agent/serialize.rb +29 -0
  91. data/lib/right_agent/serialize/message_pack.rb +102 -0
  92. data/lib/right_agent/serialize/secure_serializer.rb +131 -0
  93. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  94. data/lib/right_agent/serialize/serializable.rb +135 -0
  95. data/lib/right_agent/serialize/serializer.rb +149 -0
  96. data/lib/right_agent/stats_helper.rb +731 -0
  97. data/lib/right_agent/subprocess.rb +38 -0
  98. data/lib/right_agent/tracer.rb +124 -0
  99. data/right_agent.gemspec +60 -0
  100. data/spec/actor_registry_spec.rb +81 -0
  101. data/spec/actor_spec.rb +99 -0
  102. data/spec/agent_config_spec.rb +226 -0
  103. data/spec/agent_identity_spec.rb +75 -0
  104. data/spec/agent_spec.rb +571 -0
  105. data/spec/broker_client_spec.rb +961 -0
  106. data/spec/command/agent_manager_commands_spec.rb +51 -0
  107. data/spec/command/command_io_spec.rb +93 -0
  108. data/spec/command/command_parser_spec.rb +79 -0
  109. data/spec/command/command_runner_spec.rb +72 -0
  110. data/spec/command/command_serializer_spec.rb +51 -0
  111. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  112. data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
  113. data/spec/core_payload_types/login_user_spec.rb +98 -0
  114. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  115. data/spec/core_payload_types/spec_helper.rb +23 -0
  116. data/spec/dispatcher_spec.rb +372 -0
  117. data/spec/enrollment_result_spec.rb +53 -0
  118. data/spec/ha_broker_client_spec.rb +1673 -0
  119. data/spec/idempotent_request_spec.rb +136 -0
  120. data/spec/log_spec.rb +177 -0
  121. data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
  122. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  123. data/spec/monkey_patches/string_patch_spec.rb +99 -0
  124. data/spec/multiplexer_spec.rb +48 -0
  125. data/spec/operation_result_spec.rb +171 -0
  126. data/spec/packets_spec.rb +418 -0
  127. data/spec/platform/platform_spec.rb +60 -0
  128. data/spec/results_mock.rb +45 -0
  129. data/spec/secure_identity_spec.rb +50 -0
  130. data/spec/security/cached_certificate_store_proxy_spec.rb +56 -0
  131. data/spec/security/certificate_cache_spec.rb +71 -0
  132. data/spec/security/certificate_spec.rb +49 -0
  133. data/spec/security/distinguished_name_spec.rb +46 -0
  134. data/spec/security/encrypted_document_spec.rb +55 -0
  135. data/spec/security/rsa_key_pair_spec.rb +55 -0
  136. data/spec/security/signature_spec.rb +66 -0
  137. data/spec/security/static_certificate_store_spec.rb +52 -0
  138. data/spec/sender_spec.rb +887 -0
  139. data/spec/serialize/message_pack_spec.rb +131 -0
  140. data/spec/serialize/secure_serializer_spec.rb +102 -0
  141. data/spec/serialize/serializable_spec.rb +90 -0
  142. data/spec/serialize/serializer_spec.rb +174 -0
  143. data/spec/spec.opts +2 -0
  144. data/spec/spec_helper.rb +77 -0
  145. data/spec/stats_helper_spec.rb +681 -0
  146. data/spec/tracer_spec.rb +114 -0
  147. metadata +320 -0
@@ -0,0 +1,140 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module RightScale
24
+
25
+ class IdempotentRequest
26
+
27
+ include OperationResultHelper
28
+ include EM::Deferrable
29
+
30
+ # Wait 5 seconds before retrying in case of failure
31
+ DEFAULT_RETRY_DELAY = 5
32
+
33
+ attr_reader :raw_response
34
+
35
+ # Send idempotent request
36
+ # Retry until timeout is reached (indefinitely if timeout <= 0)
37
+ # Calls deferrable callback on completion, error callback on timeout
38
+ #
39
+ # === Parameters
40
+ # operation(String):: Request operation (i.e. '/booter/get_boot_bundle')
41
+ # payload(Hash):: Request payload
42
+ # options[:retry_on_error](FalseClass|TrueClass):: Whether request should be retried
43
+ # if recipient returned an error
44
+ # options[:timeout](Fixnum):: Number of seconds before error callback gets called
45
+ # options[:retry_delay](Fixnum):: Number of seconds before retry, defaults to 5
46
+ def initialize(operation, payload, options={})
47
+ raise ArgumentError.new("options[:operation] is required") unless @operation = operation
48
+ raise ArgumentError.new("options[:payload] is required") unless @payload = payload
49
+ @retry_on_error = options[:retry_on_error] || false
50
+ @timeout = options[:timeout] || -1
51
+ @retry_delay = options[:retry_delay] || DEFAULT_RETRY_DELAY
52
+ @targets = options[:targets]
53
+ @raw_response = nil
54
+ @done = false
55
+ end
56
+
57
+ # Send request and retry until timeout is reached or response is received
58
+ # Ignore duplicate responses
59
+ #
60
+ # === Return
61
+ # true:: Always return true
62
+ def run
63
+ Sender.instance.send_retryable_request(@operation, @payload, retrieve_target(@targets)) { |r| handle_response(r) }
64
+ if @cancel_timer.nil? && @timeout > 0
65
+ @cancel_timer = EM::Timer.new(@timeout) do
66
+ msg = "Request #{@operation} timed out after #{@timeout} seconds"
67
+ log_info(msg)
68
+ cancel(msg)
69
+ end
70
+ end
71
+ true
72
+ end
73
+
74
+ # Cancel request and call error callback
75
+ #
76
+ # === Parameters
77
+ # msg(String):: Reason why request is cancelled, given to error callback
78
+ #
79
+ # === Return
80
+ # true:: Always return true
81
+ def cancel(msg)
82
+ if @cancel_timer
83
+ @cancel_timer.cancel
84
+ @cancel_timer = nil
85
+ end
86
+ @done = true
87
+ fail(msg)
88
+ true
89
+ end
90
+
91
+ protected
92
+
93
+ # Process request response and retry if needed
94
+ #
95
+ # === Parameters
96
+ # r(Result):: Request result
97
+ #
98
+ # === Return
99
+ # true:: Always return true
100
+ def handle_response(r)
101
+ return true if @done
102
+ @raw_response = r
103
+ res = result_from(r)
104
+ res = OperationResult.non_delivery unless res
105
+ if res.success?
106
+ if @cancel_timer
107
+ @cancel_timer.cancel
108
+ @cancel_timer = nil
109
+ end
110
+ @done = true
111
+ succeed(res.content)
112
+ else
113
+ if res.non_delivery?
114
+ Log.info("Request non-delivery (#{res.content}) for #{@operation}")
115
+ elsif res.retry?
116
+ Log.info("RightScale not ready when trying to request #{@operation}")
117
+ else
118
+ Log.info("Request #{@operation} failed (#{res.content})")
119
+ end
120
+ if res.non_delivery? || res.retry? || @retry_on_error
121
+ Log.info("Retrying in #{@retry_delay} seconds...")
122
+ if @retry_delay > 0
123
+ EM.add_timer(@retry_delay) { run }
124
+ else
125
+ EM.next_tick { run }
126
+ end
127
+ else
128
+ cancel(res.content)
129
+ end
130
+ end
131
+ true
132
+ end
133
+
134
+ def retrieve_target(targets)
135
+ targets[rand(0xffff) % targets.size] if targets
136
+ end
137
+
138
+ end # IdempotentRequest
139
+
140
+ end # RightScale
@@ -0,0 +1,418 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # Protect this code from being loaded more than once since very painful
24
+ # discovering that the singleton got re-instantiated thus losing any class
25
+ # instance variable settings
26
+ unless defined?(RIGHTSCALE_LOG_DEFINED)
27
+
28
+ RIGHTSCALE_LOG_DEFINED = true
29
+
30
+ require 'logger'
31
+ require 'right_support'
32
+ require 'singleton'
33
+
34
+ require File.expand_path(File.join(File.dirname(__FILE__), 'platform'))
35
+ require File.expand_path(File.join(File.dirname(__FILE__), 'multiplexer'))
36
+ require File.expand_path(File.join(File.dirname(__FILE__), 'exceptions'))
37
+
38
+ module RightScale
39
+
40
+ # Logs both to syslog and to local file
41
+ class Log
42
+
43
+ # Expecting use of RightScale patched Singleton so that clients of this
44
+ # class do not need to use '.instance' in Log calls
45
+ include Singleton
46
+
47
+ # Default formatter for a Log
48
+ class Formatter < Logger::Formatter
49
+ @@show_time = true
50
+
51
+ # Set whether to show time in logged messages
52
+ #
53
+ # === Parameters
54
+ # show(Boolean):: Whether time should be shown
55
+ def show_time=(show=false)
56
+ @@show_time = show
57
+ end
58
+
59
+ # Prints a log message as 'datetime progname[pid]: message' if @@show_time == true;
60
+ # otherwise, doesn't print the datetime
61
+ #
62
+ # === Parameters
63
+ # severity(String):: Severity of event
64
+ # time(Time):: Date-time
65
+ # progname(String):: Program name
66
+ # msg(Object):: Message object that can be converted to a string
67
+ #
68
+ # === Return
69
+ # Formatted message
70
+ def call(severity, time, progname, msg)
71
+ if @@show_time
72
+ sprintf("%s %s[%d]: %s\n", format_datetime(time), progname, Process.pid, msg2str(msg))
73
+ else
74
+ sprintf("%s[%d]: %s\n", progname, Process.pid, msg2str(msg))
75
+ end
76
+ end
77
+
78
+ # Converts some argument to a Logger.severity() call to a string
79
+ # Regular strings pass through like normal, Exceptions get formatted
80
+ # as "message (class)\nbacktrace", and other random stuff gets put
81
+ # through "object.inspect"
82
+ #
83
+ # === Parameters
84
+ # msg(Object):: Message object to be converted to string
85
+ #
86
+ # === Return
87
+ # String
88
+ def msg2str(msg)
89
+ case msg
90
+ when ::String
91
+ msg
92
+ when ::Exception
93
+ "#{ msg.message } (#{ msg.class })\n" <<
94
+ (msg.backtrace || []).join("\n")
95
+ else
96
+ msg.inspect
97
+ end
98
+ end
99
+ end
100
+
101
+ # Map of log levels symbols associated with corresponding Logger constant
102
+ LEVELS_MAP = {:debug => Logger::DEBUG,
103
+ :info => Logger::INFO,
104
+ :warn => Logger::WARN,
105
+ :error => Logger::ERROR,
106
+ :fatal => Logger::FATAL} unless defined?(LEVELS_MAP)
107
+
108
+ @@inverted_levels_map = nil
109
+
110
+ # Undefine warn to prevent Kernel#warn from being called
111
+ undef warn
112
+
113
+ def initialize
114
+ # Was log ever used?
115
+ @initialized = false
116
+ end
117
+
118
+ # Forward all method calls to underlying Logger object created with init
119
+ # Return the result of only the first registered logger to keep the interface
120
+ # consistent with that of a Logger
121
+ #
122
+ # === Parameters
123
+ # m(Symbol):: Forwarded method name
124
+ # args(Array):: Forwarded method arguments
125
+ #
126
+ # === Return
127
+ # (Object):: Result from first registered logger
128
+ def method_missing(m, *args)
129
+ init unless @initialized
130
+ @logger.level = level_from_sym(level) if @level_frozen
131
+ @logger.__send__(m, *args)
132
+ end
133
+
134
+ # Log warning and optionally append exception information
135
+ #
136
+ # === Parameters
137
+ # description(String):: Error description
138
+ # exception(Exception|String):: Associated exception or other parenthetical error information
139
+ # backtrace(Symbol):: Exception backtrace extent: :no_trace, :caller, or :trace,
140
+ # defaults to :caller
141
+ #
142
+ # === Return
143
+ # (Object):: Result from first registered logger
144
+ def warning(description, exception = nil, backtrace = :caller)
145
+ init unless @initialized
146
+ @logger.warn(format(description, exception, backtrace))
147
+ end
148
+
149
+ alias :warn :warning
150
+
151
+ # Log error and optionally append exception information
152
+ #
153
+ # === Parameters
154
+ # description(String):: Error description
155
+ # exception(Exception|String):: Associated exception or other parenthetical error information
156
+ # backtrace(Symbol):: Exception backtrace extent: :no_trace, :caller, or :trace,
157
+ # defaults to :caller
158
+ #
159
+ # === Return
160
+ # (Object):: Result from first registered logger
161
+ def error(description, exception = nil, backtrace = :caller)
162
+ init unless @initialized
163
+ @logger.error(format(description, exception, backtrace))
164
+ end
165
+
166
+ # Format error information
167
+ #
168
+ # === Parameters
169
+ # description(String):: Error description
170
+ # exception(Exception|String):: Associated exception or other parenthetical error information
171
+ # backtrace(Symbol):: Exception backtrace extent: :no_trace, :caller, or :trace,
172
+ # defaults to :caller
173
+ #
174
+ # === Return
175
+ # (Object):: Result from first registered logger
176
+ def format(description, exception = nil, backtrace = :caller)
177
+ if exception
178
+ if exception.respond_to?(:message)
179
+ description += " (#{exception.class}: #{exception.message}"
180
+ else
181
+ description += " (#{exception}"
182
+ backtrace = :no_trace
183
+ end
184
+ case backtrace
185
+ when :no_trace then description += ")"
186
+ when :caller then description += " in " + exception.backtrace[0] + ")"
187
+ when :trace then description += " in\n " + exception.backtrace.join("\n ") + ")"
188
+ end
189
+ end
190
+ description
191
+ end
192
+
193
+ # Map symbol log level to Logger constant
194
+ #
195
+ # === Parameters
196
+ # sym(Symbol):: Log level symbol, one of :debug, :info, :warn, :error or :fatal
197
+ #
198
+ # === Return
199
+ # lvl(Constant):: One of Logger::DEBUG ... Logger::FATAL
200
+ #
201
+ # === Raise
202
+ # (RightScale::Exceptions::Argument):: if level symbol is invalid
203
+ def level_from_sym(sym)
204
+ raise Exceptions::Argument, "Invalid log level symbol :#{sym}" unless LEVELS_MAP.include?(sym)
205
+ lvl = LEVELS_MAP[sym]
206
+ end
207
+
208
+ # Map Logger log level constant to symbol
209
+ #
210
+ # === Parameters
211
+ # lvl(Constant):: Log level constant, one of Logger::DEBUG ... Logger::FATAL
212
+ #
213
+ # === Return
214
+ # sym(Symbol):: One of :debug, :info, :warn, :error or :fatal
215
+ #
216
+ # === Raise
217
+ # (RightScale::Exceptions::Argument):: if level is invalid
218
+ def level_to_sym(lvl)
219
+ @@inverted_levels_map ||= LEVELS_MAP.invert
220
+ raise Exceptions::Argument, "Invalid log level: #{lvl}" unless @@inverted_levels_map.include?(lvl)
221
+ sym = @@inverted_levels_map[lvl]
222
+ end
223
+
224
+ # Read access to internal multiplexer
225
+ #
226
+ # === Return
227
+ # logger(RightScale::Multiplexer):: Multiplexer logger
228
+ def logger
229
+ init unless @initialized
230
+ logger = @logger
231
+ end
232
+
233
+ # Add new logger to list of multiplexed loggers
234
+ #
235
+ # === Parameters
236
+ # logger(Object):: Logger that should get log messages
237
+ #
238
+ # === Return
239
+ # @logger(RightScale::Multiplexer):: Multiplexer logger
240
+ def add_logger(logger)
241
+ init unless @initialized
242
+ logger.level = level_from_sym(Log.instance.level)
243
+ @logger.add(logger)
244
+ end
245
+
246
+ # Remove logger from list of multiplexed loggers
247
+ #
248
+ # === Parameters
249
+ # logger(Object):: Logger to be removed
250
+ #
251
+ # === Return
252
+ # @logger(RightScale::Multiplexer):: Multiplexer logger
253
+ def remove_logger(logger)
254
+ init unless @initialized
255
+ @logger.remove(logger)
256
+ end
257
+
258
+ # Set whether syslog should be used or to log to an agent-specific file
259
+ # This should be called before anything else
260
+ #
261
+ # === Parameters
262
+ # val(Boolean):: Whether syslog should be used (false) or
263
+ # a agent-specific log file (true)
264
+ #
265
+ # === Raise
266
+ # RuntimeError:: If logger is already initialized
267
+ def log_to_file_only(val)
268
+ raise 'Logger already initialized' if @initialized
269
+ @log_to_file_only = !!val
270
+ end
271
+
272
+ # Was logger initialized?
273
+ #
274
+ # === Return
275
+ # true:: if logger has been initialized
276
+ # false:: Otherwise
277
+ def initialized
278
+ @initialized
279
+ end
280
+
281
+ # Sets the syslog program name that will be reported
282
+ # Can only be successfully called before logging is
283
+ # initialized
284
+ #
285
+ # === Parameters
286
+ # prog_name(String):: An arbitrary string, or "nil" to use
287
+ # the default name that is based on the agent's identity
288
+ #
289
+ # === Return
290
+ # program_name(String):: The input string
291
+ #
292
+ # === Raise
293
+ # RuntimeError:: If logger is already initialized
294
+ def program_name=(prog_name)
295
+ raise 'Logger already initialized' if @initialized
296
+ @program_name = prog_name
297
+ end
298
+
299
+ # Sets the level for the Logger by symbol or by Logger constant
300
+ #
301
+ # === Parameters
302
+ # level(Object):: One of :debug, :info, :warn, :error, :fatal or
303
+ # one of "debug", "info", "warn", "error", "fatal" or
304
+ # one of Logger::INFO ... Logger::FATAL
305
+ #
306
+ # === Return
307
+ # level(Symbol):: New log level, or current level if frozen
308
+ def level=(level)
309
+ init unless @initialized
310
+ unless @level_frozen
311
+ new_level = case level
312
+ when Symbol then
313
+ level_from_sym(level)
314
+ when String then
315
+ level_from_sym(level.to_sym)
316
+ else
317
+ level
318
+ end
319
+ if new_level != @level
320
+ @logger.info("[setup] Setting log level to #{level_to_sym(new_level).to_s.upcase}")
321
+ @logger.level = @level = new_level
322
+ @notify.each { |n| n.call(@level) } if @notify
323
+ end
324
+ end
325
+ level = level_to_sym(@level)
326
+ end
327
+
328
+ # Current log level
329
+ #
330
+ # === Return
331
+ # level(Symbol):: One of :debug, :info, :warn, :error or :fatal
332
+ def level
333
+ init unless @initialized
334
+ level = level_to_sym(@level)
335
+ end
336
+
337
+ # Register callback to be activated when there is a logging configuration change
338
+ # Currently the only logging change reported is log level
339
+ #
340
+ # === Parameters
341
+ # callback(Proc):: Block to be activated with following parameter when log level changes:
342
+ # log_level(Symbol):: Current log level
343
+ #
344
+ # === Return
345
+ # true:: Always return true
346
+ def notify(callback)
347
+ @notify = (@notify ||= []) << callback
348
+ true
349
+ end
350
+
351
+ # Force log level to debug and disregard
352
+ # any further attempt to change it
353
+ #
354
+ # === Return
355
+ # true:: Always return true
356
+ def force_debug
357
+ self.level = :debug
358
+ @level_frozen = true
359
+ end
360
+
361
+ # Force use of given logger and override all defaults
362
+ #
363
+ # === Parameters
364
+ # logger(Logger):: Logger compatible object
365
+ #
366
+ # === Return
367
+ # true:: Always return true
368
+ def force_logger(logger)
369
+ @initialized = true
370
+ @logger = logger
371
+ end
372
+
373
+ protected
374
+
375
+ # Initialize logger
376
+ #
377
+ # === Parameters
378
+ # identity(String):: Log identity
379
+ # path(String):: Log directory path
380
+ # opts[:force](TrueClass|FalseClass):: Whether to re-initialize if logger
381
+ # is already initialized
382
+ # opts[:print](TrueClass|FalseClass):: Whether to print to STDOUT log destination
383
+ #
384
+ # === Return
385
+ # logger(RightScale::Multiplexer):: logger instance
386
+ def init(identity=nil, path=nil, opts={})
387
+ if opts[:force] || !@initialized
388
+ @initialized = true
389
+ @level_frozen = false
390
+ logger = nil
391
+
392
+ if @log_to_file_only || Platform.windows?
393
+ if path
394
+ file = File.join(path, "#{identity}.log")
395
+ else
396
+ file = STDOUT
397
+ end
398
+ $stderr.puts "Logging to #{file}" if opts[:print]
399
+ logger = Logger.new(file)
400
+ logger.formatter = Formatter.new
401
+ logger.progname = @program_name || identity || 'RightAgent'
402
+ logger.formatter.datetime_format = "%b %d %H:%M:%S"
403
+ else
404
+ $stderr.puts "Logging to syslog" if opts[:print]
405
+ logger = RightSupport::SystemLogger.new(@program_name || identity || 'RightAgent')
406
+ end
407
+
408
+ @logger = Multiplexer.new(logger)
409
+ self.level = :info
410
+ end
411
+ @logger
412
+ end
413
+
414
+ end # Log
415
+
416
+ end # RightScale
417
+
418
+ end # Unless already defined