001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 package org.apache.felix.framework; 020 021 import java.lang.reflect.InvocationTargetException; 022 import java.lang.reflect.Method; 023 import org.osgi.framework.*; 024 025 /** 026 * <p> 027 * This class mimics the standard OSGi <tt>LogService</tt> interface. An 028 * instance of this class is used by the framework for all logging. By default 029 * this class logs messages to standard out. The log level can be set to 030 * control the amount of logging performed, where a higher number results in 031 * more logging. A log level of zero turns off logging completely. 032 * </p> 033 * <p> 034 * The log levels match those specified in the OSGi Log Service (i.e., 1 = error, 035 * 2 = warning, 3 = information, and 4 = debug). The default value is 1. 036 * </p> 037 * <p> 038 * This class also uses the System Bundle's context to track log services 039 * and will use the highest ranking log service, if present, as a back end 040 * instead of printing to standard out. The class uses reflection to invoking 041 * the log service's method to avoid a dependency on the log interface. 042 * </p> 043 **/ 044 public class Logger implements ServiceListener 045 { 046 public static final int LOG_ERROR = 1; 047 public static final int LOG_WARNING = 2; 048 public static final int LOG_INFO = 3; 049 public static final int LOG_DEBUG = 4; 050 051 private int m_logLevel = 1; 052 private BundleContext m_context = null; 053 054 private final static int LOGGER_OBJECT_IDX = 0; 055 private final static int LOGGER_METHOD_IDX = 1; 056 private ServiceReference m_logRef = null; 057 private Object[] m_logger = null; 058 059 public Logger() 060 { 061 } 062 063 public final synchronized void setLogLevel(int i) 064 { 065 m_logLevel = i; 066 } 067 068 public final synchronized int getLogLevel() 069 { 070 return m_logLevel; 071 } 072 073 protected void setSystemBundleContext(BundleContext context) 074 { 075 // TODO: Find a way to log to a log service inside the framework. 076 // The issue is that we log messages while holding framework 077 // internal locks -- hence, when a log service calls back into 078 // the framework (e.g., by loading a class) we might deadlock. 079 // One instance of this problem is tracked in FELIX-536. 080 // For now we just disable logging to log services inside the 081 // framework. 082 083 // m_context = context; 084 // startListeningForLogService(); 085 } 086 087 public final void log(int level, String msg) 088 { 089 _log(null, level, msg, null); 090 } 091 092 public final void log(int level, String msg, Throwable throwable) 093 { 094 _log(null, level, msg, throwable); 095 } 096 097 public final void log(ServiceReference sr, int level, String msg) 098 { 099 _log(sr, level, msg, null); 100 } 101 102 public final void log(ServiceReference sr, int level, String msg, Throwable throwable) 103 { 104 _log(sr, level, msg, throwable); 105 } 106 107 protected void doLog(ServiceReference sr, int level, String msg, Throwable throwable) 108 { 109 String s = (sr == null) ? null : "SvcRef " + sr; 110 s = (s == null) ? msg : s + " " + msg; 111 s = (throwable == null) ? s : s + " (" + throwable + ")"; 112 switch (level) 113 { 114 case LOG_DEBUG: 115 System.out.println("DEBUG: " + s); 116 break; 117 case LOG_ERROR: 118 System.out.println("ERROR: " + s); 119 if (throwable != null) 120 { 121 if ((throwable instanceof BundleException) && 122 (((BundleException) throwable).getNestedException() != null)) 123 { 124 throwable = ((BundleException) throwable).getNestedException(); 125 } 126 throwable.printStackTrace(); 127 } 128 break; 129 case LOG_INFO: 130 System.out.println("INFO: " + s); 131 break; 132 case LOG_WARNING: 133 System.out.println("WARNING: " + s); 134 break; 135 default: 136 System.out.println("UNKNOWN[" + level + "]: " + s); 137 } 138 } 139 140 private void _log(ServiceReference sr, int level, String msg, Throwable throwable) 141 { 142 // Save our own copy just in case it changes. We could try to do 143 // more conservative locking here, but let's be optimistic. 144 Object[] logger = m_logger; 145 146 if (m_logLevel >= level) 147 { 148 // Use the log service if available. 149 if (logger != null) 150 { 151 _logReflectively(logger, sr, level, msg, throwable); 152 } 153 // Otherwise, default logging action. 154 else 155 { 156 doLog(sr, level, msg, throwable); 157 } 158 } 159 } 160 161 private void _logReflectively( 162 Object[] logger, ServiceReference sr, int level, String msg, Throwable throwable) 163 { 164 if (logger != null) 165 { 166 Object[] params = { 167 sr, new Integer(level), msg, throwable 168 }; 169 try 170 { 171 ((Method) logger[LOGGER_METHOD_IDX]).invoke(logger[LOGGER_OBJECT_IDX], params); 172 } 173 catch (InvocationTargetException ex) 174 { 175 System.err.println("Logger: " + ex); 176 } 177 catch (IllegalAccessException ex) 178 { 179 System.err.println("Logger: " + ex); 180 } 181 } 182 } 183 184 /** 185 * This method is called when the system bundle context is set; 186 * it simply adds a service listener so that the system bundle can track 187 * log services to be used as the back end of the logging mechanism. It also 188 * attempts to get an existing log service, if present, but in general 189 * there will never be a log service present since the system bundle is 190 * started before every other bundle. 191 **/ 192 private synchronized void startListeningForLogService() 193 { 194 // Add a service listener for log services. 195 try 196 { 197 m_context.addServiceListener( 198 this, "(objectClass=org.osgi.service.log.LogService)"); 199 } 200 catch (InvalidSyntaxException ex) { 201 // This will never happen since the filter is hard coded. 202 } 203 // Try to get an existing log service. 204 m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService"); 205 // Get the service object if available and set it in the logger. 206 if (m_logRef != null) 207 { 208 setLogger(m_context.getService(m_logRef)); 209 } 210 } 211 212 /** 213 * This method implements the callback for the ServiceListener interface. 214 * It is public as a byproduct of implementing the interface and should 215 * not be called directly. This method tracks run-time changes to log 216 * service availability. If the log service being used by the framework's 217 * logging mechanism goes away, then this will try to find an alternative. 218 * If a higher ranking log service is registered, then this will switch 219 * to the higher ranking log service. 220 **/ 221 public final synchronized void serviceChanged(ServiceEvent event) 222 { 223 // If no logger is in use, then grab this one. 224 if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef == null)) 225 { 226 m_logRef = event.getServiceReference(); 227 // Get the service object and set it in the logger. 228 setLogger(m_context.getService(m_logRef)); 229 } 230 // If a logger is in use, but this one has a higher ranking, then swap 231 // it for the existing logger. 232 else if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef != null)) 233 { 234 ServiceReference ref = 235 m_context.getServiceReference("org.osgi.service.log.LogService"); 236 if (!ref.equals(m_logRef)) 237 { 238 m_context.ungetService(m_logRef); 239 m_logRef = ref; 240 setLogger(m_context.getService(m_logRef)); 241 } 242 243 } 244 // If the current logger is going away, release it and try to 245 // find another one. 246 else if ((event.getType() == ServiceEvent.UNREGISTERING) && 247 m_logRef.equals(event.getServiceReference())) 248 { 249 // Unget the service object. 250 m_context.ungetService(m_logRef); 251 // Try to get an existing log service. 252 m_logRef = m_context.getServiceReference( 253 "org.osgi.service.log.LogService"); 254 // Get the service object if available and set it in the logger. 255 if (m_logRef != null) 256 { 257 setLogger(m_context.getService(m_logRef)); 258 } 259 else 260 { 261 setLogger(null); 262 } 263 } 264 } 265 266 /** 267 * This method sets the new log service object. It also caches the method to 268 * invoke. The service object and method are stored in array to optimistically 269 * eliminate the need to locking when logging. 270 **/ 271 private void setLogger(Object logObj) 272 { 273 if (logObj == null) 274 { 275 m_logger = null; 276 } 277 else 278 { 279 Class[] formalParams = { 280 ServiceReference.class, 281 Integer.TYPE, 282 String.class, 283 Throwable.class 284 }; 285 286 try 287 { 288 Method logMethod = logObj.getClass().getMethod("log", formalParams); 289 logMethod.setAccessible(true); 290 m_logger = new Object[] { logObj, logMethod }; 291 } 292 catch (NoSuchMethodException ex) 293 { 294 System.err.println("Logger: " + ex); 295 m_logger = null; 296 } 297 } 298 } 299 }