diff --git a/src/tap/config/ConfigurableServiceConnection.java b/src/tap/config/ConfigurableServiceConnection.java index 48d3db434f856494ae11cf45b2432fa69789249d..20712904bb788ce919042e9e81288da804c572c7 100644 --- a/src/tap/config/ConfigurableServiceConnection.java +++ b/src/tap/config/ConfigurableServiceConnection.java @@ -1,9 +1,29 @@ package tap.config; +/* + * This file is part of TAPLibrary. + * + * TAPLibrary is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * TAPLibrary is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2016-2017 - Astronomisches Rechen Institut (ARI) + */ + import static tap.config.TAPConfiguration.DEFAULT_ASYNC_FETCH_SIZE; import static tap.config.TAPConfiguration.DEFAULT_DIRECTORY_PER_USER; import static tap.config.TAPConfiguration.DEFAULT_EXECUTION_DURATION; import static tap.config.TAPConfiguration.DEFAULT_GROUP_USER_DIRECTORIES; +import static tap.config.TAPConfiguration.DEFAULT_LOGGER; import static tap.config.TAPConfiguration.DEFAULT_MAX_ASYNC_JOBS; import static tap.config.TAPConfiguration.DEFAULT_RETENTION_PERIOD; import static tap.config.TAPConfiguration.DEFAULT_SYNC_FETCH_SIZE; @@ -19,6 +39,7 @@ import static tap.config.TAPConfiguration.KEY_FILE_MANAGER; import static tap.config.TAPConfiguration.KEY_FILE_ROOT_PATH; import static tap.config.TAPConfiguration.KEY_GEOMETRIES; import static tap.config.TAPConfiguration.KEY_GROUP_USER_DIRECTORIES; +import static tap.config.TAPConfiguration.KEY_LOGGER; import static tap.config.TAPConfiguration.KEY_LOG_ROTATION; import static tap.config.TAPConfiguration.KEY_MAX_ASYNC_JOBS; import static tap.config.TAPConfiguration.KEY_MAX_EXECUTION_DURATION; @@ -69,25 +90,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Properties; -/* - * This file is part of TAPLibrary. - * - * TAPLibrary is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * TAPLibrary is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. - * - * Copyright 2016 - Astronomisches Rechen Institut (ARI) - */ - import adql.db.FunctionDef; import adql.db.STCS; import adql.parser.ParseException; @@ -125,7 +127,7 @@ import uws.service.log.UWSLog.LogLevel; * </p> * * @author Grégory Mantelet (ARI) - * @version 2.1 (09/2016) + * @version 2.1 (09/2017) * @since 2.0 */ public final class ConfigurableServiceConnection implements ServiceConnection { @@ -354,15 +356,22 @@ public final class ConfigurableServiceConnection implements ServiceConnection { * Initialize the TAP logger with the given TAP configuration file. * * @param tapConfig The content of the TAP configuration file. + * + * @throws TAPException If no instance of the specified custom logger can + * be created. */ - private void initLogger(final Properties tapConfig){ + private void initLogger(final Properties tapConfig) throws TAPException{ // Create the logger: - logger = new DefaultTAPLog(fileManager); + String propValue = getProperty(tapConfig, KEY_LOGGER); + if (propValue == null || propValue.trim().equalsIgnoreCase(DEFAULT_LOGGER)) + logger = new DefaultTAPLog(fileManager); + else + logger = newInstance(propValue, KEY_LOGGER, TAPLog.class, new Class<?>[]{UWSFileManager.class}, new Object[]{fileManager}); StringBuffer buf = new StringBuffer("Logger initialized"); // Set the minimum log level: - String propValue = getProperty(tapConfig, KEY_MIN_LOG_LEVEL); + propValue = getProperty(tapConfig, KEY_MIN_LOG_LEVEL); if (propValue != null){ try{ ((DefaultTAPLog)logger).setMinLogLevel(LogLevel.valueOf(propValue.toUpperCase())); diff --git a/src/tap/config/TAPConfiguration.java b/src/tap/config/TAPConfiguration.java index 00363ce8384d5dece0fdd9841811ee2525c63837..1f1b260c1acff32d0605c77757353d3c48e42e12 100644 --- a/src/tap/config/TAPConfiguration.java +++ b/src/tap/config/TAPConfiguration.java @@ -36,7 +36,7 @@ import tap.backup.DefaultTAPBackupManager; * and it must be used only thanks to its static classes and attributes.</i></p> * * @author Grégory Mantelet (ARI) - * @version 2.1 (08/2017) + * @version 2.1 (09/2017) * @since 2.0 */ public final class TAPConfiguration { @@ -80,6 +80,11 @@ public final class TAPConfiguration { public final static int DEFAULT_RETENTION_PERIOD = 0; /* LOG KEYS */ + /** Name/Key of the property specifying the logger to use. + * By default, {@link tap.log.DefaultTAPLog} is used. */ + public final static String KEY_LOGGER = "logger"; + /** Default value of the property {@link #KEY_LOGGER}: {@value #DEFAULT_LOGGER}. */ + public final static String DEFAULT_LOGGER = "default"; /** Name/Key of the property specifying the minimum type of messages (i.e. DEBUG, INFO, WARNING, ERROR, FATAL) * that must be logged. By default all messages are logged...which is equivalent to set this property to "DEBUG". */ public final static String KEY_MIN_LOG_LEVEL = "min_log_level"; diff --git a/src/tap/config/tap_configuration_file.html b/src/tap/config/tap_configuration_file.html index 1e7c61c680b84d1da2999cf2f9dd412f55750205..60d1015620023891d672dad7e6ed35bd2f125526 100644 --- a/src/tap/config/tap_configuration_file.html +++ b/src/tap/config/tap_configuration_file.html @@ -400,13 +400,37 @@ </tr> <tr><td colspan="5">Log files</td></tr> + <tr class="optional"> + <td class="done">logger</td> + <td></td> + <td>text</td> + <td> + <p>Only two possibilities are already implemented.</p> + <ul> + <li><b>default</b>: default logger provided by the + library. Any logged message will be appended in the + file 'service.log' inside the root directory of this + service (cf property <code>file_root_path</code>). + </li> + <li><b><em>{...}</em></b>: a custom logger. A class name MUST be + provided (between {...}). The specified class must + reference an implementation of tap.log.TAPLog. This + implementation must have at least one constructor + with a single parameter of type + uws.service.file.UWSFileManager. + </li> + </ul> + <p><em>Default: <code>default</code> (i.e. tap.log.DefaultTAPLog)</em></p> + </td> + <td><ul><li>default</li><li>{aPackage.MyLogger}</li></ul></td> + </tr> <tr class="optional"> <td class="done">min_log_level</td> <td></td> <td>text</td> <td> <p>Minimum level that a message must have in order to be logged.</p> - <p>5 possible values:</p>p> + <p>5 possible values:</p> <ul> <li><b>DEBUG</b>: every messages are logged.</li> <li><b>INFO</b>: every messages EXCEPT DEBUG are logged.</li> diff --git a/src/tap/config/tap_full.properties b/src/tap/config/tap_full.properties index 0784203111868315d4f33e45d156127b0c2e4a30..b900cc323c884ed4201b1883c0416698451c714a 100644 --- a/src/tap/config/tap_full.properties +++ b/src/tap/config/tap_full.properties @@ -248,6 +248,22 @@ max_retention_period = 0 # LOG FILES # ############# +# [OPTIONAL] +# Logging method to use. +# +# Only two possibilities are already implemented. +# * default: default logger provided by the library. Any logged message +# will be appended in the file 'service.log' inside the root +# directory of this service (cf property 'file_root_path'). +# * {...}: a custom logger. A class name MUST be provided +# (between {...}). The specified class must reference +# an implementation of tap.log.TAPLog. This implementation +# must have at least one constructor with a single parameter of +# type uws.service.file.UWSFileManager. +# +# Default: 'default' (i.e. tap.log.DefaultTAPLog) +logger = + # [OPTIONAL] # Minimum level that a message must have in order to be logged. # diff --git a/src/uws/config/ConfigurableUWSServlet.java b/src/uws/config/ConfigurableUWSServlet.java index b31761218a54a53c81c6e15ee754bbed438efa6a..d44923669fe1baa31e761411395df2ec80d66e96 100644 --- a/src/uws/config/ConfigurableUWSServlet.java +++ b/src/uws/config/ConfigurableUWSServlet.java @@ -16,13 +16,14 @@ package uws.config; * You should have received a copy of the GNU Lesser General Public License * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2016 - Astronomisches Rechen Institut (ARI) + * Copyright 2016-2017 - Astronomisches Rechen Institut (ARI) */ import static uws.config.UWSConfiguration.DEFAULT_BACKUP_BY_USER; import static uws.config.UWSConfiguration.DEFAULT_BACKUP_FREQUENCY; import static uws.config.UWSConfiguration.DEFAULT_DIRECTORY_PER_USER; import static uws.config.UWSConfiguration.DEFAULT_GROUP_USER_DIRECTORIES; +import static uws.config.UWSConfiguration.DEFAULT_LOGGER; import static uws.config.UWSConfiguration.DEFAULT_UWS_CONF_FILE; import static uws.config.UWSConfiguration.KEY_ADD_SERIALIZERS; import static uws.config.UWSConfiguration.KEY_ADD_UWS_ACTIONS; @@ -38,6 +39,7 @@ import static uws.config.UWSConfiguration.KEY_GROUP_USER_DIRECTORIES; import static uws.config.UWSConfiguration.KEY_HOME_PAGE; import static uws.config.UWSConfiguration.KEY_HOME_PAGE_MIME_TYPE; import static uws.config.UWSConfiguration.KEY_JOB_LISTS; +import static uws.config.UWSConfiguration.KEY_LOGGER; import static uws.config.UWSConfiguration.KEY_LOG_ROTATION; import static uws.config.UWSConfiguration.KEY_MAX_RUNNING_JOBS; import static uws.config.UWSConfiguration.KEY_MIN_LOG_LEVEL; @@ -103,7 +105,7 @@ import uws.service.log.UWSLog.LogLevel; * </p> * * @author Grégory Mantelet (ARI) - * @version 4.2 (06/2016) + * @version 4.2 (09/2017) * @since 4.2 */ public class ConfigurableUWSServlet extends HttpServlet { @@ -292,15 +294,23 @@ public class ConfigurableUWSServlet extends HttpServlet { * * @param uwsConfig The content of the UWS configuration file. * @param fileManager The file manager to access the log file(s). + * + * @throws UWSException If no instance of the specified custom logger can + * be created. */ - private UWSLog createLogger(final Properties uwsConfig, final UWSFileManager fileManager){ + private UWSLog createLogger(final Properties uwsConfig, final UWSFileManager fileManager) throws UWSException{ // Create the logger: - UWSLog logger = new DefaultUWSLog(fileManager); + UWSLog logger; + String propValue = getProperty(uwsConfig, KEY_LOGGER); + if (propValue == null || propValue.trim().equalsIgnoreCase(DEFAULT_LOGGER)) + logger = new DefaultUWSLog(fileManager); + else + logger = newInstance(propValue, KEY_LOGGER, UWSLog.class, new Class<?>[]{UWSFileManager.class}, new Object[]{fileManager}); StringBuffer buf = new StringBuffer("Logger initialized"); // Set the minimum log level: - String propValue = getProperty(uwsConfig, KEY_MIN_LOG_LEVEL); + propValue = getProperty(uwsConfig, KEY_MIN_LOG_LEVEL); if (propValue != null){ try{ ((DefaultUWSLog)logger).setMinLogLevel(LogLevel.valueOf(propValue.toUpperCase())); diff --git a/src/uws/config/UWSConfiguration.java b/src/uws/config/UWSConfiguration.java index 41124202a681235179ef1b1fcf217f3cb4883500..5814445049f0134607d15abade15e9a4e91fd2da 100644 --- a/src/uws/config/UWSConfiguration.java +++ b/src/uws/config/UWSConfiguration.java @@ -16,7 +16,7 @@ package uws.config; * You should have received a copy of the GNU Lesser General Public License * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2016 - Astronomisches Rechen Institut (ARI) + * Copyright 2016-2017 - Astronomisches Rechen Institut (ARI) */ import java.lang.reflect.Constructor; @@ -39,7 +39,7 @@ import uws.service.request.UWSRequestParser; * and it must be used only thanks to its static classes and attributes.</i></p> * * @author Grégory Mantelet (ARI) - * @version 4.2 (06/2016) + * @version 4.2 (09/2017) * @since 4.2 */ public final class UWSConfiguration { @@ -178,6 +178,11 @@ public final class UWSConfiguration { /* LOG KEYS */ + /** Name/Key of the property specifying the logger to use. + * By default, {@link uws.service.log.DefaultUWSLog} is used. */ + public final static String KEY_LOGGER = "logger"; + /** Default value of the property {@link #KEY_LOGGER}: {@value #DEFAULT_LOGGER}. */ + public final static String DEFAULT_LOGGER = "default"; /** Name/Key of the property specifying the minimum type of messages (i.e. DEBUG, INFO, WARNING, ERROR, FATAL) * that must be logged. By default all messages are logged...which is equivalent to set this property to "DEBUG". */ public final static String KEY_MIN_LOG_LEVEL = "min_log_level"; diff --git a/src/uws/config/uws_configuration_file.html b/src/uws/config/uws_configuration_file.html index 3882d943455aaa65ec2daf71fcd4e79e4821eca8..56f7f6c69c583bf1faef4f98a1c6ad3be8be0b50 100644 --- a/src/uws/config/uws_configuration_file.html +++ b/src/uws/config/uws_configuration_file.html @@ -419,6 +419,30 @@ </tr> <tr><td colspan="5">Log files</td></tr> + <tr class="optional"> + <td class="todo">logger</td> + <td></td> + <td>text</td> + <td> + <p>Only two possibilities are already implemented.</p> + <ul> + <li><b>default</b>: default logger provided by the + library. Any logged message will be appended in the + file 'service.log' inside the root directory of this + service (cf property <code>file_root_path</code>). + </li> + <li><b><em>{...}</em></b>: a custom logger. A class name + MUST be provided (between {...}). The specified + class must reference an implementation of + uws.service.log.UWSLog. This implementation must + have at least one constructor with a single + parameter of type uws.service.file.UWSFileManager. + </li> + </ul> + <p><em>Default: <code>default</code> (i.e. uws.service.log.DefaultUWSLog)</em></p> + </td> + <td><ul><li>default</li><li>{aPackage.MyLogger}</li></ul></td> + </tr> <tr class="optional"> <td class="todo">min_log_level</td> <td></td> diff --git a/src/uws/config/uws_full.properties b/src/uws/config/uws_full.properties index c4f2c76cdf98fca62c2d260bf5011893366493d0..db107e78fc3bfa10f5161fd1cf7111088246cc53 100644 --- a/src/uws/config/uws_full.properties +++ b/src/uws/config/uws_full.properties @@ -327,6 +327,22 @@ group_user_dir = false # LOG FILES # ############# +# [OPTIONAL] +# Logging method to use. +# +# Only two possibilities are already implemented. +# * default: default logger provided by the library. Any logged message +# will be appended in the file 'service.log' inside the root +# directory of this service (cf property 'file_root_path'). +# * {...}: a custom logger. A class name must be provided +# (between {...}). The specified class must reference +# an implementation of uws.service.log.UWSLog. This implementation +# must have at least one constructor with a single parameter of +# type uws.service.file.UWSFileManager. +# +# Default: 'default' (i.e. uws.service.log.DefaultUWSLog) +logger = + # [OPTIONAL] # Minimum level that a message must have in order to be logged. # @@ -360,8 +376,11 @@ min_log_level = # pay attention at the case when using the frequency types 'M' # (monthly) and 'm' (every minute). # -# Note: this property is ignored if the file manager is not any more an -# extension of uws.service.file.LocalUWSFileManager. +# Note 1: this property is ignored if the file manager is not any more an +# extension of uws.service.file.LocalUWSFileManager. +# +# Note 2: this property may be ignored if the specified logger is not the +# default one (cf property 'logger'). # # Default: D 0 0 (daily at midnight) log_rotation = diff --git a/test/tap/config/TestConfigurableServiceConnection.java b/test/tap/config/TestConfigurableServiceConnection.java index 38ddbd5d575a1643e45834619064a4e9b24ca7ad..e0e3be40a32aff8d84f8c6d42067270b7ab1fc45 100644 --- a/test/tap/config/TestConfigurableServiceConnection.java +++ b/test/tap/config/TestConfigurableServiceConnection.java @@ -7,6 +7,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static tap.config.TAPConfiguration.DEFAULT_ASYNC_FETCH_SIZE; +import static tap.config.TAPConfiguration.DEFAULT_LOGGER; import static tap.config.TAPConfiguration.DEFAULT_MAX_ASYNC_JOBS; import static tap.config.TAPConfiguration.DEFAULT_SYNC_FETCH_SIZE; import static tap.config.TAPConfiguration.KEY_ASYNC_FETCH_SIZE; @@ -14,6 +15,7 @@ import static tap.config.TAPConfiguration.KEY_COORD_SYS; import static tap.config.TAPConfiguration.KEY_DEFAULT_OUTPUT_LIMIT; import static tap.config.TAPConfiguration.KEY_FILE_MANAGER; import static tap.config.TAPConfiguration.KEY_GEOMETRIES; +import static tap.config.TAPConfiguration.KEY_LOGGER; import static tap.config.TAPConfiguration.KEY_LOG_ROTATION; import static tap.config.TAPConfiguration.KEY_MAX_ASYNC_JOBS; import static tap.config.TAPConfiguration.KEY_MAX_OUTPUT_LIMIT; @@ -67,6 +69,7 @@ import tap.db.JDBCConnection; import tap.db_testtools.DBTools; import tap.formatter.OutputFormat; import tap.formatter.VOTableFormat; +import tap.log.DefaultTAPLog; import tap.metadata.TAPMetadata; import tap.metadata.TAPSchema; import uk.ac.starlink.votable.DataFormat; @@ -77,6 +80,7 @@ import uws.job.user.JobOwner; import uws.service.UWSUrl; import uws.service.UserIdentifier; import uws.service.file.LocalUWSFileManager; +import uws.service.file.UWSFileManager; import uws.service.log.DefaultUWSLog; import uws.service.log.UWSLog.LogLevel; @@ -86,7 +90,8 @@ public class TestConfigurableServiceConnection { private static Properties validProp, noFmProp, fmClassNameProp, incorrectFmProp, correctLogProp, incorrectLogLevelProp, - incorrectLogRotationProp, xmlMetaProp, + incorrectLogRotationProp, customLoggerProp, + explicitDefaultLoggerProp, xmlMetaProp, xmlMetaPropWithCustomMetaClass, xmlMetaPropWithBadCustomMetaClass, xmlMetaPropWithANonMetaClass, wrongManualMetaProp, missingMetaProp, missingMetaFileProp, wrongMetaProp, wrongMetaFileProp, @@ -139,6 +144,12 @@ public class TestConfigurableServiceConnection { incorrectLogRotationProp = (Properties)validProp.clone(); incorrectLogRotationProp.setProperty(KEY_LOG_ROTATION, "foo"); + customLoggerProp = (Properties)validProp.clone(); + customLoggerProp.setProperty(KEY_LOGGER, "{tap.config.TestConfigurableServiceConnection$CustomLogger}"); + + explicitDefaultLoggerProp = (Properties)validProp.clone(); + explicitDefaultLoggerProp.setProperty(KEY_LOGGER, DEFAULT_LOGGER); + xmlMetaProp = (Properties)validProp.clone(); xmlMetaProp.setProperty(KEY_METADATA, VALUE_XML); xmlMetaProp.setProperty(KEY_METADATA_FILE, XML_FILE); @@ -543,6 +554,24 @@ public class TestConfigurableServiceConnection { fail("This MUST have succeeded because the provided log level and log rotation are valid! \nCaught exception: " + getPertinentMessage(e)); } + // Explicit default logger: + try{ + ServiceConnection connection = new ConfigurableServiceConnection(explicitDefaultLoggerProp); + assertNotNull(connection.getFileManager()); + assertEquals(DefaultTAPLog.class, connection.getLogger().getClass()); + }catch(Exception e){ + fail("This MUST have succeeded because the value 'default' should be supported and should set the default TAP logger! \nCaught exception: " + getPertinentMessage(e)); + } + + // Custom logger: + try{ + ServiceConnection connection = new ConfigurableServiceConnection(customLoggerProp); + assertNotNull(connection.getFileManager()); + assertEquals(CustomLogger.class, connection.getLogger().getClass()); + }catch(Exception e){ + fail("This MUST have succeeded because the specified class implements TAPLog and has a constructor with a single parameter of type UWSFileManager! \nCaught exception: " + getPertinentMessage(e)); + } + // Incorrect log level: try{ ServiceConnection connection = new ConfigurableServiceConnection(incorrectLogLevelProp); @@ -1324,4 +1353,27 @@ public class TestConfigurableServiceConnection { } } + /** + * Custom TAPLog implementation. + * + * <p><i> + * Actually, for quick implementation, this class just extends + * {@link DefaultTAPLog} (and so, implements TAPLog). + * </i></p> + * + * @author Grégory Mantelet (ARI) + * @version 09/2017 + */ + private static class CustomLogger extends DefaultTAPLog { + public CustomLogger(final UWSFileManager fm){ + super(fm); + } + + @Override + public void logTAP(LogLevel level, Object obj, String event, String message, Throwable error){ + super.logTAP(level, obj, event, "[MY] " + message, error); + } + + } + }