1/* 2 * Copyright (c) 2000, 2006, 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.io.*; 30 31/** 32 * Stream based logging <tt>Handler</tt>. 33 * <p> 34 * This is primarily intended as a base class or support class to 35 * be used in implementing other logging <tt>Handlers</tt>. 36 * <p> 37 * <tt>LogRecords</tt> are published to a given <tt>java.io.OutputStream</tt>. 38 * <p> 39 * <b>Configuration:</b> 40 * By default each <tt>StreamHandler</tt> is initialized using the following 41 * <tt>LogManager</tt> configuration properties. If properties are not defined 42 * (or have invalid values) then the specified default values are used. 43 * <ul> 44 * <li> java.util.logging.StreamHandler.level 45 * specifies the default level for the <tt>Handler</tt> 46 * (defaults to <tt>Level.INFO</tt>). 47 * <li> java.util.logging.StreamHandler.filter 48 * specifies the name of a <tt>Filter</tt> class to use 49 * (defaults to no <tt>Filter</tt>). 50 * <li> java.util.logging.StreamHandler.formatter 51 * specifies the name of a <tt>Formatter</tt> class to use 52 * (defaults to <tt>java.util.logging.SimpleFormatter</tt>). 53 * <li> java.util.logging.StreamHandler.encoding 54 * the name of the character set encoding to use (defaults to 55 * the default platform encoding). 56 * </ul> 57 * 58 * @since 1.4 59 */ 60 61public class StreamHandler extends Handler { 62 private LogManager manager = LogManager.getLogManager(); 63 private OutputStream output; 64 private boolean doneHeader; 65 private Writer writer; 66 67 // Private method to configure a StreamHandler from LogManager 68 // properties and/or default values as specified in the class 69 // javadoc. 70 private void configure() { 71 LogManager manager = LogManager.getLogManager(); 72 String cname = getClass().getName(); 73 74 setLevel(manager.getLevelProperty(cname +".level", Level.INFO)); 75 setFilter(manager.getFilterProperty(cname +".filter", null)); 76 setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter())); 77 try { 78 setEncoding(manager.getStringProperty(cname +".encoding", null)); 79 } catch (Exception ex) { 80 try { 81 setEncoding(null); 82 } catch (Exception ex2) { 83 // doing a setEncoding with null should always work. 84 // assert false; 85 } 86 } 87 } 88 89 /** 90 * Create a <tt>StreamHandler</tt>, with no current output stream. 91 */ 92 public StreamHandler() { 93 sealed = false; 94 configure(); 95 sealed = true; 96 } 97 98 /** 99 * Create a <tt>StreamHandler</tt> with a given <tt>Formatter</tt> 100 * and output stream. 101 * <p> 102 * @param out the target output stream 103 * @param formatter Formatter to be used to format output 104 */ 105 public StreamHandler(OutputStream out, Formatter formatter) { 106 sealed = false; 107 configure(); 108 setFormatter(formatter); 109 setOutputStream(out); 110 sealed = true; 111 } 112 113 /** 114 * Change the output stream. 115 * <P> 116 * If there is a current output stream then the <tt>Formatter</tt>'s 117 * tail string is written and the stream is flushed and closed. 118 * Then the output stream is replaced with the new output stream. 119 * 120 * @param out New output stream. May not be null. 121 * @exception SecurityException if a security manager exists and if 122 * the caller does not have <tt>LoggingPermission("control")</tt>. 123 */ 124 protected synchronized void setOutputStream(OutputStream out) throws SecurityException { 125 if (out == null) { 126 throw new NullPointerException(); 127 } 128 flushAndClose(); 129 output = out; 130 doneHeader = false; 131 String encoding = getEncoding(); 132 if (encoding == null) { 133 writer = new OutputStreamWriter(output); 134 } else { 135 try { 136 writer = new OutputStreamWriter(output, encoding); 137 } catch (UnsupportedEncodingException ex) { 138 // This shouldn't happen. The setEncoding method 139 // should have validated that the encoding is OK. 140 throw new Error("Unexpected exception " + ex); 141 } 142 } 143 } 144 145 /** 146 * Set (or change) the character encoding used by this <tt>Handler</tt>. 147 * <p> 148 * The encoding should be set before any <tt>LogRecords</tt> are written 149 * to the <tt>Handler</tt>. 150 * 151 * @param encoding The name of a supported character encoding. 152 * May be null, to indicate the default platform encoding. 153 * @exception SecurityException if a security manager exists and if 154 * the caller does not have <tt>LoggingPermission("control")</tt>. 155 * @exception UnsupportedEncodingException if the named encoding is 156 * not supported. 157 */ 158 public void setEncoding(String encoding) 159 throws SecurityException, java.io.UnsupportedEncodingException { 160 super.setEncoding(encoding); 161 if (output == null) { 162 return; 163 } 164 // Replace the current writer with a writer for the new encoding. 165 flush(); 166 if (encoding == null) { 167 writer = new OutputStreamWriter(output); 168 } else { 169 writer = new OutputStreamWriter(output, encoding); 170 } 171 } 172 173 /** 174 * Format and publish a <tt>LogRecord</tt>. 175 * <p> 176 * The <tt>StreamHandler</tt> first checks if there is an <tt>OutputStream</tt> 177 * and if the given <tt>LogRecord</tt> has at least the required log level. 178 * If not it silently returns. If so, it calls any associated 179 * <tt>Filter</tt> to check if the record should be published. If so, 180 * it calls its <tt>Formatter</tt> to format the record and then writes 181 * the result to the current output stream. 182 * <p> 183 * If this is the first <tt>LogRecord</tt> to be written to a given 184 * <tt>OutputStream</tt>, the <tt>Formatter</tt>'s "head" string is 185 * written to the stream before the <tt>LogRecord</tt> is written. 186 * 187 * @param record description of the log event. A null record is 188 * silently ignored and is not published 189 */ 190 public synchronized void publish(LogRecord record) { 191 if (!isLoggable(record)) { 192 return; 193 } 194 String msg; 195 try { 196 msg = getFormatter().format(record); 197 } catch (Exception ex) { 198 // We don't want to throw an exception here, but we 199 // report the exception to any registered ErrorManager. 200 reportError(null, ex, ErrorManager.FORMAT_FAILURE); 201 return; 202 } 203 204 try { 205 if (!doneHeader) { 206 writer.write(getFormatter().getHead(this)); 207 doneHeader = true; 208 } 209 writer.write(msg); 210 } catch (Exception ex) { 211 // We don't want to throw an exception here, but we 212 // report the exception to any registered ErrorManager. 213 reportError(null, ex, ErrorManager.WRITE_FAILURE); 214 } 215 } 216 217 218 /** 219 * Check if this <tt>Handler</tt> would actually log a given <tt>LogRecord</tt>. 220 * <p> 221 * This method checks if the <tt>LogRecord</tt> has an appropriate level and 222 * whether it satisfies any <tt>Filter</tt>. It will also return false if 223 * no output stream has been assigned yet or the LogRecord is null. 224 * <p> 225 * @param record a <tt>LogRecord</tt> 226 * @return true if the <tt>LogRecord</tt> would be logged. 227 * 228 */ 229 public boolean isLoggable(LogRecord record) { 230 if (writer == null || record == null) { 231 return false; 232 } 233 return super.isLoggable(record); 234 } 235 236 /** 237 * Flush any buffered messages. 238 */ 239 public synchronized void flush() { 240 if (writer != null) { 241 try { 242 writer.flush(); 243 } catch (Exception ex) { 244 // We don't want to throw an exception here, but we 245 // report the exception to any registered ErrorManager. 246 reportError(null, ex, ErrorManager.FLUSH_FAILURE); 247 } 248 } 249 } 250 251 private synchronized void flushAndClose() throws SecurityException { 252 checkPermission(); 253 if (writer != null) { 254 try { 255 if (!doneHeader) { 256 writer.write(getFormatter().getHead(this)); 257 doneHeader = true; 258 } 259 writer.write(getFormatter().getTail(this)); 260 writer.flush(); 261 writer.close(); 262 } catch (Exception ex) { 263 // We don't want to throw an exception here, but we 264 // report the exception to any registered ErrorManager. 265 reportError(null, ex, ErrorManager.CLOSE_FAILURE); 266 } 267 writer = null; 268 output = null; 269 } 270 } 271 272 /** 273 * Close the current output stream. 274 * <p> 275 * The <tt>Formatter</tt>'s "tail" string is written to the stream before it 276 * is closed. In addition, if the <tt>Formatter</tt>'s "head" string has not 277 * yet been written to the stream, it will be written before the 278 * "tail" string. 279 * 280 * @exception SecurityException if a security manager exists and if 281 * the caller does not have LoggingPermission("control"). 282 */ 283 public synchronized void close() throws SecurityException { 284 flushAndClose(); 285 } 286} 287