logjam_agent 0.29.5 → 0.32.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|