Path: blob/master/src/java.rmi/share/classes/sun/rmi/runtime/Log.java
41153 views
/*1* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.rmi.runtime;2627import java.io.ByteArrayOutputStream;28import java.io.PrintStream;29import java.io.OutputStream;30import java.lang.StackWalker.StackFrame;31import java.rmi.server.LogStream;32import java.security.PrivilegedAction;33import java.util.Set;34import java.util.logging.Handler;35import java.util.logging.SimpleFormatter;36import java.util.logging.Level;37import java.util.logging.Logger;38import java.util.logging.LogRecord;39import java.util.logging.StreamHandler;4041/**42* Utility which provides an abstract "logger" like RMI internal API43* which can be directed to use one of two types of logging44* infrastructure: the java.util.logging API or the45* java.rmi.server.LogStream API. The default behavior is to use the46* java.util.logging API. The LogStream API may be used instead by47* setting the system property sun.rmi.log.useOld to true.48*49* For backwards compatibility, supports the RMI system logging50* properties which pre-1.4 comprised the only way to configure RMI51* logging. If the java.util.logging API is used and RMI system log52* properties are set, the system properties override initial RMI53* logger values as appropriate. If the java.util.logging API is54* turned off, pre-1.4 logging behavior is used.55*56* @author Laird Dornin57* @since 1.458*/59@SuppressWarnings("deprecation")60public abstract class Log {6162/** Logger re-definition of old RMI log values */63public static final Level BRIEF = Level.FINE;64public static final Level VERBOSE = Level.FINER;6566private static final StackWalker WALKER = StackWalker.getInstance(Set.of(), 4);6768/* selects log implementation */69private static final LogFactory logFactory;70static {71@SuppressWarnings("removal")72boolean useOld = java.security.AccessController.doPrivileged(73(PrivilegedAction<Boolean>) () -> Boolean.getBoolean("sun.rmi.log.useOld"));7475/* set factory to select the logging facility to use */76logFactory = (useOld ? (LogFactory) new LogStreamLogFactory() :77(LogFactory) new LoggerLogFactory());78}7980/** "logger like" API to be used by RMI implementation */81public abstract boolean isLoggable(Level level);82public abstract void log(Level level, String message);83public abstract void log(Level level, String message, Throwable thrown);8485/** get and set the RMI server call output stream */86public abstract void setOutputStream(OutputStream stream);87public abstract PrintStream getPrintStream();8889/** factory interface enables Logger and LogStream implementations */90private static interface LogFactory {91Log createLog(String loggerName, String oldLogName, Level level);92}9394/* access log objects */9596/**97* Access log for a tri-state system property.98*99* Need to first convert override value to a log level, taking100* care to interpret a range of values between BRIEF, VERBOSE and101* SILENT.102*103* An override {@literal <} 0 is interpreted to mean that the logging104* configuration should not be overridden. The level passed to the105* factories createLog method will be null in this case.106*107* Note that if oldLogName is null and old logging is on, the108* returned LogStreamLog will ignore the override parameter - the109* log will never log messages. This permits new logs that only110* write to Loggers to do nothing when old logging is active.111*112* Do not call getLog multiple times on the same logger name.113* Since this is an internal API, no checks are made to ensure114* that multiple logs do not exist for the same logger.115*/116public static Log getLog(String loggerName, String oldLogName,117int override)118{119Level level;120121if (override < 0) {122level = null;123} else if (override == LogStream.SILENT) {124level = Level.OFF;125} else if ((override > LogStream.SILENT) &&126(override <= LogStream.BRIEF)) {127level = BRIEF;128} else if ((override > LogStream.BRIEF) &&129(override <= LogStream.VERBOSE))130{131level = VERBOSE;132} else {133level = Level.FINEST;134}135return logFactory.createLog(loggerName, oldLogName, level);136}137138/**139* Access logs associated with boolean properties140*141* Do not call getLog multiple times on the same logger name.142* Since this is an internal API, no checks are made to ensure143* that multiple logs do not exist for the same logger.144*/145public static Log getLog(String loggerName, String oldLogName,146boolean override)147{148Level level = (override ? VERBOSE : null);149return logFactory.createLog(loggerName, oldLogName, level);150}151152/**153* Factory to create Log objects which deliver log messages to the154* java.util.logging API.155*/156private static class LoggerLogFactory implements LogFactory {157LoggerLogFactory() {}158159/*160* Accessor to obtain an arbitrary RMI logger with name161* loggerName. If the level of the logger is greater than the162* level for the system property with name, the logger level163* will be set to the value of system property.164*/165public Log createLog(final String loggerName, String oldLogName,166final Level level)167{168Logger logger = Logger.getLogger(loggerName);169return new LoggerLog(logger, level);170}171}172173/**174* Class specialized to log messages to the java.util.logging API175*/176private static class LoggerLog extends Log {177178/* alternate console handler for RMI loggers */179@SuppressWarnings("removal")180private static final Handler alternateConsole =181java.security.AccessController.doPrivileged(182new java.security.PrivilegedAction<Handler>() {183public Handler run() {184InternalStreamHandler alternate =185new InternalStreamHandler(System.err);186alternate.setLevel(Level.ALL);187return alternate;188}189});190191/** handler to which messages are copied */192private InternalStreamHandler copyHandler = null;193194/* logger to which log messages are written */195private final Logger logger;196197/* used as return value of RemoteServer.getLog */198private LoggerPrintStream loggerSandwich;199200/** creates a Log which will delegate to the given logger */201@SuppressWarnings("removal")202private LoggerLog(final Logger logger, final Level level) {203this.logger = logger;204205if (level != null){206java.security.AccessController.doPrivileged(207new java.security.PrivilegedAction<Void>() {208public Void run() {209if (!logger.isLoggable(level)) {210logger.setLevel(level);211}212logger.addHandler(alternateConsole);213return null;214}215}216);217}218}219220public boolean isLoggable(Level level) {221return logger.isLoggable(level);222}223224public void log(Level level, String message) {225if (isLoggable(level)) {226StackFrame sourceFrame = getSource();227logger.logp(level, sourceFrame.getClassName(), sourceFrame.getMethodName(),228Thread.currentThread().getName() + ": " + message);229}230}231232public void log(Level level, String message, Throwable thrown) {233if (isLoggable(level)) {234StackFrame sourceFrame = getSource();235logger.logp(level, sourceFrame.getClassName(), sourceFrame.getMethodName(),236Thread.currentThread().getName() + ": " +237message, thrown);238}239}240241public String toString() {242return logger.toString() + ", level: " + logger.getLevel() +243", name: " + logger.getName();244}245246/**247* Set the output stream associated with the RMI server call248* logger.249*250* Calling code needs LoggingPermission "control".251*/252public synchronized void setOutputStream(OutputStream out) {253if (out != null) {254if (!logger.isLoggable(VERBOSE)) {255logger.setLevel(VERBOSE);256}257copyHandler = new InternalStreamHandler(out);258copyHandler.setLevel(Log.VERBOSE);259logger.addHandler(copyHandler);260} else {261/* ensure that messages are not logged */262if (copyHandler != null) {263logger.removeHandler(copyHandler);264}265copyHandler = null;266}267}268269public synchronized PrintStream getPrintStream() {270if (loggerSandwich == null) {271loggerSandwich = new LoggerPrintStream(logger);272}273return loggerSandwich;274}275}276277/**278* Subclass of StreamHandler for redirecting log output. flush279* must be called in the publish and close methods.280*/281private static class InternalStreamHandler extends StreamHandler {282InternalStreamHandler(OutputStream out) {283super(out, new SimpleFormatter());284}285286public void publish(LogRecord record) {287super.publish(record);288flush();289}290291public void close() {292flush();293}294}295296/**297* PrintStream which forwards log messages to the logger. Class298* is needed to maintain backwards compatibility with299* RemoteServer.{set|get}Log().300*/301private static class LoggerPrintStream extends PrintStream {302303/** logger where output of this log is sent */304private final Logger logger;305306/** record the last character written to this stream */307private int last = -1;308309/** stream used for buffering lines */310private final ByteArrayOutputStream bufOut;311312private LoggerPrintStream(Logger logger)313{314super(new ByteArrayOutputStream());315bufOut = (ByteArrayOutputStream) super.out;316this.logger = logger;317}318319public void write(int b) {320if ((last == '\r') && (b == '\n')) {321last = -1;322return;323} else if ((b == '\n') || (b == '\r')) {324try {325/* write the converted bytes of the log message */326String message =327Thread.currentThread().getName() + ": " +328bufOut.toString();329logger.logp(Level.INFO, "LogStream", "print", message);330} finally {331bufOut.reset();332}333} else {334super.write(b);335}336last = b;337}338339public void write(byte b[], int off, int len) {340if (len < 0) {341throw new ArrayIndexOutOfBoundsException(len);342}343for (int i = 0; i < len; i++) {344write(b[off + i]);345}346}347348public String toString() {349return "RMI";350}351}352353/**354* Factory to create Log objects which deliver log messages to the355* java.rmi.server.LogStream API356*/357private static class LogStreamLogFactory implements LogFactory {358LogStreamLogFactory() {}359360/* create a new LogStreamLog for the specified log */361public Log createLog(String loggerName, String oldLogName,362Level level)363{364LogStream stream = null;365if (oldLogName != null) {366stream = LogStream.log(oldLogName);367}368return new LogStreamLog(stream, level);369}370}371372/**373* Class specialized to log messages to the374* java.rmi.server.LogStream API375*/376private static class LogStreamLog extends Log {377/** Log stream to which log messages are written */378private final LogStream stream;379380/** the level of the log as set by associated property */381private int levelValue = Level.OFF.intValue();382383private LogStreamLog(LogStream stream, Level level) {384if ((stream != null) && (level != null)) {385/* if the stream or level is null, don't log any386* messages387*/388levelValue = level.intValue();389}390this.stream = stream;391}392393public synchronized boolean isLoggable(Level level) {394return (level.intValue() >= levelValue);395}396397public void log(Level messageLevel, String message) {398if (isLoggable(messageLevel)) {399StackFrame sourceFrame = getSource();400stream.println(unqualifiedName(sourceFrame.getClassName()) +401"." + sourceFrame.getMethodName() + ": " + message);402}403}404405public void log(Level level, String message, Throwable thrown) {406if (isLoggable(level)) {407/*408* keep output contiguous and maintain the contract of409* RemoteServer.getLog410*/411synchronized (stream) {412StackFrame sourceFrame = getSource();413stream.println(unqualifiedName(sourceFrame.getClassName()) + "." +414sourceFrame.getMethodName() + ": " + message);415thrown.printStackTrace(stream);416}417}418}419420public PrintStream getPrintStream() {421return stream;422}423424public synchronized void setOutputStream(OutputStream out) {425if (out != null) {426if (VERBOSE.intValue() < levelValue) {427levelValue = VERBOSE.intValue();428}429stream.setOutputStream(out);430} else {431/* ensure that messages are not logged */432levelValue = Level.OFF.intValue();433}434}435436/*437* Mimic old log messages that only contain unqualified names.438*/439private static String unqualifiedName(String name) {440int lastDot = name.lastIndexOf('.');441if (lastDot >= 0) {442name = name.substring(lastDot + 1);443}444name = name.replace('$', '.');445return name;446}447}448449/**450* Obtain stack frame of code calling a log method.451*/452private static StackFrame getSource() {453return WALKER.walk(s -> s454.skip(3)455.findFirst()456.get());457}458}459460461