diff --git a/README.md b/README.md index f2e3f249..c7f4e846 100644 --- a/README.md +++ b/README.md @@ -288,12 +288,12 @@ logging system, configure `jruby.rack.logging` as follows: - `servlet_context` (default): Sends log messages to the servlet context. - `stdout`: Sends log messages to the standard output stream `System.out`. - `slf4j`: Sends log messages to SLF4J. SLF4J configuration is left up to you, - please refer to http://www.slf4j.org/docs.html . -- `log4j`: Sends log messages to log4J. Again, Log4J configuration is - left up to you, consult http://logging.apache.org/log4j/ . + please refer to https://www.slf4j.org/manual.html . +- `log4j`: Sends log messages through Log4j. Only Log4j 2.x is supported, for +- configuration please consult https://logging.apache.org/log4j/2.x/index.html . - `commons_logging`: Routes logs to commons-logging. You still need to configure - an underlying logging implementation with JCL. We recommend using the logger - library wrapper directly if possible, see http://commons.apache.org/logging/ . + an underlying logging implementation with JCL. + We recommend rather using the logger library wrapper directly when possible. - `jul`: Directs log messages via Java's core logging facilities (util.logging). For those loggers that require a specific named logger, set it with the diff --git a/pom.xml b/pom.xml index dcba07d3..1c285c76 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ 3.0.6 ${project.build.directory}/rubygems 2.0.17 + 2.22.1 5.3.39 @@ -128,6 +129,18 @@ ${slf4j.version} test + + org.apache.logging.log4j + log4j-api + ${log4j.version} + provided + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + test + org.springframework spring-web diff --git a/src/main/java/org/jruby/rack/DefaultRackConfig.java b/src/main/java/org/jruby/rack/DefaultRackConfig.java index 677c0757..410ddb24 100644 --- a/src/main/java/org/jruby/rack/DefaultRackConfig.java +++ b/src/main/java/org/jruby/rack/DefaultRackConfig.java @@ -414,6 +414,7 @@ private static Map getLoggerTypes() { final Map loggerTypes = new HashMap<>(8); loggerTypes.put("commons_logging", "org.jruby.rack.logging.CommonsLoggingLogger"); loggerTypes.put("clogging", "org.jruby.rack.logging.CommonsLoggingLogger"); + loggerTypes.put("log4j", "org.jruby.rack.logging.Log4jLogger"); loggerTypes.put("slf4j", "org.jruby.rack.logging.Slf4jLogger"); loggerTypes.put("jul", "org.jruby.rack.logging.JulLogger"); return loggerTypes; diff --git a/src/main/java/org/jruby/rack/logging/Log4jLogger.java b/src/main/java/org/jruby/rack/logging/Log4jLogger.java new file mode 100644 index 00000000..f7ded7b0 --- /dev/null +++ b/src/main/java/org/jruby/rack/logging/Log4jLogger.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025 Karol Bucek LTD. + * This source code is available under the MIT license. + * See the file LICENSE.txt for details. + */ +package org.jruby.rack.logging; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jruby.rack.RackLogger; + +public class Log4jLogger extends RackLogger.Base { + + private Logger logger; + + public Log4jLogger() { + this(LogManager.ROOT_LOGGER_NAME); + } + + public Log4jLogger(String loggerName) { + setLoggerName(loggerName); + } + + public Logger getLogger() { + return logger; + } + + public void setLogger(Logger logger) { + this.logger = logger; + } + + public void setLoggerName(String loggerName) { + logger = LogManager.getLogger(loggerName); + } + + @Override + public boolean isEnabled(Level level) { + if ( level == null ) return logger.isInfoEnabled(); + switch ( level ) { + case DEBUG: return logger.isDebugEnabled(); + case INFO: return logger.isInfoEnabled(); + case WARN: return logger.isWarnEnabled(); + case ERROR: return logger.isErrorEnabled(); + case FATAL: return logger.isFatalEnabled(); + } + return logger.isTraceEnabled(); + } + + @Override + public void log(Level level, CharSequence message) { + if ( level == null ) { logger.info(message); return; } + switch ( level ) { + case DEBUG: logger.debug(message); break; + case INFO: logger.info(message); break; + case WARN: logger.warn(message); break; + case ERROR: logger.error(message); break; + case FATAL: logger.fatal(message); break; + } + } + + @Override + public void log(Level level, CharSequence message, Throwable ex) { + if ( level == null ) { logger.error(message, ex); return; } + switch ( level ) { + case DEBUG: logger.debug(message, ex); break; + case INFO: logger.info(message, ex); break; + case WARN: logger.warn(message, ex); break; + case ERROR: logger.error(message, ex); break; + case FATAL: logger.fatal(message, ex); break; + } + } + + @Override + public Level getLevel() { + if ( logger.isDebugEnabled() ) return Level.DEBUG; + if ( logger.isInfoEnabled() ) return Level.INFO ; + if ( logger.isWarnEnabled() ) return Level.WARN ; + if ( logger.isErrorEnabled() ) return Level.ERROR; + if ( logger.isFatalEnabled() ) return Level.FATAL; + return null; + } + +} diff --git a/src/spec/ruby/rack/config_spec.rb b/src/spec/ruby/rack/config_spec.rb index 6039d71d..1d809f10 100644 --- a/src/spec/ruby/rack/config_spec.rb +++ b/src/spec/ruby/rack/config_spec.rb @@ -41,6 +41,11 @@ logger.should be_a(org.jruby.rack.logging.Slf4jLogger) end + it "constructs a log4j logger from the context init param" do + @servlet_context.should_receive(:getInitParameter).with("jruby.rack.logging").and_return "log4j" + logger.should be_a(org.jruby.rack.logging.Log4jLogger) + end + it "constructs a commons logging logger from system properties" do java.lang.System.setProperty("jruby.rack.logging", "commons_logging") logger.should be_a(org.jruby.rack.logging.CommonsLoggingLogger) @@ -50,13 +55,19 @@ @servlet_context.should_receive(:getInitParameter).with("jruby.rack.logging.name").and_return "/myapp" @servlet_context.should_receive(:getInitParameter).with("jruby.rack.logging").and_return "JUL" logger.should be_a(org.jruby.rack.logging.JulLogger) - logger.logger.name.should == '/myapp' + logger.getLogger.name.should == '/myapp' end it "constructs a slf4j logger with default logger name" do java.lang.System.setProperty("jruby.rack.logging", "slf4j") logger.should be_a(org.jruby.rack.logging.Slf4jLogger) - logger.logger.name.should == 'jruby.rack' + logger.getLogger.name.should == 'jruby.rack' + end + + it "constructs a log4j logger with default logger name" do + java.lang.System.setProperty("jruby.rack.logging", "log4j") + logger.should be_a(org.jruby.rack.logging.Log4jLogger) + logger.getLogger.name.should == 'jruby.rack' end it "constructs a logger from the context init params over system properties" do