/* * Copyright (c) 2011-2014 the original author or authors * * Licensed 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 io.werval.modules.jpa.internal; import java.util.HashMap; import java.util.Map; import io.werval.util.Strings; import org.eclipse.persistence.logging.AbstractSessionLog; import org.eclipse.persistence.logging.SessionLog; import org.eclipse.persistence.logging.SessionLogEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * SLF4J Logging for EclipseLink. * * Froked from Miguel Angel Sosvilla Luis <a href="https://gist.github.com/msosvi/1325764">gist</a>. * <p> * In order to register this class with EclipseLink, set the value of property * <code>eclipselink.logging.logger</code> to this class' fully qualified name * (<code>org.eclipse.persistence.logging.Slf4jSessionLogger</code>). * <p> * Once registered, log level configuration is no longer defined within EclipseLink (using property * <code>eclipselink.logging.level</code>), but within whatever SL4J implementation is being used * (<code>logback.xml</code> for <code>logback</code>, <code>log4j.properties</code> for <code>log4j</code>, etc.). * <p> * The rest of EclipseLink logging properties can still be used (<code>eclipselink.logging.timestamp</code>, * <code>eclipselink.logging.thread</code>, <code>eclipselink.logging.session</code>, * <code>eclipselink.logging.connection</code> and <code>eclipselink.logging.parameters</code>) to configure the output * format. * <p> * The following log categories are available: * <ul> * <li>org.eclipse.persistence.logging.default</li> * <li>org.eclipse.persistence.logging.sql</li> * <li>org.eclipse.persistence.logging.metadata</li> * <li>org.eclipse.persistence.logging.transaction</li> * <li>org.eclipse.persistence.logging.event</li> * <li>org.eclipse.persistence.logging.connection</li> * <li>org.eclipse.persistence.logging.query</li> * <li>org.eclipse.persistence.logging.cache</li> * <li>org.eclipse.persistence.logging.propagation</li> * <li>org.eclipse.persistence.logging.sequencing</li> * <li>org.eclipse.persistence.logging.ejb</li> * <li>org.eclipse.persistence.logging.ejb_or_metadata</li> * <li>org.eclipse.persistence.logging.weaver</li> * <li>org.eclipse.persistence.logging.properties</li> * <li>org.eclipse.persistence.logging.server</li> * </ul> * <p> * The mapping between EclipseLink and SLF4J log levels is as follows: * <ul> * <li>ALL,FINER,FINEST -> TRACE</li> * <li>FINE -> DEBUG</li> * <li>CONFIG,INFO -> INFO</li> * <li>WARNING -> WARN</li> * <li>SEVERE -> ERROR</li> * </ul> */ public class Slf4jSessionLogger extends AbstractSessionLog { public static final String ECLIPSELINK_NAMESPACE = "org.eclipse.persistence.logging"; public static final String DEFAULT_CATEGORY = "default"; public static final String DEFAULT_ECLIPSELINK_NAMESPACE = ECLIPSELINK_NAMESPACE + "." + DEFAULT_CATEGORY; private final Map<String, Logger> categoryLoggers; private final Map<Integer, LogLevel> mapLevels; public Slf4jSessionLogger() { super(); // Initialize loggers eagerly categoryLoggers = new HashMap<>(); for( String category : SessionLog.loggerCatagories ) { categoryLoggers.put( category, LoggerFactory.getLogger( ECLIPSELINK_NAMESPACE + "." + category ) ); } categoryLoggers.put( DEFAULT_CATEGORY, LoggerFactory.getLogger( DEFAULT_ECLIPSELINK_NAMESPACE ) ); // Mapping between EclipseLink and SLF4J log levels. mapLevels = new HashMap<>(); mapLevels.put( SessionLog.ALL, LogLevel.TRACE ); mapLevels.put( SessionLog.FINEST, LogLevel.TRACE ); mapLevels.put( SessionLog.FINER, LogLevel.TRACE ); mapLevels.put( SessionLog.FINE, LogLevel.DEBUG ); mapLevels.put( SessionLog.CONFIG, LogLevel.INFO ); mapLevels.put( SessionLog.INFO, LogLevel.INFO ); mapLevels.put( SessionLog.WARNING, LogLevel.WARN ); mapLevels.put( SessionLog.SEVERE, LogLevel.ERROR ); } @Override public void log( SessionLogEntry entry ) { if( !shouldLog( entry.getLevel(), entry.getNameSpace() ) ) { return; } Logger logger = getLogger( entry.getNameSpace() ); LogLevel logLevel = getLogLevel( entry.getLevel() ); StringBuilder message = new StringBuilder(); message.append( getSupplementDetailString( entry ) ); message.append( formatMessage( entry ) ); switch( logLevel ) { case TRACE: logger.trace( message.toString() ); break; case DEBUG: logger.debug( message.toString() ); break; case INFO: logger.info( message.toString() ); break; case WARN: logger.warn( message.toString() ); break; case ERROR: logger.error( message.toString() ); break; default: throw new InternalError(); } } @Override public boolean shouldLog( int level, String category ) { Logger logger = getLogger( category ); boolean resp = false; LogLevel logLevel = getLogLevel( level ); switch( logLevel ) { case TRACE: resp = logger.isTraceEnabled(); break; case DEBUG: resp = logger.isDebugEnabled(); break; case INFO: resp = logger.isInfoEnabled(); break; case WARN: resp = logger.isWarnEnabled(); break; case ERROR: resp = logger.isErrorEnabled(); break; default: resp = true; } return resp; } @Override public boolean shouldLog( int level ) { return shouldLog( level, "default" ); } /** * @return Return true if SQL logging should log visible bind parameters. If the * shouldDisplayData is not set, return false. */ @Override public boolean shouldDisplayData() { if( this.shouldDisplayData != null ) { return shouldDisplayData; } else { return false; } } /** * INTERNAL: Return the Logger for the given category */ private Logger getLogger( String category ) { if( Strings.isEmpty( category ) || !this.categoryLoggers.containsKey( category ) ) { category = DEFAULT_CATEGORY; } return categoryLoggers.get( category ); } /** * Return the corresponding Slf4j Level for a given EclipseLink level. */ private LogLevel getLogLevel( Integer level ) { LogLevel logLevel = mapLevels.get( level ); if( logLevel == null ) { logLevel = LogLevel.OFF; } return logLevel; } /** * SLF4J log levels. */ enum LogLevel { TRACE, DEBUG, INFO, WARN, ERROR, OFF } }