Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java
40948 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.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package nsk.share;2425import java.io.BufferedReader;26import java.io.ByteArrayOutputStream;27import java.io.IOException;28import java.io.PrintStream;29import java.io.PrintWriter;30import java.io.StringReader;31import java.util.Enumeration;32import java.util.HashMap;33import java.util.Map;34import java.util.Set;35import java.util.HashSet;36import java.util.Vector;3738import nsk.share.test.LazyFormatString;3940/**41* This class helps to print test-execution trace messages42* and filter them when execution mode is not verbose.43* <p>44* Verbose mode if defined by providing <i>-verbose</i> command line45* option, handled by <code>ArgumentParser</code>. Use <code>verbose()</code>46* method to determine which mode is used.47* <p>48* <code>Log</code> provides with two main methods to print messages:49* <ul>50* <li> <code>complain(String)</code> - to print error message51* <li> <code>display(String)</code> - to print additional log message52* </ul>53* No other way to print messages to the log stream should be used.54* <p>55* Error messages appeares in log stream in all modes. Additional log massages,56* printed with <code>display()</code> method will be filtered out, if log mode57* is not verbose. In verbose log made messages of both types are printed.58* Additionally, in verbose mode a summary of all occured errors will be printed59* at the program exit, by automatically invoking method60* <code>printErrorsSummary()</code>.61* <p>62* To provide printing messages from different sources into one log63* with distinct prefixes use internal <code>Log.Logger</code> class.64*65* @see #verbose()66* @see #complain(String)67* @see #display(String)68* @see ArgumentParser69* @see Log.Logger70*/71public class Log extends FinalizableObject {72/**73* Report step-by-step activity to this stream.74*75* @deprecated Tests should not use this field directly.76*/77@Deprecated78protected PrintStream out = null;7980/**81* Is log-mode verbose?82* Default value is <code>false</code>.83*/84private boolean verbose = false;8586/**87* Should log messages prefixed with timestamps?88* Default value is <code>false</code>.89*/90private boolean timestamp = false;9192/**93* Names for trace levels94*/95public static final class TraceLevel {96public static final int TRACE_NONE = 0;97public static final int TRACE_IMPORTANT = 1;98public static final int TRACE_NORMAL = 2;99public static final int TRACE_VERBOSE = 3;100public static final int TRACE_DEBUG = 4;101102public static final int DEFAULT = TRACE_IMPORTANT;103104public static final Map<String, Integer> NAME_TO_LEVEL_MAP = new HashMap<String, Integer>();105static {106NAME_TO_LEVEL_MAP.put("none", TRACE_NONE);107NAME_TO_LEVEL_MAP.put("important", TRACE_IMPORTANT);108NAME_TO_LEVEL_MAP.put("info", TRACE_NORMAL);109NAME_TO_LEVEL_MAP.put("verbose", TRACE_VERBOSE);110NAME_TO_LEVEL_MAP.put("debug", TRACE_DEBUG);111NAME_TO_LEVEL_MAP.put("default", DEFAULT);112}113114public static int nameToLevel(String value) throws IllegalArgumentException {115Integer level = NAME_TO_LEVEL_MAP.get(value.toLowerCase());116if ( level == null )117throw new IllegalArgumentException("Wrong trace level: " + value);118119return level;120}121122public static String getLevelsString() {123StringBuffer result = new StringBuffer();124for ( String s : NAME_TO_LEVEL_MAP.keySet() ) {125result.append(s).append(", ");126}127return result.substring(0, result.length() - 3);128}129}130131/**132* Threshold value for printing trace messages for debugging purpose.133* Default value is <code>0</code> a.k.a. <code>TraceLevel.INFO</code>;134*/135private int traceLevel = TraceLevel.DEFAULT;136137/**138* Is printing errors summary enabled? Default value is <code>true</code>;139*/140private boolean errorsSummaryEnabled = true;141142/**143* Is printing saved verbose messages on error enabled? Default value is <code>true</code>;144*/145private boolean verboseOnErrorEnabled = true;146147/**148* This <code>errosBuffer</code> will keep all messages printed via149* <code>complain()</code> method for final summary output.150* Ensure that buffer has enough room for messages to keep,151* to minimize probability or OutOfMemory error while keeping152* an error message in stress tests.153*/154private Vector<String> errorsBuffer = new Vector<String>(1000);155156/**157* Most tests in nsk do not log exceptions, they only log an error message.158* This makes failure analysis harder.159* To solve this we will automatically generate Exceptions for error logs.160* To not log too many Exceptions, we try to log each unique error only once.161* <code>loggedExceptions</code> contains all messages that have already been logged.162*/163private Set<String> loggedExceptions = new HashSet<String>();164165/**166* This <code>logBuffer</code> will keep all messages printed via167* <code>display()</code> method in non-verbose mode until168* swithching verbose mode on or invoking <code>complain()</code>.169* Ensure that buffer has enough room for messages to keep,170* to minimize probability or OutOfMemory error while keeping171* an error message in stress tests.172*/173private Vector<String> logBuffer = new Vector<String>(1000);174175/**176* Did I already warned if output stream is not assigned?177*/178private boolean noOutWarned = false;179180/////////////////////////////////////////////////////////////////181182/**183* Create new Log's only with <code>Log(out)</code> or with184* <code>Log(out,argsHandler)</code> constructors.185*186* @deprecated Extending test class with Log is obsolete.187*/188@Deprecated189protected Log() {190// install finalizer to print errors summary at exit191Finalizer finalizer = new Finalizer(this);192finalizer.activate();193194// Don't log exceptions from this method. It would just add unnecessary logs.195loggedExceptions.add("nsk.share.jdi.SerialExecutionDebugger.executeTests");196}197198/**199* Incarnate new Log for the given <code>stream</code> and200* for non-verbose mode.201*/202public Log(PrintStream stream) {203this();204out = stream;205}206207/**208* Incarnate new Log for the given <code>stream</code>; and209* either for verbose or for non-verbose mode accordingly to210* the given <code>verbose</code> key.211*/212public Log(PrintStream stream, boolean verbose) {213this(stream);214this.verbose = verbose;215}216217/**218* Incarnate new Log for the given <code>stream</code>; and219* either for verbose or for non-verbose mode accordingly to220* the given <code>argsHandler</code>.221*/222public Log(PrintStream stream, ArgumentParser argsParser) {223this(stream, argsParser.verbose());224traceLevel = argsParser.getTraceLevel();225timestamp = argsParser.isTimestamp();226}227228/////////////////////////////////////////////////////////////////229230/**231* Return <i>true</i> if log mode is verbose.232*/233public boolean verbose() {234return verbose;235}236237/**238* Return <i>true</i> if printing errors summary at exit is enabled.239*/240public boolean isErrorsSummaryEnabled() {241return errorsSummaryEnabled;242}243244/**245* Enable or disable printing errors summary at exit.246*/247public void enableErrorsSummary(boolean enable) {248errorsSummaryEnabled = enable;249}250251/**252* Return <i>true</i> if printing saved verbose messages on error is enabled.253*/254public boolean isVerboseOnErrorEnabled() {255return errorsSummaryEnabled;256}257258/**259* Enable or disable printing saved verbose messages on error.260*/261public void enableVerboseOnError(boolean enable) {262verboseOnErrorEnabled = enable;263}264265/**266* Enable or disable verbose mode for printing messages.267*/268public void enableVerbose(boolean enable) {269if (!verbose) {270flushLogBuffer();271}272verbose = enable;273}274275public int getTraceLevel() {276return traceLevel;277}278279/**280* Set threshold for printing trace messages.281* Warning: trace level changes may NOT be observed by other threads immediately.282*/283public void setTraceLevel(int level) {284traceLevel = level;285}286287/**288* Return output stream of this <code>Log</code> object.289*/290public PrintStream getOutStream() {291return out;292}293294/**295* Returns a string that contains prefix concatenated296* with Throwable.printStackTrace() output.297*/298public static String printExceptionToString(Object prefix, Throwable exception) {299ByteArrayOutputStream bos = new ByteArrayOutputStream();300PrintWriter pw = new PrintWriter(bos);301pw.println(prefix);302exception.printStackTrace(pw);303pw.close();304return bos.toString();305}306307/**308* Print <code>message</code> to the assigned output stream.309*310* @deprecated Test ought to be quiet if log mode is non-verbose311* and there is no errors found by the test. Methods312* <code>display()</code> and <code>complain()</code>313* are enough for testing purposes.314*/315@Deprecated316public synchronized void println(String message) {317doPrint(message);318if (!verbose() && isVerboseOnErrorEnabled()) {319keepLog(composeLine(message));320}321}322323/**324* Print <code>message</code> to the assigned output stream,325* if log mode is <i>non</i>-verbose.326*327* @deprecated Test ought to be quiet if log mode is non-verbose328* and there is no errors found by the test. Methods329* <code>display()</code> and <code>complain()</code>330* are enough for testing purposes.331*/332@Deprecated333public synchronized void comment(String message) {334if (!verbose()) {335doPrint(message);336}337}338339/**340* Print trace <code>message</code> to the assigned output stream,341* only if specified <code>level</code> is less or equal for the342* trace level specified in command line by <code>-trace.level</code>343* option.344*/345public void trace(int level, Object message) {346if (level <= traceLevel) {347synchronized ( this ) {348doPrint("### TRACE " + level + ": " + message);349}350}351}352/**353* Print trace <code>message</code> and <code>exception</code> to354* the assigned output stream,355* only if specified <code>level</code> is less or equal for the356* trace level specified in command line by <code>-trace.level</code>357* option.358*/359public void trace(int level, Object message, Throwable exception) {360if (level <= traceLevel) {361trace(level, printExceptionToString(message, exception));362}363}364365/**366* Print <code>message</code> to the assigned output stream,367* if log mode is verbose. The <code>message</code> will be lost,368* if execution mode is non-verbose, and there is no error messages369* printed.370*/371public synchronized void display(Object message) {372if (verbose()) {373doPrint(message.toString());374} else if (isVerboseOnErrorEnabled()) {375keepLog(composeLine(message.toString()));376} else {377// ignore378}379}380381/**382* Print error <code>message</code> to the assigned output stream383* (or to stderr if output is not specified) and keep the message384* into <code>errorsBuffer</code>.385*/386public synchronized void complain(Object message) {387if (!verbose() && isVerboseOnErrorEnabled()) {388PrintStream stream = findOutStream();389stream.println("#> ");390stream.println("#> WARNING: switching log to verbose mode,");391stream.println("#> because error is complained");392stream.println("#> ");393stream.flush();394enableVerbose(true);395}396String msgStr = message.toString();397printError(msgStr);398if (isErrorsSummaryEnabled()) {399keepError(msgStr);400}401402logExceptionForFailureAnalysis(msgStr);403}404405/**406* Print error <code>message</code> and <code>exception</code>407* to the assigned output stream408* (or to stderr if output is not specified) and keep the message409* into <code>errorsBuffer</code>.410*/411public void complain(Object message, Throwable exception) {412if ( exception != null )413complain(printExceptionToString(message, exception));414else415complain(message);416}417418/**419* Create an Exception and print the stack trace for an error msg.420* This makes it possible to detect a failure reason for this error.421*/422private void logExceptionForFailureAnalysis(String msg) {423// Some error messages are formatted in multiple lines and with tabs.424// We clean the messages to help parse the stack traces for failure analysis.425// We keep at most 2 lines, otherwise the error message may be too long.426String[] lines = msg.split("[\r\n]+");427msg = lines.length >= 2 ? lines[0] + " " + lines[1] : lines[0];428msg = msg.replaceAll("\t", " ");429430// Create a dummy exception just so we can print the stack trace.431TestFailure e = new TestFailure(msg);432StackTraceElement[] elements = e.getStackTrace();433434final int callerIndex = 2; // 0=this function, 1=complain(), 2=caller435if (elements.length <= callerIndex) {436return;437}438439// Only log the first complain message from each function.440// The reason is that some functions splits an error message441// into multiple lines and call complain() many times.442// We do not want a RULE for each of those calls.443// This means that we may miss some rules, but that444// is better than to create far too many rules.445String callerClass = elements[callerIndex].getClassName();446String callerMethod = elements[callerIndex].getMethodName();447String callerKey = callerClass + "." + callerMethod;448boolean isAlreadyLogged = loggedExceptions.contains(msg) || loggedExceptions.contains(callerKey);449450if (!isAlreadyLogged) {451PrintStream stream = findOutStream();452stream.println("The following stacktrace is for failure analysis.");453e.printStackTrace(stream);454}455456loggedExceptions.add(callerKey);457loggedExceptions.add(msg);458}459460/////////////////////////////////////////////////////////////////461462/**463* Redirect log to the given <code>stream</code>, and switch464* log mode to verbose.465* Prints errors summary to current stream, cancel current stream466* and switches to new stream. Turns on verbose mode for new stream.467*468* @deprecated This method is obsolete.469*/470@Deprecated471protected synchronized void logTo(PrintStream stream) {472finalize(); // flush older log stream473out = stream;474verbose = true;475}476477/////////////////////////////////////////////////////////////////478479/**480* Print all messages from log buffer which were hidden because481* of non-verbose mode,482*/483private synchronized void flushLogBuffer() {484if (!logBuffer.isEmpty()) {485PrintStream stream = findOutStream();486for (int i = 0; i < logBuffer.size(); i++) {487stream.println(logBuffer.elementAt(i));488}489stream.flush();490}491}492493/**494* Return <code>out</code> stream if defined or <code>Sytem.err<code> otherwise;495* print a warning message when <code>System.err</code> is used first time.496*/497private synchronized PrintStream findOutStream() {498PrintStream stream = out;499if (stream == null) {500stream = System.err;501if (!noOutWarned) {502noOutWarned = true;503stream.println("#> ");504stream.println("#> WARNING: switching log stream to stderr,");505stream.println("#> because no output stream is assigned");506stream.println("#> ");507};508};509stream.flush();510return stream;511}512513/**514* Compose line to print possible prefixing it with timestamp.515*/516private String composeLine(String message) {517if (timestamp) {518long time = System.currentTimeMillis();519long ms = time % 1000;520time /= 1000;521long secs = time % 60;522time /= 60;523long mins = time % 60;524time /= 60;525long hours = time % 24;526return "[" + hours + ":" + mins + ":" + secs + "." + ms + "] " + message;527}528return message;529}530531/**532* Print the given <code>message</code> either to <code>out</code>533* stream, or to <code>System.err</code> if <code>out</code>534* stream is not specified.535*/536private synchronized void doPrint(String message) {537PrintStream stream = findOutStream();538stream.println(composeLine(message));539stream.flush();540}541542/**543* Print the given error <code>message</code> either to <code>out</code>544* stream, or to <code>System.err</code> if <code>out</code>545* stream is not specified.546*/547private synchronized void printError(String message) {548// Print each line with the ERROR prefix:549BufferedReader br = new BufferedReader(new StringReader(message));550for (String line; ; ) {551try {552line = br.readLine();553if (line == null)554break;555doPrint("# ERROR: " + line);556} catch (IOException e) {557throw new TestBug("Exception in Log.printError(): " + e);558};559}560}561562/**563* Keep the given log <code>message</code> into <code>logBuffer</code>.564*/565private synchronized void keepLog(String message) {566logBuffer.addElement(message);567}568569/**570* Keep the given error <code>message</code> into <code>errorsBuffer</code>.571*/572private synchronized void keepError(String message) {573errorsBuffer.addElement(message);574}575576/**577* Print errors messages summary from errors buffer if any;578* print a warning message first.579*/580private synchronized void printErrorsSummary() {581if (errorsBuffer.size() <= 0)582return;583584PrintStream stream = findOutStream();585stream.println();586stream.println();587stream.println("#> ");588stream.println("#> SUMMARY: Following errors occured");589stream.println("#> during test execution:");590stream.println("#> ");591stream.flush();592593for (Enumeration e = errorsBuffer.elements(); e.hasMoreElements(); ) {594printError((String) e.nextElement());595}596}597598/**599* Print errors summary if mode is verbose, flush and cancel output stream.600*/601protected void finalize() {602if (verbose() && isErrorsSummaryEnabled()) {603printErrorsSummary();604}605if (out != null)606out.flush();607out = null;608}609610/**611* Perform finalization at the exit.612*/613public void finalizeAtExit() {614finalize();615}616617/**618* This class can be used as a base for each class that use <code>Log</code>619* for print messages and errors.620* <code>Logger</code> provides with ability to print such messages with621* specified prefix to make it possible to distinct messages printed from622* different sources.623*624* @see Log625*/626public static class Logger {627628/**629* Default prefix for messages.630*/631public static final String LOG_PREFIX = "";632633/**634* Log to print messages to.635*/636protected Log log = null;637638/**639* Prefix for messages.640*/641protected String logPrefix = LOG_PREFIX;642643/**644* Make <code>Logger</code> object with empty <code>Log</code> and645* default prefix.646* This method may be used only in derived class, that should specify647* the used <code>Log</code> object further and assign it to <code>log</code>.648*649* @see #log650* @see #setLogPrefix651*/652protected Logger() {653}654655/**656* Make <code>Logger</code> object for specified <code>Log</code>657* with default prefix.658*659* @see #setLogPrefix660*/661public Logger(Log log) {662this.log = log;663}664665/**666* Make <code>Logger</code> object for specified <code>Log</code> with667* given messages prefix.668*/669public Logger(Log log, String prefix) {670this.log = log;671this.logPrefix = prefix;672}673674/**675* Return <code>Log</code> object.676*/677public Log getLog() {678return log;679}680681/**682* Return output stream of this <code>Log</code> object.683*/684public PrintStream getOutStream() {685return log.getOutStream();686}687688/**689* Set prefix for printed messages.690*/691public void setLogPrefix(String prefix) {692logPrefix = prefix;693}694695/**696* Make printable message by adding <code>logPrefix<code> to it.697*/698public String makeLogMessage(String message) {699return logPrefix + message;700}701702/**703* Print trace message by invoking <code>Log.trace()</code>.704*705* @see Log#trace706*/707public void trace(int level, String message) {708log.trace(level, makeLogMessage(message));709}710711/**712* Print message by invoking <code>Log.println()</code>.713*714* @see Log#println715*/716public void println(String message) {717log.println(makeLogMessage(message));718}719720/**721* Print message by invoking <code>Log.display()</code>.722*723* @see Log#display724*/725public void display(String message) {726log.display(makeLogMessage(message));727}728729/**730* Complain about an error by invoking <code>Log.complain()</code> method.731*732* @see Log#complain733*/734public void complain(String message) {735log.complain(makeLogMessage(message));736}737738}739740}741742743