diff --git a/src/tap/config/ConfigurableServiceConnection.java b/src/tap/config/ConfigurableServiceConnection.java index b60fd099ed5e47e56afa61b471bbb60215aca19a..f7e7c48955b2b2e6033f33c1d54743482d16f24f 100644 --- a/src/tap/config/ConfigurableServiceConnection.java +++ b/src/tap/config/ConfigurableServiceConnection.java @@ -15,6 +15,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_LOG_ROTATION; import static tap.config.TAPConfiguration.KEY_MAX_ASYNC_JOBS; import static tap.config.TAPConfiguration.KEY_MAX_EXECUTION_DURATION; import static tap.config.TAPConfiguration.KEY_MAX_OUTPUT_LIMIT; @@ -22,6 +23,7 @@ import static tap.config.TAPConfiguration.KEY_MAX_RETENTION_PERIOD; import static tap.config.TAPConfiguration.KEY_MAX_UPLOAD_LIMIT; import static tap.config.TAPConfiguration.KEY_METADATA; import static tap.config.TAPConfiguration.KEY_METADATA_FILE; +import static tap.config.TAPConfiguration.KEY_MIN_LOG_LEVEL; import static tap.config.TAPConfiguration.KEY_OUTPUT_FORMATS; import static tap.config.TAPConfiguration.KEY_PROVIDER_NAME; import static tap.config.TAPConfiguration.KEY_SERVICE_DESCRIPTION; @@ -77,6 +79,7 @@ import uws.UWSException; import uws.service.UserIdentifier; import uws.service.file.LocalUWSFileManager; import uws.service.file.UWSFileManager; +import uws.service.log.UWSLog.LogLevel; import adql.db.FunctionDef; import adql.parser.ParseException; import adql.query.operand.function.UserDefinedFunction; @@ -124,7 +127,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection { initFileManager(tapConfig); // 2. CREATE THE LOGGER: - logger = new DefaultTAPLog(fileManager); + initLogger(tapConfig); // 3. BUILD THE TAP FACTORY: tapFactory = new ConfigurableTAPFactory(this, tapConfig); @@ -211,6 +214,34 @@ public final class ConfigurableServiceConnection implements ServiceConnection { } } + private void initLogger(final Properties tapConfig){ + // Create the logger: + logger = new DefaultTAPLog(fileManager); + + StringBuffer buf = new StringBuffer("Logger initialized"); + + // Set the minimum log level: + String propValue = getProperty(tapConfig, KEY_MIN_LOG_LEVEL); + if (propValue != null){ + try{ + ((DefaultTAPLog)logger).setMinLogLevel(LogLevel.valueOf(propValue.toUpperCase())); + }catch(IllegalArgumentException iae){} + } + buf.append(" (minimum log level: ").append(((DefaultTAPLog)logger).getMinLogLevel()); + + // Set the log rotation period, if any: + if (fileManager instanceof LocalUWSFileManager){ + propValue = getProperty(tapConfig, KEY_LOG_ROTATION); + if (propValue != null) + ((LocalUWSFileManager)fileManager).setLogRotationFreq(propValue); + buf.append(", log rotation: ").append(((LocalUWSFileManager)fileManager).getLogRotationFreq()); + } + + // Log the successful initialization with set parameters: + buf.append(")."); + logger.info(buf.toString()); + } + private TAPMetadata initMetadata(final Properties tapConfig) throws TAPException{ // Get the fetching method to use: String metaFetchType = getProperty(tapConfig, KEY_METADATA); diff --git a/src/tap/config/TAPConfiguration.java b/src/tap/config/TAPConfiguration.java index b41f30736de5dc176ad9bfb314a61a5131cd0c19..3a6976218926e5438857dd9e07c8511497f7e4b4 100644 --- a/src/tap/config/TAPConfiguration.java +++ b/src/tap/config/TAPConfiguration.java @@ -28,6 +28,10 @@ public final class TAPConfiguration { public final static String KEY_MAX_RETENTION_PERIOD = "max_retention_period"; public final static int DEFAULT_RETENTION_PERIOD = 0; + /* LOG KEYS */ + public final static String KEY_MIN_LOG_LEVEL = "min_log_level"; + public final static String KEY_LOG_ROTATION = "log_rotation"; + /* UWS BACKUP */ public final static String KEY_BACKUP_FREQUENCY = "backup_frequency"; public final static String VALUE_USER_ACTION = "user_action"; diff --git a/src/tap/config/tap_configuration_file.html b/src/tap/config/tap_configuration_file.html index f16f3ce730d657b2784f7f9f960fda1ce29fb49e..08879eedd78e683b74111095f484951996d6e181 100644 --- a/src/tap/config/tap_configuration_file.html +++ b/src/tap/config/tap_configuration_file.html @@ -327,6 +327,51 @@ <td>604800 <em>(1 week)</em></td> </tr> + <tr><td colspan="5">Log files</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> + <ul> + <li><b>DEBUG</b>: every messages are logged.</li> + <li><b>INFO</b>: every messages EXCEPT DEBUG are logged.</li> + <li><b>WARNING</b>: every messages EXCEPT DEBUG and INFO are logged.</li> + <li><b>ERROR</b>: only ERROR and FATAL messages are logged.</li> + <li><b>FATAL</b>: only FATAL messages are logged.</li> + </ul> + <p><em>Default: <code>DEBUG</code> (every messages are logged)</em></p> + </td> + <td><ul><li>DEBUG</li><li>INFO</li><li>WANRING</li><li>ERROR</li><li>FATAL</li></ul></td> + </tr> + <tr class="optional"> + <td class="done">log_rotation</td> + <td></td> + <td>text</td> + <td> + <p>Frequency of the log file rotation. That's to say, logs will be written in a new file after this period. This avoid having too big log files. + Old log files are renamed so that highlighting its logging period.</p> + <p>The frequency string must respect the following syntax:</p> + <ul> + <li><b>'D' hh mm</b>: daily schedule at hh:mm</li> + <li><b>'W' dd hh mm</b>: weekly schedule at the given day of the week (1:sunday, 2:monday, ..., 7:saturday) at hh:mm</li> + <li><b>'M' dd hh mm</b>: monthly schedule at the given day of the month at hh:mm</li> + <li><b>'h' mm</b>: hourly schedule at the given minute</li> + <li><b>'m'</b>: scheduled every minute (for completness :-))</li> + </ul> + <p><em>Where: hh = integer between 0 and 23, mm = integer between 0 and 59, dd (for 'W') = integer between 1 and 7 (1:sunday, 2:monday, ..., 7:saturday), + dd (for 'M') = integer between 1 and 31.</em></p> + <p><em><b>Warning:</b> + The frequency type is case sensitive! Then you should particularly pay attention at the case + when using the frequency types 'M' (monthly) and 'm' (every minute). + </em></p> + <p><em>Default: <code>D 0 0</code> (daily at midnight)</em></p> + </td> + <td><ul><li>D 6 30</li><li>W 2 6 30</li><li>M 2 6 30</li><li>H 10</li><li>m</li></ul></td> + </tr> + <tr><td colspan="5">UWS Backup</td></tr> <tr class="optional"> <td class="done">backup_frequency</td> diff --git a/src/tap/config/tap_full.properties b/src/tap/config/tap_full.properties index b54bb54c4521fb491f23cb516415a339805d2608..c8e4b14d1e4970b0a734924af0035938e9459a16 100644 --- a/src/tap/config/tap_full.properties +++ b/src/tap/config/tap_full.properties @@ -2,7 +2,7 @@ # FULL TAP CONFIGURATION FILE # # # # TAP Version: 2.0 # -# Date: 17 Feb. 2015 # +# Date: 18 Feb. 2015 # # Author: Gregory Mantelet (ARI) # # # ########################################################## @@ -191,6 +191,42 @@ default_retention_period = 0 # Default: 0 (results kept forever). max_retention_period = 0 +############# +# LOG FILES # +############# + +# [OPTIONAL] +# Minimum level that a message must have in order to be logged. +# +# 5 possible values: +# * DEBUG: every messages are logged. +# * INFO: every messages EXCEPT DEBUG are logged. +# * WARNING: every messages EXCEPT DEBUG and INFO are logged. +# * ERROR: only ERROR and FATAL messages are logged. +# * FATAL: only FATAL messages are logged. +# +# Default: DEBUG (every messages are logged) +min_log_level = + +# [OPTIONAL] +# Frequency of the log file rotation. That's to say, logs will be written in a new file after this period. This avoid having too big log files. +# Old log files are renamed so that highlighting its logging period. +# +# The frequency string must respect the following syntax: +# 'D' hh mm: daily schedule at hh:mm +# 'W' dd hh mm: weekly schedule at the given day of the week (1:sunday, 2:monday, ..., 7:saturday) at hh:mm +# 'M' dd hh mm: monthly schedule at the given day of the month at hh:mm +# 'h' mm: hourly schedule at the given minute +# 'm': scheduled every minute (for completness :-)) +# Where: hh = integer between 0 and 23, mm = integer between 0 and 59, dd (for 'W') = integer between 1 and 7 (1:sunday, 2:monday, ..., 7:saturday), dd (for 'M') = integer between 1 and 31. +# +# Warning: The frequency type is case sensitive! Then you should particularly 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. +# +# Default: D 0 0 (daily at midnight) +log_rotation = + ############## # UWS_BACKUP # ############## diff --git a/src/tap/log/DefaultTAPLog.java b/src/tap/log/DefaultTAPLog.java index f8c938da5985a90e2d1d43310ae68431ab24c80d..789bdf3032f47333f4feb0f3a5c7bc327f4ec36e 100644 --- a/src/tap/log/DefaultTAPLog.java +++ b/src/tap/log/DefaultTAPLog.java @@ -16,7 +16,7 @@ package tap.log; * 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 2012,2014 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), + * Copyright 2012-2015 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ @@ -37,7 +37,7 @@ import uws.service.log.DefaultUWSLog; * Default implementation of the {@link TAPLog} interface which lets logging any message about a TAP service. * * @author Grégory Mantelet (CDS;ARI) - * @version 2.0 (12/2014) + * @version 2.0 (02/2015) * * @see DefaultUWSLog */ @@ -117,7 +117,15 @@ public class DefaultTAPLog extends DefaultUWSLog implements TAPLog { } @Override - public void logDB(final LogLevel level, final DBConnection connection, final String event, final String message, final Throwable error){ + public void logDB(LogLevel level, final DBConnection connection, final String event, final String message, final Throwable error){ + // If the type is missing: + if (level == null) + level = (error != null) ? LogLevel.ERROR : LogLevel.INFO; + + // Log or not? + if (!canLog(level)) + return; + // log the main given error: log(level, "DB", event, (connection != null ? connection.getID() : null), message, error); @@ -133,7 +141,15 @@ public class DefaultTAPLog extends DefaultUWSLog implements TAPLog { } @Override - public void logTAP(final LogLevel level, final Object obj, final String event, final String message, final Throwable error){ + public void logTAP(LogLevel level, final Object obj, final String event, final String message, final Throwable error){ + // If the type is missing: + if (level == null) + level = (error != null) ? LogLevel.ERROR : LogLevel.INFO; + + // Log or not? + if (!canLog(level)) + return; + // Get more information (when known event and available object): String jobId = null, msgAppend = null; try{ diff --git a/test/tap/config/TestConfigurableServiceConnection.java b/test/tap/config/TestConfigurableServiceConnection.java index 398b444e7d9d92f9aeaa970c6dc61e37b53ac4c2..a7534843dc44073b244ca77a77e8e93fa3ac1909 100644 --- a/test/tap/config/TestConfigurableServiceConnection.java +++ b/test/tap/config/TestConfigurableServiceConnection.java @@ -10,10 +10,12 @@ import static tap.config.TAPConfiguration.DEFAULT_MAX_ASYNC_JOBS; 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_LOG_ROTATION; import static tap.config.TAPConfiguration.KEY_MAX_ASYNC_JOBS; import static tap.config.TAPConfiguration.KEY_MAX_OUTPUT_LIMIT; import static tap.config.TAPConfiguration.KEY_METADATA; import static tap.config.TAPConfiguration.KEY_METADATA_FILE; +import static tap.config.TAPConfiguration.KEY_MIN_LOG_LEVEL; import static tap.config.TAPConfiguration.KEY_OUTPUT_FORMATS; import static tap.config.TAPConfiguration.KEY_UDFS; import static tap.config.TAPConfiguration.KEY_USER_IDENTIFIER; @@ -55,6 +57,8 @@ import uws.job.user.JobOwner; import uws.service.UWSUrl; import uws.service.UserIdentifier; import uws.service.file.LocalUWSFileManager; +import uws.service.log.DefaultUWSLog; +import uws.service.log.UWSLog.LogLevel; import adql.db.FunctionDef; import adql.db.TestDBChecker.UDFToto; @@ -63,21 +67,22 @@ public class TestConfigurableServiceConnection { private final static String XML_FILE = "test/tap/config/tables.xml"; private static Properties validProp, noFmProp, fmClassPathProp, - incorrectFmProp, xmlMetaProp, missingMetaProp, missingMetaFileProp, - wrongMetaProp, wrongMetaFileProp, validFormatsProp, - validVOTableFormatsProp, badSVFormat1Prop, badSVFormat2Prop, - badVotFormat1Prop, badVotFormat2Prop, badVotFormat3Prop, - badVotFormat4Prop, badVotFormat5Prop, badVotFormat6Prop, - unknownFormatProp, maxAsyncProp, negativeMaxAsyncProp, - notIntMaxAsyncProp, defaultOutputLimitProp, maxOutputLimitProp, - bothOutputLimitGoodProp, bothOutputLimitBadProp, userIdentProp, - notClassPathUserIdentProp, geometriesProp, noneGeomProp, - anyGeomProp, noneInsideGeomProp, unknownGeomProp, anyUdfsProp, - noneUdfsProp, udfsProp, udfsWithClassPathProp, - udfsListWithNONEorANYProp, udfsWithWrongParamLengthProp, - udfsWithMissingBracketsProp, udfsWithMissingDefProp1, - udfsWithMissingDefProp2, emptyUdfItemProp1, emptyUdfItemProp2, - udfWithMissingEndBracketProp; + incorrectFmProp, correctLogProp, incorrectLogLevelProp, + incorrectLogRotationProp, xmlMetaProp, missingMetaProp, + missingMetaFileProp, wrongMetaProp, wrongMetaFileProp, + validFormatsProp, validVOTableFormatsProp, badSVFormat1Prop, + badSVFormat2Prop, badVotFormat1Prop, badVotFormat2Prop, + badVotFormat3Prop, badVotFormat4Prop, badVotFormat5Prop, + badVotFormat6Prop, unknownFormatProp, maxAsyncProp, + negativeMaxAsyncProp, notIntMaxAsyncProp, defaultOutputLimitProp, + maxOutputLimitProp, bothOutputLimitGoodProp, + bothOutputLimitBadProp, userIdentProp, notClassPathUserIdentProp, + geometriesProp, noneGeomProp, anyGeomProp, noneInsideGeomProp, + unknownGeomProp, anyUdfsProp, noneUdfsProp, udfsProp, + udfsWithClassPathProp, udfsListWithNONEorANYProp, + udfsWithWrongParamLengthProp, udfsWithMissingBracketsProp, + udfsWithMissingDefProp1, udfsWithMissingDefProp2, + emptyUdfItemProp1, emptyUdfItemProp2, udfWithMissingEndBracketProp; @BeforeClass public static void setUp() throws Exception{ @@ -93,6 +98,16 @@ public class TestConfigurableServiceConnection { incorrectFmProp = (Properties)validProp.clone(); incorrectFmProp.setProperty(KEY_FILE_MANAGER, "foo"); + correctLogProp = (Properties)validProp.clone(); + correctLogProp.setProperty(KEY_LOG_ROTATION, " M 5 6 03 "); + correctLogProp.setProperty(KEY_MIN_LOG_LEVEL, " WARNing "); + + incorrectLogLevelProp = (Properties)validProp.clone(); + incorrectLogLevelProp.setProperty(KEY_MIN_LOG_LEVEL, "foo"); + + incorrectLogRotationProp = (Properties)validProp.clone(); + incorrectLogRotationProp.setProperty(KEY_LOG_ROTATION, "foo"); + xmlMetaProp = (Properties)validProp.clone(); xmlMetaProp.setProperty(KEY_METADATA, VALUE_XML); xmlMetaProp.setProperty(KEY_METADATA_FILE, XML_FILE); @@ -254,7 +269,9 @@ public class TestConfigurableServiceConnection { // tests: assertNotNull(connection.getLogger()); + assertEquals(LogLevel.DEBUG, ((DefaultUWSLog)connection.getLogger()).getMinLogLevel()); assertNotNull(connection.getFileManager()); + assertEquals("daily at 00:00", ((LocalUWSFileManager)connection.getFileManager()).getLogRotationFreq()); assertNotNull(connection.getFactory()); assertNotNull(connection.getTAPMetadata()); assertTrue(connection.getTAPMetadata().getNbSchemas() >= 1); @@ -284,7 +301,9 @@ public class TestConfigurableServiceConnection { try{ ServiceConnection connection = new ConfigurableServiceConnection(xmlMetaProp); assertNotNull(connection.getLogger()); + assertEquals(LogLevel.DEBUG, ((DefaultUWSLog)connection.getLogger()).getMinLogLevel()); assertNotNull(connection.getFileManager()); + assertEquals("daily at 00:00", ((LocalUWSFileManager)connection.getFileManager()).getLogRotationFreq()); assertNotNull(connection.getFactory()); assertNotNull(connection.getTAPMetadata()); assertEquals(nbSchemas, connection.getTAPMetadata().getNbSchemas()); @@ -349,7 +368,9 @@ public class TestConfigurableServiceConnection { try{ ServiceConnection connection = new ConfigurableServiceConnection(fmClassPathProp); assertNotNull(connection.getLogger()); + assertEquals(LogLevel.DEBUG, ((DefaultUWSLog)connection.getLogger()).getMinLogLevel()); assertNotNull(connection.getFileManager()); + assertEquals("daily at 00:00", ((LocalUWSFileManager)connection.getFileManager()).getLogRotationFreq()); assertNotNull(connection.getFactory()); assertNotNull(connection.getTAPMetadata()); assertFalse(connection.isAvailable()); @@ -374,6 +395,35 @@ public class TestConfigurableServiceConnection { assertEquals("Unknown value for the property \"" + KEY_FILE_MANAGER + "\": \"foo\". Only two possible values: " + VALUE_LOCAL + " or a class path between {...}.", e.getMessage()); } + // Custom log level and log rotation: + try{ + ServiceConnection connection = new ConfigurableServiceConnection(correctLogProp); + assertNotNull(connection.getLogger()); + assertEquals(LogLevel.WARNING, ((DefaultUWSLog)connection.getLogger()).getMinLogLevel()); + assertNotNull(connection.getFileManager()); + assertEquals("monthly on the 5th at 06:03", ((LocalUWSFileManager)connection.getFileManager()).getLogRotationFreq()); + }catch(Exception e){ + fail("This MUST have succeeded because the provided log level and log rotation are valid! \nCaught exception: " + getPertinentMessage(e)); + } + + // Incorrect log level: + try{ + ServiceConnection connection = new ConfigurableServiceConnection(incorrectLogLevelProp); + assertNotNull(connection.getLogger()); + assertEquals(LogLevel.DEBUG, ((DefaultUWSLog)connection.getLogger()).getMinLogLevel()); + }catch(Exception e){ + fail("This MUST have succeeded because even if the provided log level is incorrect the default behavior is to not throw exception and set the default value! \nCaught exception: " + getPertinentMessage(e)); + } + + // Incorrect log rotation: + try{ + ServiceConnection connection = new ConfigurableServiceConnection(incorrectLogRotationProp); + assertNotNull(connection.getFileManager()); + assertEquals("daily at 00:00", ((LocalUWSFileManager)connection.getFileManager()).getLogRotationFreq()); + }catch(Exception e){ + fail("This MUST have succeeded because even if the provided log rotation is incorrect the default behavior is to not throw exception and set the default value! \nCaught exception: " + getPertinentMessage(e)); + } + // Valid output formats list: try{ ServiceConnection connection = new ConfigurableServiceConnection(validFormatsProp);