Bug Fix: Fix ability to change log level via the API

The ability to change the log level as described at https://www.elastic.co/guide/en/logstash/current/logging.html#_logging_apis appears to no longer work. The root cause of this issue is that log4j2 context we initialize is different from the log4j2 context that we are setting via the API. The fix here is to mirror the functionality of org.apache.logging.log4j.core.config.Configurator.setLevel Java method, but instead use the logging_context initialized in our code via JRuby.

Fixes #7277

Fixes #7321
This commit is contained in:
Jake Landis 2017-06-05 18:21:58 -05:00
parent 254d58d3a4
commit 55979c3a07
3 changed files with 94 additions and 1 deletions

View file

@ -7,6 +7,7 @@ module LogStash
java_import org.apache.logging.log4j.LogManager
java_import org.apache.logging.log4j.core.config.Configurator
java_import org.apache.logging.log4j.core.config.DefaultConfiguration
java_import org.apache.logging.log4j.core.config.LoggerConfig
class Logger
@@config_mutex = Mutex.new
@ -65,7 +66,7 @@ module LogStash
end
def self.configure_logging(level, path = LogManager::ROOT_LOGGER_NAME)
@@config_mutex.synchronize { Configurator.setLevel(path, Level.valueOf(level)) }
@@config_mutex.synchronize { set_level(level, path) }
rescue Exception => e
raise ArgumentError, "invalid level[#{level}] for logger[#{path}]"
end
@ -90,6 +91,30 @@ module LogStash
def self.get_logging_context
return @@logging_context
end
# Clone of org.apache.logging.log4j.core.config.Configurator.setLevel(), but using initialized @@logging_context
def self.set_level(_level, path)
configuration = @@logging_context.getConfiguration()
level = Level.valueOf(_level)
if path.nil? || path.strip.empty?
root_logger = configuration.getRootLogger()
if root_logger.getLevel() != level
root_logger.setLevel(level)
@@logging_context.updateLoggers()
end
else
package_logger = configuration.getLoggerConfig(path)
if package_logger.name != path #no package logger found
configuration.addLogger(path, LoggerConfig.new(path, level, true))
@@logging_context.updateLoggers()
elsif package_logger.getLevel() != level
package_logger.setLevel(level)
@@logging_context.updateLoggers()
end
end
end
private_class_method :set_level
end
class SlowLogger

View file

@ -33,4 +33,13 @@ class MonitoringAPI
JSON.parse(resp)
end
def logging_get
resp = Manticore.get("http://localhost:9600/_node/logging").body
JSON.parse(resp)
end
def logging_put(json_body)
resp = Manticore.put("http://localhost:9600/_node/logging", {headers: {"Content-Type" => "application/json"}, body: json_body }).body
JSON.parse(resp)
end
end

View file

@ -77,4 +77,63 @@ describe "Test Monitoring API" do
end
end
end
it "can configure logging" do
logstash_service = @fixture.get_service("logstash")
logstash_service.start_with_stdin
logstash_service.wait_for_logstash
Stud.try(max_retry.times, [StandardError, RSpec::Expectations::ExpectationNotMetError]) do
# monitoring api can fail if the subsystem isn't ready
result = logstash_service.monitoring_api.logging_get rescue nil
expect(result).not_to be_nil
expect(result["loggers"].size).to be > 0
#default
logging_get_assert logstash_service, "INFO", "TRACE"
#root logger - does not apply to logger.slowlog
logging_put_assert logstash_service.monitoring_api.logging_put({"logger." => "WARN"}.to_json)
logging_get_assert logstash_service, "WARN", "TRACE"
logging_put_assert logstash_service.monitoring_api.logging_put({"logger." => "INFO"}.to_json)
logging_get_assert logstash_service, "INFO", "TRACE"
#package logger
logging_put_assert logstash_service.monitoring_api.logging_put({"logger.logstash.agent" => "DEBUG"}.to_json)
expect(logstash_service.monitoring_api.logging_get["loggers"]["logstash.agent"]).to eq ("DEBUG")
logging_put_assert logstash_service.monitoring_api.logging_put({"logger.logstash.agent" => "INFO"}.to_json)
logging_get_assert logstash_service, "INFO", "TRACE"
#parent package loggers
logging_put_assert logstash_service.monitoring_api.logging_put({"logger.logstash" => "ERROR"}.to_json)
logging_put_assert logstash_service.monitoring_api.logging_put({"logger.slowlog" => "ERROR"}.to_json)
result = logstash_service.monitoring_api.logging_get
result["loggers"].each do | k, v |
#since we explicitly set the logstash.agent logger above, the parent logger will not take precedence
if k.eql? "logstash.agent"
expect(v).to eq("INFO")
else
expect(v).to eq("ERROR")
end
end
end
end
private
def logging_get_assert(logstash_service, logstash_level, slowlog_level)
result = logstash_service.monitoring_api.logging_get
result["loggers"].each do | k, v |
if k.start_with? "logstash"
expect(v).to eq(logstash_level)
elsif k.start_with? "slowlog"
expect(v).to eq(slowlog_level)
end
end
end
def logging_put_assert(result)
expect(result["acknowledged"]).to be(true)
end
end