FELIX-903 add felix.cm.loglevel property to limit the log output
in the absence of an OSGi LogService
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@738382 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
index 95e215d..2fd6b6e 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/ConfigurationManager.java
@@ -73,9 +73,24 @@
*/
public static final String CM_CONFIG_DIR = "felix.cm.dir";
+ /**
+ * The name of the bundle context property defining the maximum log level
+ * (value is "felix.cm.loglevel"). The log level setting is only used if
+ * there is no OSGi LogService available. Otherwise this setting is ignored.
+ * <p>
+ * This value of this property is expected to be an integer number
+ * corresponding to the log level values of the OSGi LogService. That is 1
+ * for errors, 2 for warnings, 3 for informational messages and 4 for debug
+ * messages. The default value is 2, such that only warnings and errors are
+ * logged in the absence of a LogService.
+ */
+ public static final String CM_LOG_LEVEL = "felix.cm.loglevel";
+
// The name of the LogService (not using the class, which might be missing)
private static final String LOG_SERVICE_NAME = "org.osgi.service.log.LogService";
+ private static final int CM_LOG_LEVEL_DEFAULT = 2;
+
// random number generator to create configuration PIDs for factory
// configurations
private static SecureRandom numberGenerator;
@@ -125,12 +140,32 @@
// the cache of Configuration instances mapped by their PID
private Map configurations;
+ // the maximum log level when no LogService is available
+ private int logLevel = CM_LOG_LEVEL_DEFAULT;
public void start( BundleContext bundleContext )
{
// track the log service using a ServiceTracker
logTracker = new ServiceTracker( bundleContext, LOG_SERVICE_NAME , null );
logTracker.open();
+
+ // assign the log level
+ String logLevelProp = bundleContext.getProperty( CM_LOG_LEVEL );
+ if ( logLevelProp == null )
+ {
+ logLevel = CM_LOG_LEVEL_DEFAULT;
+ }
+ else
+ {
+ try
+ {
+ logLevel = Integer.parseInt( logLevelProp );
+ }
+ catch ( NumberFormatException nfe )
+ {
+ logLevel = CM_LOG_LEVEL_DEFAULT;
+ }
+ }
// set up some fields
this.bundleContext = bundleContext;
@@ -784,6 +819,7 @@
void log( int level, String message, Throwable t )
{
+ // log using the LogService if available
Object log = logTracker.getService();
if ( log != null )
{
@@ -791,30 +827,34 @@
return;
}
- String code;
- switch ( level )
+ // Otherwise only log if more serious than the configured level
+ if ( level <= logLevel )
{
- case LogService.LOG_INFO:
- code = "*INFO *";
- break;
+ String code;
+ switch ( level )
+ {
+ case LogService.LOG_INFO:
+ code = "*INFO *";
+ break;
- case LogService.LOG_WARNING:
- code = "*WARN *";
- break;
+ case LogService.LOG_WARNING:
+ code = "*WARN *";
+ break;
- case LogService.LOG_ERROR:
- code = "*ERROR*";
- break;
+ case LogService.LOG_ERROR:
+ code = "*ERROR*";
+ break;
- case LogService.LOG_DEBUG:
- default:
- code = "*DEBUG*";
- }
+ case LogService.LOG_DEBUG:
+ default:
+ code = "*DEBUG*";
+ }
- System.err.println( code + " " + message );
- if ( t != null )
- {
- t.printStackTrace( System.err );
+ System.err.println( code + " " + message );
+ if ( t != null )
+ {
+ t.printStackTrace( System.err );
+ }
}
}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/MockLogService.java b/configadmin/src/test/java/org/apache/felix/cm/MockLogService.java
new file mode 100644
index 0000000..7b8aea4
--- /dev/null
+++ b/configadmin/src/test/java/org/apache/felix/cm/MockLogService.java
@@ -0,0 +1,84 @@
+/*
+ * 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.felix.cm;
+
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+
+/**
+ * The <code>MockLogService</code> is a very simple log service, which just
+ * prints the loglevel and message to StdErr.
+ */
+public class MockLogService implements LogService
+{
+
+ public void log( int logLevel, String message )
+ {
+ System.err.print( toMessageLine( logLevel, message ) );
+ }
+
+
+ public void log( int logLevel, String message, Throwable t )
+ {
+ log( logLevel, message );
+ }
+
+
+ public void log( ServiceReference ref, int logLevel, String message )
+ {
+ log( logLevel, message );
+ }
+
+
+ public void log( ServiceReference ref, int logLevel, String message, Throwable t )
+ {
+ log( logLevel, message );
+ }
+
+
+ /**
+ * Helper method to format log level and log message exactly the same as the
+ * <code>ConfigurationManager.log()</code> does.
+ */
+ public static String toMessageLine( int level, String message )
+ {
+ String messageLine;
+ switch ( level )
+ {
+ case LogService.LOG_INFO:
+ messageLine = "*INFO *";
+ break;
+
+ case LogService.LOG_WARNING:
+ messageLine = "*WARN *";
+ break;
+
+ case LogService.LOG_ERROR:
+ messageLine = "*ERROR*";
+ break;
+
+ case LogService.LOG_DEBUG:
+ default:
+ messageLine = "*DEBUG*";
+ }
+ return messageLine + " " + message + System.getProperty( "line.separator" );
+ }
+}
diff --git a/configadmin/src/test/java/org/apache/felix/cm/impl/ConfigurationManagerTest.java b/configadmin/src/test/java/org/apache/felix/cm/impl/ConfigurationManagerTest.java
new file mode 100644
index 0000000..d438c9e
--- /dev/null
+++ b/configadmin/src/test/java/org/apache/felix/cm/impl/ConfigurationManagerTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.felix.cm.impl;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.cm.MockBundleContext;
+import org.apache.felix.cm.MockLogService;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+public class ConfigurationManagerTest extends TestCase
+{
+
+ private PrintStream replacedStdErr;
+
+ private ByteArrayOutputStream output;
+
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ replacedStdErr = System.err;
+
+ output = new ByteArrayOutputStream();
+ System.setErr( new PrintStream( output ) );
+ }
+
+
+ protected void tearDown() throws Exception
+ {
+ System.setErr( replacedStdErr );
+
+ super.tearDown();
+ }
+
+
+ public void testLogNoLogService()
+ {
+ ConfigurationManager configMgr = createConfigurationManager( null );
+
+ setLogLevel( configMgr, LogService.LOG_WARNING );
+ assertNoLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, LogService.LOG_ERROR );
+ assertNoLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ // lower than error -- no output
+ setLogLevel( configMgr, LogService.LOG_ERROR - 1 );
+ assertNoLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ // minimal log level -- no output
+ setLogLevel( configMgr, Integer.MIN_VALUE );
+ assertNoLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertNoLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, LogService.LOG_INFO );
+ assertNoLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, LogService.LOG_DEBUG );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ // maximal log level -- all output
+ setLogLevel( configMgr, Integer.MAX_VALUE );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+ }
+
+
+ // this test always expects output since when using a LogService, the log
+ // level property is ignored
+ public void testLogWithLogService()
+ {
+ LogService logService = new MockLogService();
+ ConfigurationManager configMgr = createConfigurationManager( logService );
+
+ setLogLevel( configMgr, LogService.LOG_WARNING );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, LogService.LOG_ERROR );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, LogService.LOG_ERROR - 1 );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, Integer.MIN_VALUE );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, LogService.LOG_INFO );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, LogService.LOG_DEBUG );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+
+ setLogLevel( configMgr, Integer.MAX_VALUE );
+ assertLog( configMgr, LogService.LOG_DEBUG, "Debug Test Message", null );
+ assertLog( configMgr, LogService.LOG_INFO, "Info Test Message", null );
+ assertLog( configMgr, LogService.LOG_WARNING, "Warning Test Message", null );
+ assertLog( configMgr, LogService.LOG_ERROR, "Error Test Message", null );
+ }
+
+
+ private void assertNoLog( ConfigurationManager configMgr, int level, String message, Throwable t )
+ {
+ try
+ {
+ configMgr.log( level, message, t );
+ assertTrue( "Expecting no log output", output.size() == 0 );
+ }
+ finally
+ {
+ // clear the output for future data
+ output.reset();
+ }
+ }
+
+
+ private void assertLog( ConfigurationManager configMgr, int level, String message, Throwable t )
+ {
+ try
+ {
+ configMgr.log( level, message, t );
+ assertTrue( "Expecting log output", output.size() > 0 );
+
+ final String expectedLog = MockLogService.toMessageLine( level, message );
+ final String actualLog = new String( output.toByteArray() );
+ assertEquals( "Log Message not correct", expectedLog, actualLog );
+
+ }
+ finally
+ {
+ // clear the output for future data
+ output.reset();
+ }
+ }
+
+
+ private static void setLogLevel( ConfigurationManager configMgr, int level )
+ {
+ final String fieldName = "logLevel";
+ try
+ {
+ Field field = configMgr.getClass().getDeclaredField( fieldName );
+ field.setAccessible( true );
+ field.setInt( configMgr, level );
+ }
+ catch ( Throwable ignore )
+ {
+ ignore.printStackTrace( System.out );
+ }
+ }
+
+
+ private static ConfigurationManager createConfigurationManager( final LogService logService )
+ {
+ ConfigurationManager configMgr = new ConfigurationManager();
+
+ try
+ {
+ Field field = configMgr.getClass().getDeclaredField( "logTracker" );
+ field.setAccessible( true );
+ field.set( configMgr, new ServiceTracker( new MockBundleContext(), "", null )
+ {
+ public Object getService()
+ {
+ return logService;
+ }
+ } );
+ }
+ catch ( Throwable ignore )
+ {
+ ignore.printStackTrace( System.out );
+ }
+
+ return configMgr;
+ }
+}