Home | History | Annotate | Download | only in logging
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 
     28 package java.util.logging;
     29 
     30 import java.io.*;
     31 import java.net.*;
     32 import libcore.net.NetworkSecurityPolicy;
     33 
     34 /**
     35  * Simple network logging <tt>Handler</tt>.
     36  * <p>
     37  * <tt>LogRecords</tt> are published to a network stream connection.  By default
     38  * the <tt>XMLFormatter</tt> class is used for formatting.
     39  * <p>
     40  * <b>Configuration:</b>
     41  * By default each <tt>SocketHandler</tt> is initialized using the following
     42  * <tt>LogManager</tt> configuration properties where <tt>&lt;handler-name&gt;</tt>
     43  * refers to the fully-qualified class name of the handler.
     44  * If properties are not defined
     45  * (or have invalid values) then the specified default values are used.
     46  * <ul>
     47  * <li>   &lt;handler-name&gt;.level
     48  *        specifies the default level for the <tt>Handler</tt>
     49  *        (defaults to <tt>Level.ALL</tt>). </li>
     50  * <li>   &lt;handler-name&gt;.filter
     51  *        specifies the name of a <tt>Filter</tt> class to use
     52  *        (defaults to no <tt>Filter</tt>). </li>
     53  * <li>   &lt;handler-name&gt;.formatter
     54  *        specifies the name of a <tt>Formatter</tt> class to use
     55  *        (defaults to <tt>java.util.logging.XMLFormatter</tt>). </li>
     56  * <li>   &lt;handler-name&gt;.encoding
     57  *        the name of the character set encoding to use (defaults to
     58  *        the default platform encoding). </li>
     59  * <li>   &lt;handler-name&gt;.host
     60  *        specifies the target host name to connect to (no default). </li>
     61  * <li>   &lt;handler-name&gt;.port
     62  *        specifies the target TCP port to use (no default). </li>
     63  * </ul>
     64  * <p>
     65  * For example, the properties for {@code SocketHandler} would be:
     66  * <ul>
     67  * <li>   java.util.logging.SocketHandler.level=INFO </li>
     68  * <li>   java.util.logging.SocketHandler.formatter=java.util.logging.SimpleFormatter </li>
     69  * </ul>
     70  * <p>
     71  * For a custom handler, e.g. com.foo.MyHandler, the properties would be:
     72  * <ul>
     73  * <li>   com.foo.MyHandler.level=INFO </li>
     74  * <li>   com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
     75  * </ul>
     76  * <p>
     77  * The output IO stream is buffered, but is flushed after each
     78  * <tt>LogRecord</tt> is written.
     79  *
     80  * @since 1.4
     81  */
     82 
     83 public class SocketHandler extends StreamHandler {
     84     private Socket sock;
     85     private String host;
     86     private int port;
     87 
     88     // Private method to configure a SocketHandler from LogManager
     89     // properties and/or default values as specified in the class
     90     // javadoc.
     91     private void configure() {
     92         LogManager manager = LogManager.getLogManager();
     93         String cname = getClass().getName();
     94 
     95         setLevel(manager.getLevelProperty(cname +".level", Level.ALL));
     96         setFilter(manager.getFilterProperty(cname +".filter", null));
     97         setFormatter(manager.getFormatterProperty(cname +".formatter", new XMLFormatter()));
     98         try {
     99             setEncoding(manager.getStringProperty(cname +".encoding", null));
    100         } catch (Exception ex) {
    101             try {
    102                 setEncoding(null);
    103             } catch (Exception ex2) {
    104                 // doing a setEncoding with null should always work.
    105                 // assert false;
    106             }
    107         }
    108         port = manager.getIntProperty(cname + ".port", 0);
    109         host = manager.getStringProperty(cname + ".host", null);
    110     }
    111 
    112 
    113     /**
    114      * Create a <tt>SocketHandler</tt>, using only <tt>LogManager</tt> properties
    115      * (or their defaults).
    116      * @throws IllegalArgumentException if the host or port are invalid or
    117      *          are not specified as LogManager properties.
    118      * @throws IOException if we are unable to connect to the target
    119      *         host and port.
    120      */
    121     public SocketHandler() throws IOException {
    122         // We are going to use the logging defaults.
    123         sealed = false;
    124         configure();
    125 
    126         try {
    127             connect();
    128         } catch (IOException ix) {
    129             System.err.println("SocketHandler: connect failed to " + host + ":" + port);
    130             throw ix;
    131         }
    132         sealed = true;
    133     }
    134 
    135     /**
    136      * Construct a <tt>SocketHandler</tt> using a specified host and port.
    137      *
    138      * The <tt>SocketHandler</tt> is configured based on <tt>LogManager</tt>
    139      * properties (or their default values) except that the given target host
    140      * and port arguments are used. If the host argument is empty, but not
    141      * null String then the localhost is used.
    142      *
    143      * @param host target host.
    144      * @param port target port.
    145      *
    146      * @throws IllegalArgumentException if the host or port are invalid.
    147      * @throws IOException if we are unable to connect to the target
    148      *         host and port.
    149      */
    150     public SocketHandler(String host, int port) throws IOException {
    151         sealed = false;
    152         configure();
    153         sealed = true;
    154         this.port = port;
    155         this.host = host;
    156         connect();
    157     }
    158 
    159     private void connect() throws IOException {
    160         // Check the arguments are valid.
    161         if (port == 0) {
    162             throw new IllegalArgumentException("Bad port: " + port);
    163         }
    164         if (host == null) {
    165             throw new IllegalArgumentException("Null host name: " + host);
    166         }
    167 
    168         if (!NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted()) {
    169             throw new IOException("Cleartext traffic not permitted");
    170         }
    171 
    172         // Try to open a new socket.
    173         sock = new Socket(host, port);
    174         OutputStream out = sock.getOutputStream();
    175         BufferedOutputStream bout = new BufferedOutputStream(out);
    176         setOutputStream(bout);
    177     }
    178 
    179     /**
    180      * Close this output stream.
    181      *
    182      * @exception  SecurityException  if a security manager exists and if
    183      *             the caller does not have <tt>LoggingPermission("control")</tt>.
    184      */
    185     @Override
    186     public synchronized void close() throws SecurityException {
    187         super.close();
    188         if (sock != null) {
    189             try {
    190                 sock.close();
    191             } catch (IOException ix) {
    192                 // drop through.
    193             }
    194         }
    195         sock = null;
    196     }
    197 
    198     /**
    199      * Format and publish a <tt>LogRecord</tt>.
    200      *
    201      * @param  record  description of the log event. A null record is
    202      *                 silently ignored and is not published
    203      */
    204     @Override
    205     public synchronized void publish(LogRecord record) {
    206         if (!isLoggable(record)) {
    207             return;
    208         }
    209         super.publish(record);
    210         flush();
    211     }
    212 }
    213