SimpleLog.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.logging.impl;

  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.PrintWriter;
  21. import java.io.Serializable;
  22. import java.io.StringWriter;
  23. import java.security.AccessController;
  24. import java.security.PrivilegedAction;
  25. import java.text.DateFormat;
  26. import java.text.SimpleDateFormat;
  27. import java.util.Date;
  28. import java.util.Locale;
  29. import java.util.Objects;
  30. import java.util.Properties;

  31. import org.apache.commons.logging.Log;
  32. import org.apache.commons.logging.LogConfigurationException;

  33. /**
  34.  * Simple implementation of Log that sends all enabled log messages,
  35.  * for all defined loggers, to System.err.  The following system properties
  36.  * are supported to configure the behavior of this logger:
  37.  * <ul>
  38.  * <li>{@code org.apache.commons.logging.simplelog.defaultlog} -
  39.  *     Default logging detail level for all instances of SimpleLog.
  40.  *     Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
  41.  *     If not specified, defaults to "info". </li>
  42.  * <li>{@code org.apache.commons.logging.simplelog.log.xxxxx} -
  43.  *     Logging detail level for a SimpleLog instance named "xxxxx".
  44.  *     Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
  45.  *     If not specified, the default logging detail level is used.</li>
  46.  * <li>{@code org.apache.commons.logging.simplelog.showlogname} -
  47.  *     Set to {@code true} if you want the Log instance name to be
  48.  *     included in output messages. Defaults to {@code false}.</li>
  49.  * <li>{@code org.apache.commons.logging.simplelog.showShortLogname} -
  50.  *     Set to {@code true} if you want the last component of the name to be
  51.  *     included in output messages. Defaults to {@code true}.</li>
  52.  * <li>{@code org.apache.commons.logging.simplelog.showdatetime} -
  53.  *     Set to {@code true} if you want the current date and time
  54.  *     to be included in output messages. Default is {@code false}.</li>
  55.  * <li>{@code org.apache.commons.logging.simplelog.dateTimeFormat} -
  56.  *     The date and time format to be used in the output messages.
  57.  *     The pattern describing the date and time format is the same that is
  58.  *     used in {@link java.text.SimpleDateFormat}. If the format is not
  59.  *     specified or is invalid, the default format is used.
  60.  *     The default format is {@code yyyy/MM/dd HH:mm:ss:SSS zzz}.</li>
  61.  * </ul>
  62.  * <p>
  63.  * In addition to looking for system properties with the names specified
  64.  * above, this implementation also checks for a class loader resource named
  65.  * {@code "simplelog.properties"}, and includes any matching definitions
  66.  * from this resource (if it exists).
  67.  * </p>
  68.  */
  69. public class SimpleLog implements Log, Serializable {

  70.     /** Serializable version identifier. */
  71.     private static final long serialVersionUID = 136942970684951178L;

  72.     /** All system properties used by {@code SimpleLog} start with this */
  73.     static protected final String systemPrefix = "org.apache.commons.logging.simplelog.";

  74.     /** Properties loaded from simplelog.properties */
  75.     static protected final Properties simpleLogProps = new Properties();

  76.     /** The default format to use when formating dates */
  77.     static protected final String DEFAULT_DATE_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss:SSS zzz";

  78.     /** Include the instance name in the log message? */
  79.     static volatile protected boolean showLogName;

  80.     /**
  81.      * Include the short name (last component) of the logger in the log
  82.      * message. Defaults to true - otherwise we'll be lost in a flood of
  83.      * messages without knowing who sends them.
  84.      */
  85.     static volatile protected boolean showShortName = true;

  86.     /** Include the current time in the log message */
  87.     static volatile protected boolean showDateTime;

  88.     /** The date and time format to use in the log message */
  89.     static volatile protected String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;

  90.     /**
  91.      * Used to format times.
  92.      * <p>
  93.      * Any code that accesses this object should first obtain a lock on it,
  94.      * that is, use synchronized(dateFormatter); this requirement was introduced
  95.      * in 1.1.1 to fix an existing thread safety bug (SimpleDateFormat.format
  96.      * is not thread-safe).
  97.      * </p>
  98.      */
  99.     static protected DateFormat dateFormatter;

  100.     /** "Trace" level logging. */
  101.     public static final int LOG_LEVEL_TRACE  = 1;
  102.     /** "Debug" level logging. */
  103.     public static final int LOG_LEVEL_DEBUG  = 2;
  104.     /** "Info" level logging. */
  105.     public static final int LOG_LEVEL_INFO   = 3;
  106.     /** "Warn" level logging. */
  107.     public static final int LOG_LEVEL_WARN   = 4;
  108.     /** "Error" level logging. */
  109.     public static final int LOG_LEVEL_ERROR  = 5;
  110.     /** "Fatal" level logging. */
  111.     public static final int LOG_LEVEL_FATAL  = 6;

  112.     /** Enable all logging levels */
  113.     public static final int LOG_LEVEL_ALL    = LOG_LEVEL_TRACE - 1;

  114.     /** Enable no logging levels */
  115.     public static final int LOG_LEVEL_OFF    = LOG_LEVEL_FATAL + 1;

  116.     // Initialize class attributes.
  117.     // Load properties file, if found.
  118.     // Override with system properties.
  119.     static {
  120.         // Add props from the resource simplelog.properties
  121.         try (InputStream in = getResourceAsStream("simplelog.properties")) {
  122.             if (null != in) {
  123.                 simpleLogProps.load(in);
  124.             }
  125.         } catch (final IOException ignore) {
  126.             // Ignore
  127.         }

  128.         showLogName = getBooleanProperty(systemPrefix + "showlogname", showLogName);
  129.         showShortName = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
  130.         showDateTime = getBooleanProperty(systemPrefix + "showdatetime", showDateTime);

  131.         if (showDateTime) {
  132.             dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat", dateTimeFormat);
  133.             try {
  134.                 dateFormatter = new SimpleDateFormat(dateTimeFormat);
  135.             } catch (final IllegalArgumentException e) {
  136.                 // If the format pattern is invalid - use the default format
  137.                 dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
  138.                 dateFormatter = new SimpleDateFormat(dateTimeFormat);
  139.             }
  140.         }
  141.     }

  142.     private static boolean getBooleanProperty(final String name, final boolean defaultValue) {
  143.         final String prop = getStringProperty(name);
  144.         return prop == null ? defaultValue : Boolean.parseBoolean(prop);
  145.     }

  146.     /**
  147.      * Gets the thread context class loader if available. Otherwise return null.
  148.      *
  149.      * The thread context class loader is available if certain security conditions are met.
  150.      *
  151.      * @throws LogConfigurationException if a suitable class loader cannot be identified.
  152.      */
  153.     private static ClassLoader getContextClassLoader() {
  154.         ClassLoader classLoader = null;

  155.         // Get the thread context class loader (if there is one)
  156.         try {
  157.             classLoader = Thread.currentThread().getContextClassLoader();
  158.         } catch (final RuntimeException e) {
  159.             /**
  160.              * getContextClassLoader() throws SecurityException when the context class loader isn't an ancestor of the calling class's class loader, or if
  161.              * security permissions are restricted.
  162.              *
  163.              * In the first case (not related), we want to ignore and keep going. We cannot help but also ignore the second with the logic below, but other
  164.              * calls elsewhere (to obtain a class loader) will trigger this exception where we can make a distinction.
  165.              */
  166.             // Capture 'e.getTargetException()' exception for details
  167.             // alternate: log 'e.getTargetException()', and pass back 'e'.
  168.             if (!(e instanceof SecurityException)) {
  169.                 throw new LogConfigurationException("Unexpected SecurityException", e);
  170.             }
  171.         }

  172.         if (classLoader == null) {
  173.             classLoader = SimpleLog.class.getClassLoader();
  174.         }

  175.         // Return the selected class loader
  176.         return classLoader;
  177.     }

  178.     private static InputStream getResourceAsStream(final String name) {
  179.         return AccessController.doPrivileged((PrivilegedAction<InputStream>) () -> {
  180.             final ClassLoader threadCL = getContextClassLoader();
  181.             if (threadCL != null) {
  182.                 return threadCL.getResourceAsStream(name);
  183.             }
  184.             return ClassLoader.getSystemResourceAsStream(name);
  185.         });
  186.     }

  187.     private static String getStringProperty(final String name) {
  188.         String prop = null;
  189.         try {
  190.             prop = System.getProperty(name);
  191.         } catch (final SecurityException e) {
  192.             // Ignore
  193.         }
  194.         return prop == null ? simpleLogProps.getProperty(name) : prop;
  195.     }
  196.     private static String getStringProperty(final String name, final String defaultValue) {
  197.         final String prop = getStringProperty(name);
  198.         return prop == null ? defaultValue : prop;
  199.     }
  200.     /** The name of this simple log instance */
  201.     protected volatile String logName;

  202.     /** The current log level */
  203.     protected volatile int currentLogLevel;

  204.     /** The short name of this simple log instance */
  205.     private volatile String shortLogName;

  206.     /**
  207.      * Constructs a simple log with given name.
  208.      *
  209.      * @param name log name
  210.      */
  211.     public SimpleLog(String name) {
  212.         logName = name;

  213.         // Set initial log level
  214.         // Used to be: set default log level to ERROR
  215.         // IMHO it should be lower, but at least info (costin).
  216.         setLevel(LOG_LEVEL_INFO);

  217.         // Set log level from properties
  218.         String level = getStringProperty(systemPrefix + "log." + logName);
  219.         int i = String.valueOf(name).lastIndexOf(".");
  220.         while (level == null && i > -1) {
  221.             name = name.substring(0, i);
  222.             level = getStringProperty(systemPrefix + "log." + name);
  223.             i = String.valueOf(name).lastIndexOf(".");
  224.         }

  225.         if (level == null) {
  226.             level = getStringProperty(systemPrefix + "defaultlog");
  227.         }
  228.         if (level != null) {
  229.             level = level.toLowerCase(Locale.ROOT);
  230.         }
  231.         if (level != null) {
  232.             switch (level) {
  233.             case "all":
  234.                 setLevel(LOG_LEVEL_ALL);
  235.                 break;
  236.             case "trace":
  237.                 setLevel(LOG_LEVEL_TRACE);
  238.                 break;
  239.             case "debug":
  240.                 setLevel(LOG_LEVEL_DEBUG);
  241.                 break;
  242.             case "info":
  243.                 setLevel(LOG_LEVEL_INFO);
  244.                 break;
  245.             case "warn":
  246.                 setLevel(LOG_LEVEL_WARN);
  247.                 break;
  248.             case "error":
  249.                 setLevel(LOG_LEVEL_ERROR);
  250.                 break;
  251.             case "fatal":
  252.                 setLevel(LOG_LEVEL_FATAL);
  253.                 break;
  254.             case "off":
  255.                 setLevel(LOG_LEVEL_OFF);
  256.                 break;
  257.             default:
  258.                 // do nothing
  259.             }
  260.         }
  261.     }

  262.     /**
  263.      * Logs a message with
  264.      * {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG}.
  265.      *
  266.      * @param message to log
  267.      * @see org.apache.commons.logging.Log#debug(Object)
  268.      */
  269.     @Override
  270.     public final void debug(final Object message) {
  271.         if (isLevelEnabled(LOG_LEVEL_DEBUG)) {
  272.             log(LOG_LEVEL_DEBUG, message, null);
  273.         }
  274.     }

  275.     /**
  276.      * Logs a message with
  277.      * {@code org.apache.commons.logging.impl.LOG_LEVEL_DEBUG}.
  278.      *
  279.      * @param message to log
  280.      * @param t log this cause
  281.      * @see org.apache.commons.logging.Log#debug(Object, Throwable)
  282.      */
  283.     @Override
  284.     public final void debug(final Object message, final Throwable t) {
  285.         if (isLevelEnabled(LOG_LEVEL_DEBUG)) {
  286.             log(LOG_LEVEL_DEBUG, message, t);
  287.         }
  288.     }

  289.     /**
  290.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR}.
  291.      *
  292.      * @param message to log
  293.      * @see org.apache.commons.logging.Log#error(Object)
  294.      */
  295.     @Override
  296.     public final void error(final Object message) {
  297.         if (isLevelEnabled(LOG_LEVEL_ERROR)) {
  298.             log(LOG_LEVEL_ERROR, message, null);
  299.         }
  300.     }

  301.     /**
  302.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR}.
  303.      *
  304.      * @param message to log
  305.      * @param t log this cause
  306.      * @see org.apache.commons.logging.Log#error(Object, Throwable)
  307.      */
  308.     @Override
  309.     public final void error(final Object message, final Throwable t) {
  310.         if (isLevelEnabled(LOG_LEVEL_ERROR)) {
  311.             log(LOG_LEVEL_ERROR, message, t);
  312.         }
  313.     }

  314.     /**
  315.      * Log a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL}.
  316.      *
  317.      * @param message to log
  318.      * @see org.apache.commons.logging.Log#fatal(Object)
  319.      */
  320.     @Override
  321.     public final void fatal(final Object message) {
  322.         if (isLevelEnabled(LOG_LEVEL_FATAL)) {
  323.             log(LOG_LEVEL_FATAL, message, null);
  324.         }
  325.     }

  326.     /**
  327.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL}.
  328.      *
  329.      * @param message to log
  330.      * @param t log this cause
  331.      * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
  332.      */
  333.     @Override
  334.     public final void fatal(final Object message, final Throwable t) {
  335.         if (isLevelEnabled(LOG_LEVEL_FATAL)) {
  336.             log(LOG_LEVEL_FATAL, message, t);
  337.         }
  338.     }

  339.     /**
  340.      * Gets logging level.
  341.      *
  342.      * @return  logging level.
  343.      */
  344.     public int getLevel() {
  345.         return currentLogLevel;
  346.     }

  347.     /**
  348.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO}.
  349.      *
  350.      * @param message to log
  351.      * @see org.apache.commons.logging.Log#info(Object)
  352.      */
  353.     @Override
  354.     public final void info(final Object message) {
  355.         if (isLevelEnabled(LOG_LEVEL_INFO)) {
  356.             log(LOG_LEVEL_INFO,message,null);
  357.         }
  358.     }

  359.     /**
  360.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO}.
  361.      *
  362.      * @param message to log
  363.      * @param t log this cause
  364.      * @see org.apache.commons.logging.Log#info(Object, Throwable)
  365.      */
  366.     @Override
  367.     public final void info(final Object message, final Throwable t) {
  368.         if (isLevelEnabled(LOG_LEVEL_INFO)) {
  369.             log(LOG_LEVEL_INFO, message, t);
  370.         }
  371.     }

  372.     /**
  373.      * Tests whether debug messages are enabled.
  374.      * <p>
  375.      * This allows expensive operations such as {@code String}
  376.      * concatenation to be avoided when the message will be ignored by the
  377.      * logger.
  378.      * </p>
  379.      */
  380.     @Override
  381.     public final boolean isDebugEnabled() {
  382.         return isLevelEnabled(LOG_LEVEL_DEBUG);
  383.     }

  384.     /**
  385.      * Tests whether error messages are enabled.
  386.      * <p>
  387.      * This allows expensive operations such as {@code String}
  388.      * concatenation to be avoided when the message will be ignored by the
  389.      * logger.
  390.      * </p>
  391.      */
  392.     @Override
  393.     public final boolean isErrorEnabled() {
  394.         return isLevelEnabled(LOG_LEVEL_ERROR);
  395.     }

  396.     /**
  397.      * Tests whether fatal messages are enabled.
  398.      * <p>
  399.      * This allows expensive operations such as {@code String}
  400.      * concatenation to be avoided when the message will be ignored by the
  401.      * logger.
  402.      * </p>
  403.      */
  404.     @Override
  405.     public final boolean isFatalEnabled() {
  406.         return isLevelEnabled(LOG_LEVEL_FATAL);
  407.     }

  408.     /**
  409.      * Tests whether info messages are enabled.
  410.      * <p>
  411.      * This allows expensive operations such as {@code String}
  412.      * concatenation to be avoided when the message will be ignored by the
  413.      * logger.
  414.      * </p>
  415.      */
  416.     @Override
  417.     public final boolean isInfoEnabled() {
  418.         return isLevelEnabled(LOG_LEVEL_INFO);
  419.     }

  420.     /**
  421.      * Tests whether the given level is enabled.
  422.      *
  423.      * @param logLevel is this level enabled?
  424.      * @return whether the given log level currently enabled.
  425.      */
  426.     protected boolean isLevelEnabled(final int logLevel) {
  427.         // log level are numerically ordered so can use simple numeric
  428.         // comparison
  429.         return logLevel >= currentLogLevel;
  430.     }

  431.     /**
  432.      * Tests whether trace messages are enabled.
  433.      * <p>
  434.      * This allows expensive operations such as {@code String}
  435.      * concatenation to be avoided when the message will be ignored by the
  436.      * logger.
  437.      * </p>
  438.      */
  439.     @Override
  440.     public final boolean isTraceEnabled() {
  441.         return isLevelEnabled(LOG_LEVEL_TRACE);
  442.     }

  443.     /**
  444.      * Tests whether warn messages are enabled.
  445.      * <p>
  446.      * This allows expensive operations such as {@code String}
  447.      * concatenation to be avoided when the message will be ignored by the
  448.      * logger.
  449.      * </p>
  450.      */
  451.     @Override
  452.     public final boolean isWarnEnabled() {
  453.         return isLevelEnabled(LOG_LEVEL_WARN);
  454.     }

  455.     /**
  456.      * Do the actual logging.
  457.      * <p>
  458.      * This method assembles the message and then calls {@code write()}
  459.      * to cause it to be written.
  460.      * </p>
  461.      *
  462.      * @param type One of the LOG_LEVEL_XXX constants defining the log level
  463.      * @param message The message itself (typically a String)
  464.      * @param t The exception whose stack trace should be logged
  465.      */
  466.     protected void log(final int type, final Object message, final Throwable t) {
  467.         // Use a string buffer for better performance
  468.         final StringBuilder buf = new StringBuilder();

  469.         // Append date-time if so configured
  470.         if (showDateTime) {
  471.             final Date now = new Date();
  472.             String dateText;
  473.             synchronized (dateFormatter) {
  474.                 dateText = dateFormatter.format(now);
  475.             }
  476.             buf.append(dateText);
  477.             buf.append(" ");
  478.         }

  479.         // Append a readable representation of the log level
  480.         switch (type) {
  481.         case LOG_LEVEL_TRACE:
  482.             buf.append("[TRACE] ");
  483.             break;
  484.         case LOG_LEVEL_DEBUG:
  485.             buf.append("[DEBUG] ");
  486.             break;
  487.         case LOG_LEVEL_INFO:
  488.             buf.append("[INFO] ");
  489.             break;
  490.         case LOG_LEVEL_WARN:
  491.             buf.append("[WARN] ");
  492.             break;
  493.         case LOG_LEVEL_ERROR:
  494.             buf.append("[ERROR] ");
  495.             break;
  496.         case LOG_LEVEL_FATAL:
  497.             buf.append("[FATAL] ");
  498.             break;
  499.         default:
  500.             // Or throw?
  501.             buf.append("[UNDEFINED] ");
  502.             break;
  503.         }

  504.         // Append the name of the log instance if so configured
  505.         if (showShortName) {
  506.             if (shortLogName == null) {
  507.                 // Cut all but the last component of the name for both styles
  508.                 final String slName = logName.substring(logName.lastIndexOf(".") + 1);
  509.                 shortLogName = slName.substring(slName.lastIndexOf("/") + 1);
  510.             }
  511.             buf.append(String.valueOf(shortLogName)).append(" - ");
  512.         } else if (showLogName) {
  513.             buf.append(String.valueOf(logName)).append(" - ");
  514.         }

  515.         // Append the message
  516.         buf.append(String.valueOf(message));

  517.         // Append stack trace if not null
  518.         if (t != null) {
  519.             buf.append(" <");
  520.             buf.append(t.toString());
  521.             buf.append(">");

  522.             final StringWriter sw = new StringWriter(1024);
  523.             try (PrintWriter pw = new PrintWriter(sw)) {
  524.                 t.printStackTrace(pw);
  525.             }
  526.             buf.append(sw.toString());
  527.         }

  528.         // Print to the appropriate destination
  529.         write(buf);
  530.     }

  531.     /**
  532.      * Sets logging level.
  533.      *
  534.      * @param currentLogLevel new logging level
  535.      */
  536.     public void setLevel(final int currentLogLevel) {
  537.         this.currentLogLevel = currentLogLevel;
  538.     }

  539.     /**
  540.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE}.
  541.      *
  542.      * @param message to log
  543.      * @see org.apache.commons.logging.Log#trace(Object)
  544.      */
  545.     @Override
  546.     public final void trace(final Object message) {
  547.         if (isLevelEnabled(LOG_LEVEL_TRACE)) {
  548.             log(LOG_LEVEL_TRACE, message, null);
  549.         }
  550.     }

  551.     /**
  552.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE}.
  553.      *
  554.      * @param message to log
  555.      * @param t log this cause
  556.      * @see org.apache.commons.logging.Log#trace(Object, Throwable)
  557.      */
  558.     @Override
  559.     public final void trace(final Object message, final Throwable t) {
  560.         if (isLevelEnabled(LOG_LEVEL_TRACE)) {
  561.             log(LOG_LEVEL_TRACE, message, t);
  562.         }
  563.     }

  564.     /**
  565.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN}.
  566.      *
  567.      * @param message to log
  568.      * @see org.apache.commons.logging.Log#warn(Object)
  569.      */
  570.     @Override
  571.     public final void warn(final Object message) {
  572.         if (isLevelEnabled(LOG_LEVEL_WARN)) {
  573.             log(LOG_LEVEL_WARN, message, null);
  574.         }
  575.     }

  576.     /**
  577.      * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN}.
  578.      *
  579.      * @param message to log
  580.      * @param t log this cause
  581.      * @see org.apache.commons.logging.Log#warn(Object, Throwable)
  582.      */
  583.     @Override
  584.     public final void warn(final Object message, final Throwable t) {
  585.         if (isLevelEnabled(LOG_LEVEL_WARN)) {
  586.             log(LOG_LEVEL_WARN, message, t);
  587.         }
  588.     }

  589.     /**
  590.      * Writes the content of the message accumulated in the specified
  591.      * {@code StringBuffer} to the appropriate output destination.  The
  592.      * default implementation writes to {@code System.err}.
  593.      *
  594.      * @param buffer A {@code StringBuffer} containing the accumulated
  595.      *  text to be logged
  596.      */
  597.     private void write(final Object buffer) {
  598.         System.err.println(Objects.toString(buffer));
  599.     }

  600.     /**
  601.      * Writes the content of the message accumulated in the specified
  602.      * {@code StringBuffer} to the appropriate output destination.  The
  603.      * default implementation writes to {@code System.err}.
  604.      *
  605.      * @param buffer A {@code StringBuffer} containing the accumulated
  606.      *  text to be logged
  607.      */
  608.     protected void write(final StringBuffer buffer) {
  609.         System.err.println(Objects.toString(buffer));
  610.     }
  611. }