require 'jruby' require 'fileutils' module Trinidad module Logging JUL = Java::JavaUtilLogging LogFactory = Java::OrgApacheJuliLogging::LogFactory @@configured = nil # Configure the "global" Trinidad logging. def self.configure(log_level = nil) return false if @@configured @@configured = true root_logger = JUL::Logger.getLogger('') level = parse_log_level(log_level, :INFO) out_handler = new_console_handler JRuby.runtime.out out_handler.formatter = console_formatter root_logger.synchronized do root_logger.handlers.to_a.each do |handler| root_logger.remove_handler(handler) if handler.is_a?(JUL::ConsoleHandler) end root_logger.add_handler(out_handler) if JRuby.runtime.out != Java::JavaLang::System.out || JRuby.runtime.err != Java::JavaLang::System.err # NOTE: only add err handler if customized STDOUT or STDERR : err_handler = new_console_handler JRuby.runtime.err err_handler.formatter = console_formatter err_handler.level = level.intValue > JUL::Level::WARNING.intValue ? level : JUL::Level::WARNING # only >= WARNING on STDERR root_logger.add_handler(err_handler) end set_log_level(root_logger, level) end silence_tomcat_loggers root_logger end # Force logging (re-)configuration. # @see #configure def self.configure!(log_level = nil) ( @@configured = false ) || configure(log_level) end def self.configure_web_app!(web_app, context) configure_web_app!(web_app, context, true) end # Configure logging for a web application. def self.configure_web_app(web_app, context, reset = nil) param_name, param_value = 'jruby.rack.logging', 'JUL' # 1. delegate (jruby-rack) servlet log to JUL if set_value = web_app_context_param(web_app, context, param_name) return nil if set_value.upcase != param_value else context.add_parameter(param_name, param_value) end # 2. use Tomcat's JUL logger name (unless set) : param_name = 'jruby.rack.logging.name' unless logger_name = web_app_context_param(web_app, context, param_name) # for a context path e.g. '/foo' most likely smt of the following : # org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/foo] context.add_parameter(param_name, logger_name = context.send(:logName)) end configure # make sure 'global' logging is configured logger = JUL::Logger.getLogger(logger_name) # exclusive for web app logger.handlers.each { |h| logger.remove_handler(h); h.close } if reset # avoid duplicate calls - do not configure (e.g. FileHandler) twice : return false unless logger.handlers.empty? logging = web_app.logging logger.level = parse_log_level(logging[:level], nil) # delegate to root (console) output only in development mode : logger.use_parent_handlers = logging[:use_parent_handlers] # logging: # file: # dir: log # [RAILS_ROOT]/log # prefix: production # suffix: .log if file = logging[:file] prefix, suffix = file[:prefix], file[:suffix] # {prefix}{date}{suffix} file_handler = FileHandler.new(file[:dir] || file[:directory], prefix, suffix) file_handler.rotatable = file.key?(:rotatable) ? file[:rotatable] : file[:rotate] file_handler.buffer_size = file[:buffer_size] if file[:buffer_size] format = file.key?(:format) ? file[:format] : logging[:format] file_handler.formatter = web_app_formatter(format) # nil uses default logger.add_handler(file_handler) end logger end protected def self.console_formatter MessageFormatter.new end def self.web_app_formatter(format = nil) # format used by Rails "2012-06-13 16:42:21 +0200" DefaultFormatter.new(format.nil? ? 'yyyy-MM-dd HH:mm:ss Z' : format) end private def self.parse_log_level(log_level, default = nil) log_level = log_level && log_level.to_s.upcase unless JUL::Level.constants.find { |level| level.to_s == log_level } log_level = { # try mapping common level names to JUL names 'ERROR' => 'SEVERE', 'WARN' => 'WARNING', 'DEBUG' => 'FINE' }[log_level] log_level = default ? default.to_s.upcase : nil unless log_level end JUL::Level.parse(log_level) if log_level end def self.set_log_level(logger, level) logger.level = level; LogFactory.getLog(logger.name) end def self.silence_tomcat_loggers # org.apache.coyote.http11.Http11Protocol INFO: Initializing ProtocolHandler ["http-bio-3000"] # org.apache.catalina.core.StandardService INFO: Starting service Tomcat # org.apache.catalina.core.StandardEngine INFO: Starting Servlet Engine: Apache Tomcat/7.0.27 # org.apache.catalina.startup.ContextConfig INFO: No global web.xml found # org.apache.coyote.http11.Http11Protocol INFO: Starting ProtocolHandler ["http-bio-3000"] level = JUL::Level::WARNING logger_names = [ 'org.apache.catalina.core.StandardService', 'org.apache.catalina.core.StandardEngine', 'org.apache.catalina.startup.ContextConfig', ] for name in logger_names logger = JUL::Logger.getLogger(name) set_log_level(logger, level) if logger end end def self.web_app_context_param(web_app, context, name) context.find_parameter(name) || web_app.web_xml_context_param(name) end JUL::ConsoleHandler.class_eval do field_accessor :sealed rescue nil field_writer :writer rescue nil end def self.new_console_handler(stream) handler = JUL::ConsoleHandler.new # sets output stream to System.err handler.writer = nil if handler.respond_to?(:writer=) # avoid writer.close if handler.respond_to?(:sealed) && handler.sealed handler.sealed = false # avoid manager security checks handler.setOutputStream(stream) # closes previous writer if != null handler.sealed = true else handler.setOutputStream(stream) end handler end FileHandler = Java::RbTrinidadLogging::FileHandler # We're truly missing a #formatThrown exception helper method. JUL::Formatter.class_eval do # :nodoc: LINE_SEP = java.lang.System.getProperty("line.separator") protected def formatThrown(record) if record.thrown writer = java.io.StringWriter.new(1024) print_writer = java.io.PrintWriter.new(writer) print_writer.println record.thrown.printStackTrace(print_writer) print_writer.close return writer.toString end end end # A message formatter only prints the log message (and the thrown value). MessageFormatter = Java::RbTrinidadLogging::MessageFormatter DefaultFormatter = Java::RbTrinidadLogging::DefaultFormatter end LogFormatter = Logging::DefaultFormatter # backwards compatibility end