1/* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27package java.util.logging; 28 29import java.util.Objects; 30import java.io.UnsupportedEncodingException; 31import java.security.AccessController; 32import java.security.PrivilegedAction; 33 34/** 35 * A {@code Handler} object takes log messages from a {@code Logger} and 36 * exports them. It might for example, write them to a console 37 * or write them to a file, or send them to a network logging service, 38 * or forward them to an OS log, or whatever. 39 * <p> 40 * A {@code Handler} can be disabled by doing a {@code setLevel(Level.OFF)} 41 * and can be re-enabled by doing a {@code setLevel} with an appropriate level. 42 * <p> 43 * {@code Handler} classes typically use {@code LogManager} properties to set 44 * default values for the {@code Handler}'s {@code Filter}, {@code Formatter}, 45 * and {@code Level}. See the specific documentation for each concrete 46 * {@code Handler} class. 47 * 48 * 49 * @since 1.4 50 */ 51 52public abstract class Handler { 53 private static final int offValue = Level.OFF.intValue(); 54 private final LogManager manager = LogManager.getLogManager(); 55 56 // We're using volatile here to avoid synchronizing getters, which 57 // would prevent other threads from calling isLoggable() 58 // while publish() is executing. 59 // On the other hand, setters will be synchronized to exclude concurrent 60 // execution with more complex methods, such as StreamHandler.publish(). 61 // We wouldn't want 'level' to be changed by another thread in the middle 62 // of the execution of a 'publish' call. 63 private volatile Filter filter; 64 private volatile Formatter formatter; 65 private volatile Level logLevel = Level.ALL; 66 private volatile ErrorManager errorManager = new ErrorManager(); 67 private volatile String encoding; 68 69 /** 70 * Default constructor. The resulting {@code Handler} has a log 71 * level of {@code Level.ALL}, no {@code Formatter}, and no 72 * {@code Filter}. A default {@code ErrorManager} instance is installed 73 * as the {@code ErrorManager}. 74 */ 75 protected Handler() { 76 } 77 78 /** 79 * Package-private constructor for chaining from subclass constructors 80 * that wish to configure the handler with specific default and/or 81 * specified values. 82 * 83 * @param defaultLevel a default {@link Level} to configure if one is 84 * not found in LogManager configuration properties 85 * @param defaultFormatter a default {@link Formatter} to configure if one is 86 * not specified by {@code specifiedFormatter} parameter 87 * nor found in LogManager configuration properties 88 * @param specifiedFormatter if not null, this is the formatter to configure 89 */ 90 Handler(Level defaultLevel, Formatter defaultFormatter, 91 Formatter specifiedFormatter) { 92 93 LogManager manager = LogManager.getLogManager(); 94 String cname = getClass().getName(); 95 96 final Level level = manager.getLevelProperty(cname + ".level", defaultLevel); 97 final Filter filter = manager.getFilterProperty(cname + ".filter", null); 98 final Formatter formatter = specifiedFormatter == null 99 ? manager.getFormatterProperty(cname + ".formatter", defaultFormatter) 100 : specifiedFormatter; 101 final String encoding = manager.getStringProperty(cname + ".encoding", null); 102 103 AccessController.doPrivileged(new PrivilegedAction<Void>() { 104 @Override 105 public Void run() { 106 setLevel(level); 107 setFilter(filter); 108 setFormatter(formatter); 109 try { 110 setEncoding(encoding); 111 } catch (Exception ex) { 112 try { 113 setEncoding(null); 114 } catch (Exception ex2) { 115 // doing a setEncoding with null should always work. 116 // assert false; 117 } 118 } 119 return null; 120 } 121 }, null, LogManager.controlPermission); 122 } 123 124 /** 125 * Publish a {@code LogRecord}. 126 * <p> 127 * The logging request was made initially to a {@code Logger} object, 128 * which initialized the {@code LogRecord} and forwarded it here. 129 * <p> 130 * The {@code Handler} is responsible for formatting the message, when and 131 * if necessary. The formatting should include localization. 132 * 133 * @param record description of the log event. A null record is 134 * silently ignored and is not published 135 */ 136 public abstract void publish(LogRecord record); 137 138 /** 139 * Flush any buffered output. 140 */ 141 public abstract void flush(); 142 143 /** 144 * Close the {@code Handler} and free all associated resources. 145 * <p> 146 * The close method will perform a {@code flush} and then close the 147 * {@code Handler}. After close has been called this {@code Handler} 148 * should no longer be used. Method calls may either be silently 149 * ignored or may throw runtime exceptions. 150 * 151 * @exception SecurityException if a security manager exists and if 152 * the caller does not have {@code LoggingPermission("control")}. 153 */ 154 public abstract void close() throws SecurityException; 155 156 /** 157 * Set a {@code Formatter}. This {@code Formatter} will be used 158 * to format {@code LogRecords} for this {@code Handler}. 159 * <p> 160 * Some {@code Handlers} may not use {@code Formatters}, in 161 * which case the {@code Formatter} will be remembered, but not used. 162 * 163 * @param newFormatter the {@code Formatter} to use (may not be null) 164 * @exception SecurityException if a security manager exists and if 165 * the caller does not have {@code LoggingPermission("control")}. 166 */ 167 public synchronized void setFormatter(Formatter newFormatter) throws SecurityException { 168 checkPermission(); 169 formatter = Objects.requireNonNull(newFormatter); 170 } 171 172 /** 173 * Return the {@code Formatter} for this {@code Handler}. 174 * @return the {@code Formatter} (may be null). 175 */ 176 public Formatter getFormatter() { 177 return formatter; 178 } 179 180 /** 181 * Set the character encoding used by this {@code Handler}. 182 * <p> 183 * The encoding should be set before any {@code LogRecords} are written 184 * to the {@code Handler}. 185 * 186 * @param encoding The name of a supported character encoding. 187 * May be null, to indicate the default platform encoding. 188 * @exception SecurityException if a security manager exists and if 189 * the caller does not have {@code LoggingPermission("control")}. 190 * @exception UnsupportedEncodingException if the named encoding is 191 * not supported. 192 */ 193 public synchronized void setEncoding(String encoding) 194 throws SecurityException, java.io.UnsupportedEncodingException { 195 checkPermission(); 196 if (encoding != null) { 197 try { 198 if(!java.nio.charset.Charset.isSupported(encoding)) { 199 throw new UnsupportedEncodingException(encoding); 200 } 201 } catch (java.nio.charset.IllegalCharsetNameException e) { 202 throw new UnsupportedEncodingException(encoding); 203 } 204 } 205 this.encoding = encoding; 206 } 207 208 /** 209 * Return the character encoding for this {@code Handler}. 210 * 211 * @return The encoding name. May be null, which indicates the 212 * default encoding should be used. 213 */ 214 public String getEncoding() { 215 return encoding; 216 } 217 218 /** 219 * Set a {@code Filter} to control output on this {@code Handler}. 220 * <P> 221 * For each call of {@code publish} the {@code Handler} will call 222 * this {@code Filter} (if it is non-null) to check if the 223 * {@code LogRecord} should be published or discarded. 224 * 225 * @param newFilter a {@code Filter} object (may be null) 226 * @exception SecurityException if a security manager exists and if 227 * the caller does not have {@code LoggingPermission("control")}. 228 */ 229 public synchronized void setFilter(Filter newFilter) throws SecurityException { 230 checkPermission(); 231 filter = newFilter; 232 } 233 234 /** 235 * Get the current {@code Filter} for this {@code Handler}. 236 * 237 * @return a {@code Filter} object (may be null) 238 */ 239 public Filter getFilter() { 240 return filter; 241 } 242 243 /** 244 * Define an ErrorManager for this Handler. 245 * <p> 246 * The ErrorManager's "error" method will be invoked if any 247 * errors occur while using this Handler. 248 * 249 * @param em the new ErrorManager 250 * @exception SecurityException if a security manager exists and if 251 * the caller does not have {@code LoggingPermission("control")}. 252 */ 253 public synchronized void setErrorManager(ErrorManager em) { 254 checkPermission(); 255 if (em == null) { 256 throw new NullPointerException(); 257 } 258 errorManager = em; 259 } 260 261 /** 262 * Retrieves the ErrorManager for this Handler. 263 * 264 * @return the ErrorManager for this Handler 265 * @exception SecurityException if a security manager exists and if 266 * the caller does not have {@code LoggingPermission("control")}. 267 */ 268 public ErrorManager getErrorManager() { 269 checkPermission(); 270 return errorManager; 271 } 272 273 /** 274 * Protected convenience method to report an error to this Handler's 275 * ErrorManager. Note that this method retrieves and uses the ErrorManager 276 * without doing a security check. It can therefore be used in 277 * environments where the caller may be non-privileged. 278 * 279 * @param msg a descriptive string (may be null) 280 * @param ex an exception (may be null) 281 * @param code an error code defined in ErrorManager 282 */ 283 protected void reportError(String msg, Exception ex, int code) { 284 try { 285 errorManager.error(msg, ex, code); 286 } catch (Exception ex2) { 287 System.err.println("Handler.reportError caught:"); 288 ex2.printStackTrace(); 289 } 290 } 291 292 /** 293 * Set the log level specifying which message levels will be 294 * logged by this {@code Handler}. Message levels lower than this 295 * value will be discarded. 296 * <p> 297 * The intention is to allow developers to turn on voluminous 298 * logging, but to limit the messages that are sent to certain 299 * {@code Handlers}. 300 * 301 * @param newLevel the new value for the log level 302 * @exception SecurityException if a security manager exists and if 303 * the caller does not have {@code LoggingPermission("control")}. 304 */ 305 public synchronized void setLevel(Level newLevel) throws SecurityException { 306 if (newLevel == null) { 307 throw new NullPointerException(); 308 } 309 checkPermission(); 310 logLevel = newLevel; 311 } 312 313 /** 314 * Get the log level specifying which messages will be 315 * logged by this {@code Handler}. Message levels lower 316 * than this level will be discarded. 317 * @return the level of messages being logged. 318 */ 319 public Level getLevel() { 320 return logLevel; 321 } 322 323 /** 324 * Check if this {@code Handler} would actually log a given {@code LogRecord}. 325 * <p> 326 * This method checks if the {@code LogRecord} has an appropriate 327 * {@code Level} and whether it satisfies any {@code Filter}. It also 328 * may make other {@code Handler} specific checks that might prevent a 329 * handler from logging the {@code LogRecord}. It will return false if 330 * the {@code LogRecord} is null. 331 * 332 * @param record a {@code LogRecord} 333 * @return true if the {@code LogRecord} would be logged. 334 * 335 */ 336 public boolean isLoggable(LogRecord record) { 337 final int levelValue = getLevel().intValue(); 338 if (record.getLevel().intValue() < levelValue || levelValue == offValue) { 339 return false; 340 } 341 final Filter filter = getFilter(); 342 if (filter == null) { 343 return true; 344 } 345 return filter.isLoggable(record); 346 } 347 348 // Package-private support method for security checks. 349 // We check that the caller has appropriate security privileges 350 // to update Handler state and if not throw a SecurityException. 351 void checkPermission() throws SecurityException { 352 manager.checkPermission(); 353 } 354} 355