blob: 3655868c685298792e46b1d77e9ac2efe32b345c [file] [log] [blame]
/*
* 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.text.MessageFormat;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
/**
* Log implementation either logging to a {@code LogService} or to {@code System.err}.
* The logger can be get using the static {@link #logger} field.
*
* The logger is initialized through {@link #start(BundleContext)} and {@link #set(ServiceReference)}.
* It gets cleaned up through {@link #stop()}.
*/
public class Log
{
/** The shared logger instance. */
public static final Log logger = new Log();
/**
* 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.
*/
private 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;
// the ServiceTracker to emit log services (see log(int, String, Throwable))
@SuppressWarnings("rawtypes")
private volatile ServiceTracker logTracker;
// the maximum log level when no LogService is available
private volatile int logLevel = CM_LOG_LEVEL_DEFAULT;
private volatile ServiceReference<ConfigurationAdmin> serviceReference;
/**
* Start the tracker for the logger and set the log level according to the configuration.
* @param bundleContext The bundle context
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void start( final 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 the service reference to the configuration admin in order to include this
* in every log message.
* @param ref The service reference
*/
public void set(final ServiceReference<ConfigurationAdmin> ref)
{
this.serviceReference = ref;
}
/**
* Stop the log service tracker and clear the service reference
*/
public void stop()
{
if ( logTracker != null )
{
logTracker.close();
logTracker = null;
}
serviceReference = null;
}
/**
* Is the log level enabled?
* @param level The level
* @return {@code true} if enabled
*/
public boolean isLogEnabled( final int level )
{
return level <= logLevel;
}
/**
* Log a message in the given level.
* If arguments are provided and contain a {@code ServiceReference} then
* the argument is replaced with the result of {@link #toString(ServiceReference)}.
*
* @param level The log level
* @param format The message text
* @param args The optional arguments
*/
public void log( final int level, final String format, final Object[] args )
{
@SuppressWarnings("rawtypes")
final ServiceTracker tracker = this.logTracker;
final Object log = tracker == null ? null : tracker.getService();
if ( log != null || isLogEnabled( level ) )
{
Throwable throwable = null;
String message = format;
if ( args != null && args.length > 0 )
{
for(int i=0; i<args.length; i++)
{
if ( args[i] instanceof ServiceReference )
{
args[i] = toString((ServiceReference<?>)args[i]);
}
}
if ( args[args.length - 1] instanceof Throwable )
{
throwable = ( Throwable ) args[args.length - 1];
}
message = MessageFormat.format( format, args );
}
log( level, message, throwable );
}
}
/**
* Log the message with the given level and throwable.
* @param level The log level
* @param message The message
* @param t The exception
*/
public void log( final int level, final String message, final Throwable t )
{
// log using the LogService if available
@SuppressWarnings("rawtypes")
final ServiceTracker tracker = this.logTracker;
final Object log = tracker == null ? null : tracker.getService();
if ( log != null )
{
( ( LogService ) log ).log( serviceReference, level, message, t );
return;
}
// Otherwise only log if more serious than the configured level
if ( isLogEnabled( level ) )
{
String code;
switch ( level )
{
case LogService.LOG_INFO:
code = "*INFO *";
break;
case LogService.LOG_WARNING:
code = "*WARN *";
break;
case LogService.LOG_ERROR:
code = "*ERROR*";
break;
case LogService.LOG_DEBUG:
default:
code = "*DEBUG*";
}
System.err.println( code + " " + message );
if ( t != null )
{
t.printStackTrace( System.err );
}
}
}
/**
* Create a string representation of the service reference
* @param ref The service reference
* @return The string representation
*/
private static String toString( final ServiceReference<?> ref )
{
String[] ocs = ( String[] ) ref.getProperty( "objectClass" );
StringBuilder buf = new StringBuilder( "[" );
for ( int i = 0; i < ocs.length; i++ )
{
buf.append( ocs[i] );
if ( i < ocs.length - 1 )
buf.append( ", " );
}
buf.append( ", id=" ).append( ref.getProperty( Constants.SERVICE_ID ) );
Bundle provider = ref.getBundle();
if ( provider != null )
{
buf.append( ", bundle=" ).append( provider.getBundleId() );
buf.append( '/' ).append( Activator.getLocation(provider) );
}
else
{
buf.append( ", unregistered" );
}
buf.append( "]" );
return buf.toString();
}
}