001 /*
002 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.util.logging;
027
028 import java.io.*;
029
030 /**
031 * Stream based logging <tt>Handler</tt>.
032 * <p>
033 * This is primarily intended as a base class or support class to
034 * be used in implementing other logging <tt>Handlers</tt>.
035 * <p>
036 * <tt>LogRecords</tt> are published to a given <tt>java.io.OutputStream</tt>.
037 * <p>
038 * <b>Configuration:</b>
039 * By default each <tt>StreamHandler</tt> is initialized using the following
040 * <tt>LogManager</tt> configuration properties. If properties are not defined
041 * (or have invalid values) then the specified default values are used.
042 * <ul>
043 * <li> java.util.logging.StreamHandler.level
044 * specifies the default level for the <tt>Handler</tt>
045 * (defaults to <tt>Level.INFO</tt>).
046 * <li> java.util.logging.StreamHandler.filter
047 * specifies the name of a <tt>Filter</tt> class to use
048 * (defaults to no <tt>Filter</tt>).
049 * <li> java.util.logging.StreamHandler.formatter
050 * specifies the name of a <tt>Formatter</tt> class to use
051 * (defaults to <tt>java.util.logging.SimpleFormatter</tt>).
052 * <li> java.util.logging.StreamHandler.encoding
053 * the name of the character set encoding to use (defaults to
054 * the default platform encoding).
055 * </ul>
056 *
057 * @version 1.26, 05/05/07
058 * @since 1.4
059 */
060
061 public class StreamHandler extends Handler {
062 private LogManager manager = LogManager.getLogManager();
063 private OutputStream output;
064 private boolean doneHeader;
065 private Writer writer;
066
067 // Private method to configure a StreamHandler from LogManager
068 // properties and/or default values as specified in the class
069 // javadoc.
070 private void configure() {
071 LogManager manager = LogManager.getLogManager();
072 String cname = getClass().getName();
073
074 setLevel(manager.getLevelProperty(cname + ".level", Level.INFO));
075 setFilter(manager.getFilterProperty(cname + ".filter", null));
076 setFormatter(manager.getFormatterProperty(cname + ".formatter",
077 new SimpleFormatter()));
078 try {
079 setEncoding(manager.getStringProperty(cname + ".encoding",
080 null));
081 } catch (Exception ex) {
082 try {
083 setEncoding(null);
084 } catch (Exception ex2) {
085 // doing a setEncoding with null should always work.
086 // assert false;
087 }
088 }
089 }
090
091 /**
092 * Create a <tt>StreamHandler</tt>, with no current output stream.
093 */
094 public StreamHandler() {
095 sealed = false;
096 configure();
097 sealed = true;
098 }
099
100 /**
101 * Create a <tt>StreamHandler</tt> with a given <tt>Formatter</tt>
102 * and output stream.
103 * <p>
104 * @param out the target output stream
105 * @param formatter Formatter to be used to format output
106 */
107 public StreamHandler(OutputStream out, Formatter formatter) {
108 sealed = false;
109 configure();
110 setFormatter(formatter);
111 setOutputStream(out);
112 sealed = true;
113 }
114
115 /**
116 * Change the output stream.
117 * <P>
118 * If there is a current output stream then the <tt>Formatter</tt>'s
119 * tail string is written and the stream is flushed and closed.
120 * Then the output stream is replaced with the new output stream.
121 *
122 * @param out New output stream. May not be null.
123 * @exception SecurityException if a security manager exists and if
124 * the caller does not have <tt>LoggingPermission("control")</tt>.
125 */
126 protected synchronized void setOutputStream(OutputStream out)
127 throws SecurityException {
128 if (out == null) {
129 throw new NullPointerException();
130 }
131 flushAndClose();
132 output = out;
133 doneHeader = false;
134 String encoding = getEncoding();
135 if (encoding == null) {
136 writer = new OutputStreamWriter(output);
137 } else {
138 try {
139 writer = new OutputStreamWriter(output, encoding);
140 } catch (UnsupportedEncodingException ex) {
141 // This shouldn't happen. The setEncoding method
142 // should have validated that the encoding is OK.
143 throw new Error("Unexpected exception " + ex);
144 }
145 }
146 }
147
148 /**
149 * Set (or change) the character encoding used by this <tt>Handler</tt>.
150 * <p>
151 * The encoding should be set before any <tt>LogRecords</tt> are written
152 * to the <tt>Handler</tt>.
153 *
154 * @param encoding The name of a supported character encoding.
155 * May be null, to indicate the default platform encoding.
156 * @exception SecurityException if a security manager exists and if
157 * the caller does not have <tt>LoggingPermission("control")</tt>.
158 * @exception UnsupportedEncodingException if the named encoding is
159 * not supported.
160 */
161 public void setEncoding(String encoding) throws SecurityException,
162 java.io.UnsupportedEncodingException {
163 super .setEncoding(encoding);
164 if (output == null) {
165 return;
166 }
167 // Replace the current writer with a writer for the new encoding.
168 flush();
169 if (encoding == null) {
170 writer = new OutputStreamWriter(output);
171 } else {
172 writer = new OutputStreamWriter(output, encoding);
173 }
174 }
175
176 /**
177 * Format and publish a <tt>LogRecord</tt>.
178 * <p>
179 * The <tt>StreamHandler</tt> first checks if there is an <tt>OutputStream</tt>
180 * and if the given <tt>LogRecord</tt> has at least the required log level.
181 * If not it silently returns. If so, it calls any associated
182 * <tt>Filter</tt> to check if the record should be published. If so,
183 * it calls its <tt>Formatter</tt> to format the record and then writes
184 * the result to the current output stream.
185 * <p>
186 * If this is the first <tt>LogRecord</tt> to be written to a given
187 * <tt>OutputStream</tt>, the <tt>Formatter</tt>'s "head" string is
188 * written to the stream before the <tt>LogRecord</tt> is written.
189 *
190 * @param record description of the log event. A null record is
191 * silently ignored and is not published
192 */
193 public synchronized void publish(LogRecord record) {
194 if (!isLoggable(record)) {
195 return;
196 }
197 String msg;
198 try {
199 msg = getFormatter().format(record);
200 } catch (Exception ex) {
201 // We don't want to throw an exception here, but we
202 // report the exception to any registered ErrorManager.
203 reportError(null, ex, ErrorManager.FORMAT_FAILURE);
204 return;
205 }
206
207 try {
208 if (!doneHeader) {
209 writer.write(getFormatter().getHead(this ));
210 doneHeader = true;
211 }
212 writer.write(msg);
213 } catch (Exception ex) {
214 // We don't want to throw an exception here, but we
215 // report the exception to any registered ErrorManager.
216 reportError(null, ex, ErrorManager.WRITE_FAILURE);
217 }
218 }
219
220 /**
221 * Check if this <tt>Handler</tt> would actually log a given <tt>LogRecord</tt>.
222 * <p>
223 * This method checks if the <tt>LogRecord</tt> has an appropriate level and
224 * whether it satisfies any <tt>Filter</tt>. It will also return false if
225 * no output stream has been assigned yet or the LogRecord is Null.
226 * <p>
227 * @param record a <tt>LogRecord</tt>
228 * @return true if the <tt>LogRecord</tt> would be logged.
229 *
230 */
231 public boolean isLoggable(LogRecord record) {
232 if (writer == null || record == null) {
233 return false;
234 }
235 return super .isLoggable(record);
236 }
237
238 /**
239 * Flush any buffered messages.
240 */
241 public synchronized void flush() {
242 if (writer != null) {
243 try {
244 writer.flush();
245 } catch (Exception ex) {
246 // We don't want to throw an exception here, but we
247 // report the exception to any registered ErrorManager.
248 reportError(null, ex, ErrorManager.FLUSH_FAILURE);
249 }
250 }
251 }
252
253 private synchronized void flushAndClose() throws SecurityException {
254 checkAccess();
255 if (writer != null) {
256 try {
257 if (!doneHeader) {
258 writer.write(getFormatter().getHead(this ));
259 doneHeader = true;
260 }
261 writer.write(getFormatter().getTail(this ));
262 writer.flush();
263 writer.close();
264 } catch (Exception ex) {
265 // We don't want to throw an exception here, but we
266 // report the exception to any registered ErrorManager.
267 reportError(null, ex, ErrorManager.CLOSE_FAILURE);
268 }
269 writer = null;
270 output = null;
271 }
272 }
273
274 /**
275 * Close the current output stream.
276 * <p>
277 * The <tt>Formatter</tt>'s "tail" string is written to the stream before it
278 * is closed. In addition, if the <tt>Formatter</tt>'s "head" string has not
279 * yet been written to the stream, it will be written before the
280 * "tail" string.
281 *
282 * @exception SecurityException if a security manager exists and if
283 * the caller does not have LoggingPermission("control").
284 */
285 public synchronized void close() throws SecurityException {
286 flushAndClose();
287 }
288 }
|