| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.qpid.server.store.berkeleydb.logging; |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.logging.ErrorManager; |
| import java.util.logging.Formatter; |
| import java.util.logging.Handler; |
| import java.util.logging.Level; |
| import java.util.logging.LogRecord; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import com.sleepycat.je.cleaner.Cleaner; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| |
| public class Slf4jLoggingHandler extends Handler |
| { |
| private static Pattern NOT_DELETED_DUE_TO_PROTECTION = Pattern.compile("Cleaner has ([0-9]+) files not deleted because they are protected.*"); |
| |
| private final ConcurrentMap<String,Logger> _loggers = new ConcurrentHashMap<>(); |
| private final int _logHandlerCleanerProtectedFilesLimit; |
| |
| public Slf4jLoggingHandler(final String prefix, final int logHandlerCleanerProtectedFilesLimit) |
| { |
| setFormatter(new Formatter() |
| { |
| @Override |
| public String format(final LogRecord record) |
| { |
| return prefix + " " + formatMessage(record); |
| } |
| }); |
| _logHandlerCleanerProtectedFilesLimit = logHandlerCleanerProtectedFilesLimit; |
| } |
| |
| private interface MappedLevel |
| { |
| boolean isEnabled(final Logger logger); |
| |
| void log(Logger logger, String message); |
| } |
| |
| private static final MappedLevel ERROR = new MappedLevel() |
| { |
| @Override |
| public boolean isEnabled(final Logger logger) |
| { |
| return logger.isErrorEnabled(); |
| } |
| |
| @Override |
| public void log(final Logger logger, final String message) |
| { |
| logger.error(message); |
| } |
| }; |
| |
| private static final MappedLevel WARN = new MappedLevel() |
| { |
| @Override |
| public boolean isEnabled(final Logger logger) |
| { |
| return logger.isWarnEnabled(); |
| } |
| |
| @Override |
| public void log(final Logger logger, final String message) |
| { |
| logger.warn(message); |
| } |
| }; |
| |
| private static final MappedLevel INFO = new MappedLevel() |
| { |
| @Override |
| public boolean isEnabled(final Logger logger) |
| { |
| return logger.isInfoEnabled(); |
| } |
| |
| @Override |
| public void log(final Logger logger, final String message) |
| { |
| logger.info(message); |
| } |
| }; |
| |
| private static final MappedLevel DEBUG = new MappedLevel() |
| { |
| @Override |
| public boolean isEnabled(final Logger logger) |
| { |
| return logger.isDebugEnabled(); |
| } |
| |
| @Override |
| public void log(final Logger logger, final String message) |
| { |
| logger.debug(message); |
| } |
| }; |
| |
| private static final MappedLevel TRACE = new MappedLevel() |
| { |
| @Override |
| public boolean isEnabled(final Logger logger) |
| { |
| return logger.isTraceEnabled(); |
| } |
| |
| @Override |
| public void log(final Logger logger, final String message) |
| { |
| logger.trace(message); |
| } |
| }; |
| |
| private static final Map<Level, MappedLevel> LEVEL_MAP; |
| static |
| { |
| Map<Level, MappedLevel> map = new HashMap<>(); |
| map.put(Level.SEVERE, ERROR); |
| map.put(Level.WARNING, WARN); |
| //Note that INFO comes out at DEBUG level as the BDB logging at INFO seems to be more of a DEBUG nature |
| map.put(Level.INFO, DEBUG); |
| map.put(Level.CONFIG, DEBUG); |
| map.put(Level.FINE, TRACE); |
| map.put(Level.FINER, TRACE); |
| map.put(Level.FINEST, TRACE); |
| map.put(Level.ALL, TRACE); |
| |
| LEVEL_MAP = Collections.unmodifiableMap(map); |
| } |
| |
| @Override |
| public void publish(final LogRecord record) |
| { |
| MappedLevel level = convertLevel(record); |
| final Logger logger = getLogger(record.getLoggerName()); |
| if (level.isEnabled(logger)) |
| { |
| |
| Formatter formatter = getFormatter(); |
| |
| try |
| { |
| String message = formatter.format(record); |
| try |
| { |
| level.log(logger, message); |
| } |
| catch (RuntimeException e) |
| { |
| reportError(null, e, ErrorManager.WRITE_FAILURE); |
| } |
| } |
| catch (RuntimeException e) |
| { |
| reportError(null, e, ErrorManager.FORMAT_FAILURE); |
| } |
| } |
| } |
| |
| private Logger getLogger(String loggerName) |
| { |
| Logger logger = _loggers.get(loggerName); |
| if(logger == null) |
| { |
| logger = LoggerFactory.getLogger(loggerName); |
| _loggers.putIfAbsent(loggerName, logger); |
| } |
| return logger; |
| } |
| |
| private MappedLevel convertLevel(LogRecord record) |
| { |
| if (record.getLevel() == Level.SEVERE && isJECleaner(record) |
| && record.getMessage().startsWith("Average cleaner backlog has grown from")) |
| { |
| // this is not a real ERROR condition; reducing level to INFO |
| return INFO; |
| } |
| |
| if (record.getLevel() == Level.WARNING && isJECleaner(record)) |
| { |
| Matcher matcher = NOT_DELETED_DUE_TO_PROTECTION.matcher(record.getMessage()); |
| if (matcher.matches() && matcher.groupCount() > 0 && Integer.parseInt(matcher.group(1)) < _logHandlerCleanerProtectedFilesLimit) |
| { |
| return DEBUG; |
| } |
| } |
| |
| return convertLevel(record.getLevel()); |
| } |
| |
| private boolean isJECleaner(final LogRecord record) |
| { |
| return Cleaner.class.getName().equals(record.getLoggerName()); |
| } |
| |
| @Override |
| public boolean isLoggable(final LogRecord record) |
| { |
| MappedLevel mappedLevel = convertLevel(record.getLevel()); |
| |
| return mappedLevel.isEnabled(getLogger(record.getLoggerName())); |
| } |
| |
| private MappedLevel convertLevel(final Level level) |
| { |
| //Note that INFO comes out at DEBUG level as the BDB logging at INFO seems to be more of a DEBUG nature |
| MappedLevel result = LEVEL_MAP.get(level); |
| if(result == null) |
| { |
| if (level.intValue() >= Level.SEVERE.intValue()) |
| { |
| result = ERROR; |
| } |
| else if (level.intValue() >= Level.WARNING.intValue()) |
| { |
| result = WARN; |
| } |
| else if (level.intValue() >= Level.CONFIG.intValue()) |
| { |
| result = DEBUG; |
| } |
| else |
| { |
| result = TRACE; |
| } |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public void flush() |
| { |
| |
| } |
| |
| @Override |
| public void close() throws SecurityException |
| { |
| |
| } |
| } |