logjam_agent 0.29.5 → 0.32.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.
- checksums.yaml +4 -4
- data/README.md +49 -28
- data/Rakefile +5 -0
- data/lib/logjam_agent.rb +13 -4
- data/lib/logjam_agent/active_support/core_ext/array/extract.rb +21 -0
- data/lib/logjam_agent/active_support/parameter_filter.rb +128 -0
- data/lib/logjam_agent/buffered_logger.rb +17 -42
- data/lib/logjam_agent/middleware.rb +16 -4
- data/lib/logjam_agent/rack/logger.rb +19 -81
- data/lib/logjam_agent/rack/rails_support.rb +26 -0
- data/lib/logjam_agent/rack/sinatra_request.rb +32 -0
- data/lib/logjam_agent/railtie.rb +3 -0
- data/lib/logjam_agent/receiver.rb +22 -0
- data/lib/logjam_agent/sinatra.rb +116 -0
- data/lib/logjam_agent/syslog_like_formatter.rb +15 -7
- data/lib/logjam_agent/util.rb +6 -1
- data/lib/logjam_agent/version.rb +1 -1
- data/lib/logjam_agent/zmq_forwarder.rb +55 -30
- data/test/sinatra_app.rb +32 -0
- data/test/sinatra_classic_app.rb +31 -0
- data/test/sinatra_classic_test.rb +20 -0
- data/test/sinatra_test.rb +54 -0
- data/test/test_helper.rb +8 -3
- data/test/util_test.rb +5 -0
- metadata +69 -16
- data/.gitignore +0 -5
- data/Gemfile +0 -4
- data/logjam_agent.gemspec +0 -34
- data/script/console +0 -28
@@ -7,14 +7,19 @@ module LogjamAgent
|
|
7
7
|
class Logger < ActiveSupport::LogSubscriber
|
8
8
|
def initialize(app, taggers = nil)
|
9
9
|
@app = app
|
10
|
-
@taggers = taggers || Rails.application.config.log_tags || []
|
10
|
+
@taggers = taggers || (defined?(Rails::Railtie) ? Rails.application.config.log_tags : []) || []
|
11
11
|
@hostname = LogjamAgent.hostname
|
12
12
|
@asset_prefix = Rails.application.config.assets.prefix rescue "---"
|
13
13
|
@ignore_asset_requests = LogjamAgent.ignore_asset_requests
|
14
14
|
end
|
15
15
|
|
16
16
|
def call(env)
|
17
|
-
|
17
|
+
if env["logjam_agent.framework"] == :sinatra
|
18
|
+
request = ::Sinatra::Request.new(env)
|
19
|
+
env["rack.logger"] = logger
|
20
|
+
else
|
21
|
+
request = ActionDispatch::Request.new(env)
|
22
|
+
end
|
18
23
|
|
19
24
|
if logger.respond_to?(:tagged) && !@taggers.empty?
|
20
25
|
logger.tagged(compute_tags(request)) { call_app(request, env) }
|
@@ -88,7 +93,7 @@ module LogjamAgent
|
|
88
93
|
spoofed = nil
|
89
94
|
ip = nil
|
90
95
|
begin
|
91
|
-
ip = LogjamAgent.ip_obfuscator(env["action_dispatch.remote_ip"].to_s)
|
96
|
+
ip = LogjamAgent.ip_obfuscator((env["action_dispatch.remote_ip"] || request.ip).to_s)
|
92
97
|
rescue ActionDispatch::RemoteIp::IpSpoofAttackError => spoofed
|
93
98
|
ip = "*** SPOOFED IP ***"
|
94
99
|
end
|
@@ -107,6 +112,11 @@ module LogjamAgent
|
|
107
112
|
if completed_info = Thread.current.thread_variable_get(:time_bandits_completed_info)
|
108
113
|
_, additions, view_time, _ = completed_info
|
109
114
|
end
|
115
|
+
additions ||= []
|
116
|
+
if env["logjam_agent.framework"] == :sinatra
|
117
|
+
TimeBandits.consumed
|
118
|
+
additions.concat TimeBandits.runtimes
|
119
|
+
end
|
110
120
|
logjam_request = LogjamAgent.request
|
111
121
|
|
112
122
|
if (allowed_time_ms = env['HTTP_X_LOGJAM_CALLER_TIMEOUT'].to_i) > 0 && (run_time_ms > allowed_time_ms)
|
@@ -123,9 +133,9 @@ module LogjamAgent
|
|
123
133
|
info message unless logjam_request.ignored?
|
124
134
|
|
125
135
|
ActiveSupport::LogSubscriber.flush_all!
|
126
|
-
request_info = {
|
127
|
-
|
128
|
-
|
136
|
+
request_info = { :total_time => run_time_ms, :code => status }
|
137
|
+
request_info[:view_time] = view_time if view_time
|
138
|
+
request_info[:wait_time] = wait_time_ms if wait_time_ms > 0
|
129
139
|
logjam_request.fields.merge!(request_info)
|
130
140
|
|
131
141
|
env["time_bandits.metrics"] = TimeBandits.metrics
|
@@ -153,7 +163,7 @@ module LogjamAgent
|
|
153
163
|
|
154
164
|
result
|
155
165
|
rescue Exception => e
|
156
|
-
|
166
|
+
logger.error(e)
|
157
167
|
result
|
158
168
|
end
|
159
169
|
|
@@ -197,82 +207,10 @@ module LogjamAgent
|
|
197
207
|
|
198
208
|
headers
|
199
209
|
end
|
200
|
-
|
201
210
|
end
|
202
211
|
end
|
203
212
|
end
|
204
213
|
|
205
|
-
|
206
|
-
|
207
|
-
require 'action_controller/log_subscriber'
|
208
|
-
|
209
|
-
module ActionController #:nodoc:
|
210
|
-
|
211
|
-
class LogSubscriber
|
212
|
-
if Rails::VERSION::STRING =~ /\A3\.0/
|
213
|
-
def start_processing(event)
|
214
|
-
payload = event.payload
|
215
|
-
params = payload[:params].except(*INTERNAL_PARAMS)
|
216
|
-
|
217
|
-
controller = payload[:controller]
|
218
|
-
action = payload[:action]
|
219
|
-
full_name = "#{controller}##{action}"
|
220
|
-
action_name = LogjamAgent.action_name_proc.call(full_name)
|
221
|
-
|
222
|
-
LogjamAgent.request.fields[:action] = action_name
|
223
|
-
|
224
|
-
info " Processing by #{full_name} as #{payload[:formats].first.to_s.upcase}"
|
225
|
-
info " Parameters: #{params.inspect}" unless params.empty?
|
226
|
-
end
|
227
|
-
|
228
|
-
elsif Rails::VERSION::STRING =~ /\A3\.1/
|
229
|
-
|
230
|
-
def start_processing(event)
|
231
|
-
payload = event.payload
|
232
|
-
params = payload[:params].except(*INTERNAL_PARAMS)
|
233
|
-
format = payload[:format]
|
234
|
-
format = format.to_s.upcase if format.is_a?(Symbol)
|
235
|
-
|
236
|
-
controller = payload[:controller]
|
237
|
-
action = payload[:action]
|
238
|
-
full_name = "#{controller}##{action}"
|
239
|
-
action_name = LogjamAgent.action_name_proc.call(full_name)
|
240
|
-
|
241
|
-
LogjamAgent.request.fields[:action] = action_name
|
242
|
-
|
243
|
-
info " Processing by #{full_name} as #{format}"
|
244
|
-
info " Parameters: #{params.inspect}" unless params.empty?
|
245
|
-
end
|
246
|
-
|
247
|
-
elsif Rails::VERSION::STRING =~ /\A(3\.2|4|5|6)/
|
248
|
-
|
249
|
-
# Rails 4.1 uses method_added to automatically subscribe newly
|
250
|
-
# added methods. Since start_processing is already defined, the
|
251
|
-
# net effect is that start_processing gets called
|
252
|
-
# twice. Therefore, we temporarily switch to protected mode and
|
253
|
-
# change it back later to public.
|
254
|
-
protected
|
255
|
-
def start_processing(event)
|
256
|
-
payload = event.payload
|
257
|
-
params = payload[:params].except(*INTERNAL_PARAMS)
|
258
|
-
format = payload[:format]
|
259
|
-
format = format.to_s.upcase if format.is_a?(Symbol)
|
260
|
-
|
261
|
-
controller = payload[:controller]
|
262
|
-
action = payload[:action]
|
263
|
-
full_name = "#{controller}##{action}"
|
264
|
-
action_name = LogjamAgent.action_name_proc.call(full_name)
|
265
|
-
|
266
|
-
LogjamAgent.request.fields[:action] = action_name
|
267
|
-
|
268
|
-
info "Processing by #{full_name} as #{format}"
|
269
|
-
info " Parameters: #{params.inspect}" unless params.empty?
|
270
|
-
end
|
271
|
-
public :start_processing
|
272
|
-
|
273
|
-
else
|
274
|
-
raise "logjam_agent ActionController monkey patch is not compatible with your Rails version"
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
214
|
+
if defined?(Rails::Railtie)
|
215
|
+
require_relative "rails_support"
|
278
216
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# patch the actioncontroller logsubscriber to set the action on the logjam logger as soon as it starts processing the request
|
2
|
+
require 'action_controller/metal/instrumentation'
|
3
|
+
require 'action_controller/log_subscriber'
|
4
|
+
|
5
|
+
module ActionController #:nodoc:
|
6
|
+
|
7
|
+
class LogSubscriber
|
8
|
+
def start_processing(event)
|
9
|
+
payload = event.payload
|
10
|
+
params = payload[:params].except(*INTERNAL_PARAMS)
|
11
|
+
format = payload[:format]
|
12
|
+
format = format.to_s.upcase if format.is_a?(Symbol)
|
13
|
+
|
14
|
+
controller = payload[:controller]
|
15
|
+
action = payload[:action]
|
16
|
+
full_name = "#{controller}##{action}"
|
17
|
+
action_name = LogjamAgent.action_name_proc.call(full_name)
|
18
|
+
|
19
|
+
LogjamAgent.request.fields[:action] = action_name
|
20
|
+
|
21
|
+
info "Processing by #{full_name} as #{format}"
|
22
|
+
info " Parameters: #{params.inspect}" unless params.empty?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
begin
|
3
|
+
require 'active_support/parameter_filter'
|
4
|
+
rescue LoadError
|
5
|
+
require_relative '../active_support/parameter_filter'
|
6
|
+
end
|
7
|
+
|
8
|
+
# Extend the Sinatra Request class with some methods to make it look more like an
|
9
|
+
# ActionDispatch request.
|
10
|
+
|
11
|
+
class Sinatra::Request
|
12
|
+
alias_method :method, :request_method
|
13
|
+
def query_parameters; self.GET; end
|
14
|
+
def request_parameters; self.POST; end
|
15
|
+
|
16
|
+
def parameter_filter
|
17
|
+
ActiveSupport::ParameterFilter.new(LogjamAgent.parameter_filters)
|
18
|
+
end
|
19
|
+
|
20
|
+
KV_RE = '[^&;=]+'
|
21
|
+
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
|
22
|
+
|
23
|
+
def filtered_path
|
24
|
+
return path if query_string.empty?
|
25
|
+
filter = parameter_filter
|
26
|
+
filtered_query_string = query_string.gsub(PAIR_RE) do |_|
|
27
|
+
filter.filter($1 => $2).first.join("=")
|
28
|
+
end
|
29
|
+
"#{path}?#{filtered_query_string}"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/logjam_agent/railtie.rb
CHANGED
@@ -80,6 +80,9 @@ module LogjamAgent
|
|
80
80
|
# disable logjam request forwarding by default in test environment
|
81
81
|
LogjamAgent.disable! if Rails.env.test?
|
82
82
|
|
83
|
+
# only sent pings in production like environments
|
84
|
+
LogjamAgent.ensure_ping_at_exit = !%w(test development).include?(Rails.env.to_s)
|
85
|
+
|
83
86
|
# patch controller testing to create a logjam request, because middlewares aren't executed
|
84
87
|
if Rails.env.test?
|
85
88
|
ActiveSupport.on_load(:action_controller) do
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module LogjamAgent
|
2
|
+
class Receiver
|
3
|
+
def initialize
|
4
|
+
@socket = ZMQForwarder.context.socket(ZMQ::ROUTER)
|
5
|
+
@socket.setsockopt(ZMQ::RCVTIMEO, 100)
|
6
|
+
if @socket.bind("inproc://app") < 0
|
7
|
+
raise "ZMQ error on binding: #{ZMQ::Util.error_string}"
|
8
|
+
end
|
9
|
+
at_exit { @socket.close }
|
10
|
+
end
|
11
|
+
|
12
|
+
def receive
|
13
|
+
answer_parts = []
|
14
|
+
if @socket.recv_strings(answer_parts) < 0
|
15
|
+
raise "ZMQ error on receiving: #{ZMQ::Util.error_string}"
|
16
|
+
end
|
17
|
+
answer_parts.shift
|
18
|
+
answer_parts[2] = JSON.parse(answer_parts[2])
|
19
|
+
answer_parts
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'logger'
|
3
|
+
require 'logjam_agent'
|
4
|
+
require 'logjam_agent/middleware'
|
5
|
+
require 'logjam_agent/rack/sinatra_request'
|
6
|
+
require 'logjam_agent/rack/logger'
|
7
|
+
require 'time_bandits'
|
8
|
+
|
9
|
+
module LogjamAgent
|
10
|
+
module Sinatra
|
11
|
+
class Middleware
|
12
|
+
def initialize(app)
|
13
|
+
app_with_logging = LogjamAgent::Rack::Logger.new(app)
|
14
|
+
@app = LogjamAgent::Middleware.new(app_with_logging, :sinatra)
|
15
|
+
end
|
16
|
+
def call(env)
|
17
|
+
@app.call(env)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Helpers
|
22
|
+
def action_name(action_name)
|
23
|
+
LogjamAgent.request.fields[:action] = action_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def logger
|
27
|
+
LogjamAgent.logger
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup_logjam_logger
|
32
|
+
log_path = ENV["APP_LOG_TO_STDOUT"].present? ? STDOUT : "#{settings.root}/log/#{LogjamAgent.environment_name}.log"
|
33
|
+
logger = LogjamAgent::BufferedLogger.new(log_path) rescue LogjamAgent::BufferedLogger.new(STDERR)
|
34
|
+
|
35
|
+
loglevel = settings.respond_to?(:loglevel) ? settings.loglevel : :info
|
36
|
+
logger.level = ::Logger.const_get(loglevel.to_s.upcase)
|
37
|
+
|
38
|
+
LogjamAgent.log_device_log_level = logger.level
|
39
|
+
LogjamAgent.log_device_log_level = ::Logger::ERROR unless %i[test development].include?(settings.environment.to_sym)
|
40
|
+
|
41
|
+
logger.formatter = LogjamAgent::SyslogLikeFormatter.new
|
42
|
+
logger = ActiveSupport::TaggedLogging.new(logger)
|
43
|
+
LogjamAgent.logger = logger
|
44
|
+
ActiveSupport::LogSubscriber.logger = logger
|
45
|
+
|
46
|
+
log_path = ENV["APP_LOG_TO_STDOUT"].present? ? STDOUT : "#{settings.root}/log/logjam_agent_error.log"
|
47
|
+
forwarding_error_logger = ::Logger.new(log_path) rescue ::Logger.new(STDERR)
|
48
|
+
forwarding_error_logger.level = ::Logger::ERROR
|
49
|
+
forwarding_error_logger.formatter = ::Logger::Formatter.new
|
50
|
+
LogjamAgent.forwarding_error_logger = forwarding_error_logger
|
51
|
+
|
52
|
+
truncate_overlong_params = lambda { |key, value|
|
53
|
+
max_size = LogjamAgent.max_logged_size_for(key)
|
54
|
+
if value.is_a?(String) && value.size > max_size
|
55
|
+
value[max_size..-1] = " ... [TRUNCATED]"
|
56
|
+
end
|
57
|
+
}
|
58
|
+
LogjamAgent.parameter_filters << truncate_overlong_params
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.registered(app)
|
62
|
+
app.helpers Helpers
|
63
|
+
LogjamAgent.environment_name = ENV['LOGJAM_ENV'] || app.settings.environment.to_s
|
64
|
+
LogjamAgent.auto_detect_logged_exceptions
|
65
|
+
LogjamAgent.disable! if app.settings.environment.to_sym == :test
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# For classic apps.
|
71
|
+
Sinatra.register LogjamAgent::Sinatra
|
72
|
+
|
73
|
+
# We already supply a logger.
|
74
|
+
Sinatra::Base.class_eval do
|
75
|
+
class << self
|
76
|
+
def setup_logging(builder); end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Patch Sinatra's render logic to compute corrected view times.
|
81
|
+
module LogjamAgent
|
82
|
+
module ComputeRenderTimes
|
83
|
+
def render(engine, data, options = {}, locals = {}, &block)
|
84
|
+
consumed_before_rendering = TimeBandits.consumed
|
85
|
+
result = exception = nil
|
86
|
+
duration = Benchmark.ms do
|
87
|
+
begin
|
88
|
+
result = super
|
89
|
+
rescue => exception
|
90
|
+
end
|
91
|
+
end
|
92
|
+
consumed_during_rendering = TimeBandits.consumed - consumed_before_rendering
|
93
|
+
duration -= consumed_during_rendering
|
94
|
+
raise exception if exception
|
95
|
+
result
|
96
|
+
ensure
|
97
|
+
Thread.current.thread_variable_set(
|
98
|
+
:time_bandits_completed_info,
|
99
|
+
[ duration, ["Views: %.3fms" % duration.to_f], duration, "" ]
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
Sinatra::Base.prepend LogjamAgent::ComputeRenderTimes
|
106
|
+
|
107
|
+
# Define exception, but don't do anything about it. Sneaky!
|
108
|
+
module ActionDispatch
|
109
|
+
module RemoteIp
|
110
|
+
class IpSpoofAttackError < StandardError; end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Add GC time bandit
|
115
|
+
TimeBandits.reset
|
116
|
+
TimeBandits.add TimeBandits::TimeConsumers::GarbageCollection.instance if GC.respond_to? :enable_stats
|
@@ -5,11 +5,19 @@ module LogjamAgent
|
|
5
5
|
def initialize
|
6
6
|
@hostname = LogjamAgent.hostname
|
7
7
|
@app_name = "rails"
|
8
|
-
@attributes = []
|
9
8
|
@newline = ActiveSupport::VERSION::STRING < "4.0" ? "" : "\n"
|
10
9
|
end
|
11
10
|
|
12
|
-
|
11
|
+
def attributes=(attributes)
|
12
|
+
Thread.current.thread_variable_set(:__logjam_formatter_attributes__, attributes)
|
13
|
+
end
|
14
|
+
|
15
|
+
def attributes
|
16
|
+
unless attributes = Thread.current.thread_variable_get(:__logjam_formatter_attributes__)
|
17
|
+
attributes = Thread.current.thread_variable_set(:__logjam_formatter_attributes__, [])
|
18
|
+
end
|
19
|
+
attributes
|
20
|
+
end
|
13
21
|
|
14
22
|
SEV_LABEL = Logger::SEV_LABEL.map{|sev| "%-5s" % sev}
|
15
23
|
|
@@ -33,7 +41,7 @@ module LogjamAgent
|
|
33
41
|
"#{format_severity(severity)} #{format_time(timestamp)}#{render_attributes}#{format_host_info(progname)}: #{format_message(msg)}#{@newline}"
|
34
42
|
end
|
35
43
|
|
36
|
-
if !defined?(Rails) || Rails.env.development?
|
44
|
+
if !defined?(Rails::Railtie) || Rails.env.development?
|
37
45
|
def format_host_info(progname); ""; end
|
38
46
|
else
|
39
47
|
def format_host_info(progname)
|
@@ -42,19 +50,19 @@ module LogjamAgent
|
|
42
50
|
end
|
43
51
|
|
44
52
|
def render_attributes
|
45
|
-
|
53
|
+
attributes.map{|key, value| " #{key}[#{value}]"}.join
|
46
54
|
end
|
47
55
|
|
48
56
|
def set_attribute(name, value)
|
49
|
-
if attribute =
|
57
|
+
if attribute = attributes.detect{|n,v| n == name}
|
50
58
|
attribute[1] = value
|
51
59
|
else
|
52
|
-
|
60
|
+
attributes << [name, value]
|
53
61
|
end
|
54
62
|
end
|
55
63
|
|
56
64
|
def reset_attributes
|
57
|
-
|
65
|
+
self.attributes = []
|
58
66
|
end
|
59
67
|
end
|
60
68
|
end
|
data/lib/logjam_agent/util.rb
CHANGED
@@ -62,7 +62,12 @@ module LogjamAgent
|
|
62
62
|
protocol, host, port = %r{\A(?:([^:]+)://)?([^:]+)(?::(\d+))?\z}.match(spec).captures
|
63
63
|
protocol ||= "tcp"
|
64
64
|
port ||= default_port
|
65
|
-
|
65
|
+
if protocol == "inproc"
|
66
|
+
# should only be used for integration tests
|
67
|
+
"#{protocol}://#{host}"
|
68
|
+
else
|
69
|
+
"#{protocol}://#{host}:#{port}"
|
70
|
+
end
|
66
71
|
end
|
67
72
|
end
|
68
73
|
end
|
data/lib/logjam_agent/version.rb
CHANGED
@@ -15,7 +15,8 @@ module LogjamAgent
|
|
15
15
|
@config[:host] = "localhost" if @config[:host].blank?
|
16
16
|
@sequence = SEQUENCE_START
|
17
17
|
@socket = nil
|
18
|
-
|
18
|
+
@ping_ensured = false
|
19
|
+
@socket_mutex = Mutex.new
|
19
20
|
end
|
20
21
|
|
21
22
|
def connection_specs
|
@@ -35,11 +36,11 @@ module LogjamAgent
|
|
35
36
|
}
|
36
37
|
end
|
37
38
|
|
38
|
-
@@
|
39
|
+
@@context_mutex = Mutex.new
|
39
40
|
@@zmq_context = nil
|
40
41
|
|
41
42
|
def self.context
|
42
|
-
@@
|
43
|
+
@@context_mutex.synchronize do
|
43
44
|
@@zmq_context ||=
|
44
45
|
begin
|
45
46
|
require 'ffi-rzmq'
|
@@ -50,26 +51,18 @@ module LogjamAgent
|
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
53
|
-
def socket
|
54
|
-
return @socket if @socket
|
55
|
-
@socket = self.class.context.socket(ZMQ::DEALER)
|
56
|
-
@socket.setsockopt(ZMQ::LINGER, @config[:linger])
|
57
|
-
@socket.setsockopt(ZMQ::SNDHWM, @config[:snd_hwm])
|
58
|
-
@socket.setsockopt(ZMQ::RCVHWM, @config[:rcv_hwm])
|
59
|
-
@socket.setsockopt(ZMQ::RCVTIMEO, @config[:rcv_timeo])
|
60
|
-
@socket.setsockopt(ZMQ::SNDTIMEO, @config[:snd_timeo])
|
61
|
-
spec = connection_specs.sort_by{rand}.first
|
62
|
-
@socket.connect(spec)
|
63
|
-
@socket
|
64
|
-
end
|
65
|
-
|
66
54
|
def reset
|
67
|
-
|
68
|
-
|
69
|
-
@socket = nil
|
55
|
+
@socket_mutex.synchronize do
|
56
|
+
reset_without_locking
|
70
57
|
end
|
71
58
|
end
|
72
59
|
|
60
|
+
def ensure_ping_at_exit
|
61
|
+
return if @ping_ensured
|
62
|
+
at_exit { ping; reset }
|
63
|
+
@ping_ensured = true
|
64
|
+
end
|
65
|
+
|
73
66
|
def forward(data, options={})
|
74
67
|
app_env = options[:app_env] || @app_env
|
75
68
|
key = options[:routing_key] || "logs.#{app_env.sub('-','.')}"
|
@@ -77,27 +70,57 @@ module LogjamAgent
|
|
77
70
|
key += ".#{engine}"
|
78
71
|
end
|
79
72
|
msg = LogjamAgent.encode_payload(data)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
73
|
+
@socket_mutex.synchronize do
|
74
|
+
if options[:sync]
|
75
|
+
send_receive(app_env, key, msg)
|
76
|
+
else
|
77
|
+
publish(app_env, key, msg)
|
78
|
+
end
|
84
79
|
end
|
85
80
|
rescue => error
|
86
81
|
reraise_expectation_errors!
|
87
82
|
raise ForwardingError.new(error.message)
|
88
83
|
end
|
89
84
|
|
85
|
+
private
|
86
|
+
|
87
|
+
def reset_without_locking
|
88
|
+
if @socket
|
89
|
+
@socket.close
|
90
|
+
@socket = nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# this method assumes the caller holds the socket mutex
|
95
|
+
def socket
|
96
|
+
return @socket if @socket
|
97
|
+
@socket = self.class.context.socket(ZMQ::DEALER)
|
98
|
+
raise "ZMQ error on socket creation: #{ZMQ::Util.error_string}" if @socket.nil?
|
99
|
+
if LogjamAgent.ensure_ping_at_exit
|
100
|
+
ensure_ping_at_exit
|
101
|
+
else
|
102
|
+
at_exit { reset }
|
103
|
+
end
|
104
|
+
@socket.setsockopt(ZMQ::LINGER, @config[:linger])
|
105
|
+
@socket.setsockopt(ZMQ::SNDHWM, @config[:snd_hwm])
|
106
|
+
@socket.setsockopt(ZMQ::RCVHWM, @config[:rcv_hwm])
|
107
|
+
@socket.setsockopt(ZMQ::RCVTIMEO, @config[:rcv_timeo])
|
108
|
+
@socket.setsockopt(ZMQ::SNDTIMEO, @config[:snd_timeo])
|
109
|
+
spec = connection_specs.sort_by{rand}.first
|
110
|
+
@socket.connect(spec)
|
111
|
+
@socket
|
112
|
+
end
|
113
|
+
|
90
114
|
def publish(app_env, key, data)
|
91
115
|
info = pack_info(@sequence = next_fixnum(@sequence))
|
92
116
|
parts = [app_env, key, data, info]
|
93
117
|
if socket.send_strings(parts, ZMQ::DONTWAIT) < 0
|
118
|
+
error = ZMQ::Util.error_string
|
94
119
|
reset if connection_specs.size > 1
|
95
|
-
raise "ZMQ error on publishing: #{
|
120
|
+
raise "ZMQ error on publishing: #{error}"
|
96
121
|
end
|
97
122
|
end
|
98
123
|
|
99
|
-
private
|
100
|
-
|
101
124
|
def log_warning(message)
|
102
125
|
LogjamAgent.error_handler.call ForwardingWarning.new(message)
|
103
126
|
end
|
@@ -110,12 +133,12 @@ module LogjamAgent
|
|
110
133
|
answer_parts = []
|
111
134
|
if socket.send_strings(request_parts) < 0
|
112
135
|
log_warning "ZMQ error on sending: #{ZMQ::Util.error_string}"
|
113
|
-
|
136
|
+
reset_without_locking
|
114
137
|
return nil
|
115
138
|
end
|
116
139
|
if socket.recv_strings(answer_parts) < 0
|
117
140
|
log_warning "ZMQ error on receiving: #{ZMQ::Util.error_string}"
|
118
|
-
|
141
|
+
reset_without_locking
|
119
142
|
return nil
|
120
143
|
end
|
121
144
|
if answer_parts.first != "" || !VALID_RESPONSE_CODES.include?(answer_parts.second.to_s.to_i)
|
@@ -125,8 +148,10 @@ module LogjamAgent
|
|
125
148
|
end
|
126
149
|
|
127
150
|
def ping
|
128
|
-
|
129
|
-
|
151
|
+
@socket_mutex.synchronize do
|
152
|
+
if @socket && !send_receive("ping", @app_env, "{}", NO_COMPRESSION)
|
153
|
+
log_warning "failed to receive pong"
|
154
|
+
end
|
130
155
|
end
|
131
156
|
end
|
132
157
|
|