package water.util; import java.io.*; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Locale; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.PropertyConfigurator; import water.*; import water.api.Constants.Schemes; import water.util.Log.Tag.Kind; import water.util.Log.Tag.Sys; /** Log for H2O. This class should be loaded before we start to print as it wraps around * System.{out,err}. * * There are three kinds of message: INFO, WARN and ERRR, for general information, * events that look wrong, and runtime exceptions. * WARN messages and uncaught exceptions are printed on Standard output. Some * INFO messages are also printed on standard output. Many more messages are * printed to the log file in the ice directory and to the K/V store. * * Messages can come from a number of subsystems, Sys.RANDF for instance * denotes the Random forest implementation. Subsystem names are five letter * mnemonics to keep formatting nicely even. * * To print messages from a subsystem to the log file, set a property on the command line * -Dlog.RANDF=true * -Dlog.RANDF=false // turn off * or call the API function * Log.setFlag(Sys.RANDF); * Log.unsetFlag(Sys.RANDF); // turn off * * * OOME: when the VM is low on memory, OutOfMemoryError can be thrown in the * logging framework while it is trying to print a message. In this case the * first message that fails is recorded for later printout, and a number of * messages can be discarded. The framework will attempt to print the recorded * message later, and report the number of dropped messages, but this done in * a best effort and lossy manner. Basically when an OOME occurs during * logging, no guarantees are made about the messages. **/ public abstract class Log { /** Tags for log messages */ public static interface Tag { /** Which subsystem of h2o? */ public static enum Sys implements Tag { RANDF, GBM__, DRF__, GENLM, KMEAN, PARSE, STORE, WATER, HDFS_, HTTPD, CLEAN, CONFM, EXCEL, SCORM, LOCKS, HTLOG; boolean _enable; } /** What kind of message? */ public static enum Kind implements Tag { TRAC, DEBG, INFO, WARN, ERRR, FATL; } } static { for(Kind k : Kind.values()) assert k.name().length() == Kind.INFO.name().length(); for(Sys s : Sys.values()) assert s.name().length() == Sys.RANDF.name().length(); } public static final Kind[] KINDS = Kind.values(); public static final Sys[] SYSS = Sys.values(); private static final String NL = System.getProperty("line.separator"); static public void wrap() { ///Turning off wrapping for now... If this breaks stuff will put it back on. /// System.setOut(new Wrapper(System.out)); System.setErr(new Wrapper(System.err)); } /** Local log file */ static String LOG_DIR = null; /** Key for the log in the KV store */ public static Key LOG_KEY = null; /** Time from when this class loaded. */ static final Timer time = new Timer(); /** Some guess at the process ID. */ public static final long PID = getPid(); /** Additional logging for debugging. */ private static String _longHeaders; private static boolean printAll; /** Per subsystem debugging flags. */ static { String pa = System.getProperty("log.printAll"); printAll = (pa!=null && pa.equals("true")); setFlag(Sys.WATER); setFlag(Sys.RANDF); setFlag(Sys.HTTPD); for(Sys s : Sys.values()) { String str = System.getProperty("log."+s); if (str == null) continue; if (str.equals("false")) unsetFlag(s); else setFlag(s); } } /** Check if a subsystem will print debug message to the LOG file */ public static boolean flag(Sys t) { return t._enable || printAll; } /** Set the debug flag. */ public static void setFlag(Sys t) { t._enable = true; } /** Unset the debug flag. */ public static void unsetFlag(Sys t) { t._enable = false; } /** * Events are created for all calls to the logging API. **/ static class Event { Kind kind; Sys sys; Timer when; long msFromStart; Throwable ouch; Object[] messages; Object message; String thread; /**True if we have yet finished printing this event.*/ volatile boolean printMe; private volatile static Timer lastGoodTimer = new Timer(); private volatile static Event lastEvent = new Event(); private volatile static int missed; static Event make(Tag.Sys sys, Tag.Kind kind, Throwable ouch, Object[] messages) { return make0(sys, kind, ouch, messages , null); } static Event make(Tag.Sys sys, Tag.Kind kind, Throwable ouch, Object message) { return make0(sys, kind, ouch, null , message); } static private Event make0(Tag.Sys sys, Tag.Kind kind, Throwable ouch, Object[] messages, Object message) { Event result = null; try { result = new Event(); result.init(sys, kind, ouch, messages, message, lastGoodTimer = new Timer()); } catch (OutOfMemoryError e){ synchronized (Event.class){ if (lastEvent.printMe) { missed++; return null; }// Giving up; record the number of lost messages result = lastEvent; result.init(sys, kind, ouch, messages, null, lastGoodTimer); } } return result; } private void init(Tag.Sys sys, Tag.Kind kind, Throwable ouch, Object[] messages, Object message, Timer t) { this.kind = kind; this.ouch = ouch; this.messages = messages; this.message = message; this.sys = sys; this.when = t; this.printMe = true; } public String toString() { StringBuilder buf = longHeader(new StringBuilder(120)); int headroom = buf.length(); buf.append(body(headroom)); return buf.toString(); } public String toShortString() { StringBuilder buf = shortHeader(new StringBuilder(120)); int headroom = buf.length(); buf.append(body(headroom)); return buf.toString(); } private String body(int headroom) { //if( body != null ) return body; // the different message have different padding ... can't quite cache. StringBuilder buf = new StringBuilder(120); if (messages!=null) for( Object m : messages ) buf.append(m.toString()); else if (message !=null ) buf.append(message.toString()); // --- "\n" vs NL --- // Embedded strings often use "\n" to denote a new-line. This is either // 1 or 2 chars ON OUTPUT depending Unix vs Windows, but always 1 char in // the incoming string. We search & split the incoming string based on // the 1 character "\n", but we build result strings with NL (a String of // length 1 or 2). i.e. // GOOD: String.indexOf("\n"); SB.append( NL ) // BAD : String.indexOf( NL ); SB.append("\n") if( buf.indexOf("\n") != -1 ) { String s = buf.toString(); String[] lines = s.split("\n"); if (lines.length > 0) { //gracefully handle s = "\n" StringBuilder buf2 = new StringBuilder(2 * buf.length()); buf2.append(lines[0]); for (int i = 1; i < lines.length; i++) { buf2.append(NL).append("+"); for (int j = 1; j < headroom; j++) buf2.append(" "); buf2.append(lines[i]); } buf = buf2; } } if( ouch != null ) { buf.append(NL); Writer wr = new StringWriter(); PrintWriter pwr = new PrintWriter(wr); ouch.printStackTrace(pwr); String mess = wr.toString(); String[] lines = mess.split("\n"); for( int i = 0; i < lines.length; i++ ) { buf.append("+"); for( int j = 1; j < headroom; j++ ) buf.append(" "); buf.append(lines[i]); if( i != lines.length - 1 ) buf.append(NL); } } return buf.toString(); } private StringBuilder longHeader(StringBuilder buf) { String headers = _longHeaders; if(headers == null) { String host = H2O.SELF_ADDRESS != null ? H2O.SELF_ADDRESS.getHostAddress() : ""; headers = fixedLength(host + ":" + H2O.API_PORT + " ", 22) + fixedLength(PID + " ", 6); if(H2O.SELF_ADDRESS != null) _longHeaders = headers; } buf.append(when.startAsString()).append(" ").append(headers); if( thread == null ) thread = fixedLength(Thread.currentThread().getName() + " ", 10); buf.append(thread); buf.append(kind.toString()).append(" ").append(sys.toString()).append(": "); return buf; } private StringBuilder shortHeader(StringBuilder buf) { buf.append(when.startAsShortString()).append(" "); if(H2O.DEBUG) { String host = H2O.SELF_ADDRESS != null ? H2O.SELF_ADDRESS.getHostAddress() : ""; buf.append(fixedLength(host + ":" + H2O.API_PORT + " ", 18)); } if( thread == null ) thread = fixedLength(Thread.currentThread().getName() + " ", 8); buf.append(thread); if(!H2O.DEBUG) buf.append(kind.toString()).append(" ").append(sys.toString()).append(": "); return buf; } } /** Write different versions of E to the three outputs. */ private static void write(Event e, boolean printOnOut, boolean logToKV) { try { write0(e,printOnOut,logToKV); if (Event.lastEvent.printMe || Event.missed > 0) { synchronized(Event.class){ if ( Event.lastEvent.printMe) { Event ev = Event.lastEvent; write0(ev,true,logToKV); Event.lastEvent = new Event(); } if (Event.missed > 0) { if (Event.lastEvent.printMe==false) { Event.lastEvent.init(Sys.WATER, Kind.WARN, null, null, "Logging framework dropped a message", Event.lastGoodTimer); Event.missed--; } } } } } catch (OutOfMemoryError xe) { synchronized (Event.class){ if (Event.lastEvent.printMe == false) Event.lastEvent = e; else Event.missed++; } } } private static org.apache.log4j.Logger _logger = null; public static String getLogDir() { if (LOG_DIR == null) { return "unknown-log-dir"; } return LOG_DIR; } /** * @return The common prefix for all of the different log files for this process. */ public static String getLogPathFileNameStem() { String ip; if (H2O.SELF_ADDRESS == null) { ip = "UnknownIP"; } else { ip = H2O.SELF_ADDRESS.getHostAddress(); } // Somehow, the above process for producing an IP address has a slash // in it, which is mystifying. Remove it. int port = H2O.API_PORT; String portString = Integer.toString(port); String logFileName = getLogDir() + File.separator + "h2o_" + ip + "_" + portString; return logFileName; } /** * @return This is what shows up in the Web UI when clicking on show log file. */ public static String getLogPathFileName() { return getLogPathFileNameStem() + "-2-debug.log"; } private static org.apache.log4j.Logger getLog4jLogger() { return _logger; } private static void setLog4jProperties(String logDirParent, java.util.Properties p) { LOG_DIR = logDirParent + File.separator + "h2ologs"; String logPathFileName = getLogPathFileNameStem(); // H2O-wide logging p.setProperty("log4j.rootLogger", "TRACE, R1, R2, R3, R4, R5, R6"); p.setProperty("log4j.appender.R1", "org.apache.log4j.RollingFileAppender"); p.setProperty("log4j.appender.R1.Threshold", "TRACE"); p.setProperty("log4j.appender.R1.File", logPathFileName + "-1-trace.log"); p.setProperty("log4j.appender.R1.MaxFileSize", "1MB"); p.setProperty("log4j.appender.R1.MaxBackupIndex", "3"); p.setProperty("log4j.appender.R1.layout", "org.apache.log4j.PatternLayout"); p.setProperty("log4j.appender.R1.layout.ConversionPattern", "%m%n"); p.setProperty("log4j.appender.R2", "org.apache.log4j.RollingFileAppender"); p.setProperty("log4j.appender.R2.Threshold", "DEBUG"); p.setProperty("log4j.appender.R2.File", logPathFileName + "-2-debug.log"); p.setProperty("log4j.appender.R2.MaxFileSize", "3MB"); p.setProperty("log4j.appender.R2.MaxBackupIndex", "3"); p.setProperty("log4j.appender.R2.layout", "org.apache.log4j.PatternLayout"); p.setProperty("log4j.appender.R2.layout.ConversionPattern", "%m%n"); p.setProperty("log4j.appender.R3", "org.apache.log4j.RollingFileAppender"); p.setProperty("log4j.appender.R3.Threshold", "INFO"); p.setProperty("log4j.appender.R3.File", logPathFileName + "-3-info.log"); p.setProperty("log4j.appender.R3.MaxFileSize", "2MB"); p.setProperty("log4j.appender.R3.MaxBackupIndex", "3"); p.setProperty("log4j.appender.R3.layout", "org.apache.log4j.PatternLayout"); p.setProperty("log4j.appender.R3.layout.ConversionPattern", "%m%n"); p.setProperty("log4j.appender.R4", "org.apache.log4j.RollingFileAppender"); p.setProperty("log4j.appender.R4.Threshold", "WARN"); p.setProperty("log4j.appender.R4.File", logPathFileName + "-4-warn.log"); p.setProperty("log4j.appender.R4.MaxFileSize", "256KB"); p.setProperty("log4j.appender.R4.MaxBackupIndex", "3"); p.setProperty("log4j.appender.R4.layout", "org.apache.log4j.PatternLayout"); p.setProperty("log4j.appender.R4.layout.ConversionPattern", "%m%n"); p.setProperty("log4j.appender.R5", "org.apache.log4j.RollingFileAppender"); p.setProperty("log4j.appender.R5.Threshold", "ERROR"); p.setProperty("log4j.appender.R5.File", logPathFileName + "-5-error.log"); p.setProperty("log4j.appender.R5.MaxFileSize", "256KB"); p.setProperty("log4j.appender.R5.MaxBackupIndex", "3"); p.setProperty("log4j.appender.R5.layout", "org.apache.log4j.PatternLayout"); p.setProperty("log4j.appender.R5.layout.ConversionPattern", "%m%n"); p.setProperty("log4j.appender.R6", "org.apache.log4j.RollingFileAppender"); p.setProperty("log4j.appender.R6.Threshold", "FATAL"); p.setProperty("log4j.appender.R6.File", logPathFileName + "-6-fatal.log"); p.setProperty("log4j.appender.R6.MaxFileSize", "256KB"); p.setProperty("log4j.appender.R6.MaxBackupIndex", "3"); p.setProperty("log4j.appender.R6.layout", "org.apache.log4j.PatternLayout"); p.setProperty("log4j.appender.R6.layout.ConversionPattern", "%m%n"); // HTTPD logging p.setProperty("log4j.logger.water.api.RequestServer", "TRACE, HTTPD"); p.setProperty("log4j.additivity.water.api.RequestServer", "false"); p.setProperty("log4j.appender.HTTPD", "org.apache.log4j.RollingFileAppender"); p.setProperty("log4j.appender.HTTPD.Threshold", "TRACE"); p.setProperty("log4j.appender.HTTPD.File", logPathFileName + "-httpd.log"); p.setProperty("log4j.appender.HTTPD.MaxFileSize", "1MB"); p.setProperty("log4j.appender.HTTPD.MaxBackupIndex", "3"); p.setProperty("log4j.appender.HTTPD.layout", "org.apache.log4j.PatternLayout"); p.setProperty("log4j.appender.HTTPD.layout.ConversionPattern", "%m%n"); // Turn down the logging for some class hierarchies. p.setProperty("log4j.logger.org.apache.http", "WARN"); p.setProperty("log4j.logger.com.amazonaws", "WARN"); p.setProperty("log4j.logger.org.apache.hadoop", "WARN"); p.setProperty("log4j.logger.org.jets3t.service", "WARN"); // See the following document for information about the pattern layout. // http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html // // Uncomment this line to find the source of unwanted messages. // p.setProperty("log4j.appender.R1.layout.ConversionPattern", "%p %C %m%n"); } private static org.apache.log4j.Logger createLog4jLogger(String logDirParent) { synchronized (water.util.Log.class) { if (_logger != null) { return _logger; } // If a log4j properties file was specified on the command-line, use it. // Otherwise, create some default properties on the fly. String log4jProperties = System.getProperty ("log4j.properties"); if (log4jProperties != null) { PropertyConfigurator.configure(log4jProperties); // TODO: Need some way to set LOG_DIR here for LogCollectorTask to work. } else { java.util.Properties p = new java.util.Properties(); setLog4jProperties(logDirParent, p); PropertyConfigurator.configure(p); } _logger = LogManager.getLogger(Log.class.getName()); } return _logger; } public static void setLogLevel(int log_level) throws IllegalArgumentException { Level l; switch(log_level) { case 1: l = Level.TRACE; break; case 2: l = Level.DEBUG; break; case 3: l = Level.INFO; break; case 4: l = Level.WARN; break; case 5: l = Level.ERROR; break; case 6: l = Level.FATAL; break; default: throw new IllegalArgumentException("Log level " + log_level + " is invalid"); } _logger.setLevel(l); System.out.println("Set log level to " + l); _logger.info("Set log level to " + l); } static volatile boolean loggerCreateWasCalled = false; static private Object startupLogEventsLock = new Object(); static volatile private ArrayList<Event> startupLogEvents = new ArrayList<Event>(); private static void log0(org.apache.log4j.Logger l4j, Event e) { if (e.sys == Sys.HTLOG) { // As a special additional log, put HTLOG requests in their own file. // HTLOG are requests from RequestServer that haven't been filtered out. // HTLOG requests should only come at INFO. e.sys = Sys.HTTPD; String s = "tid(" + Thread.currentThread().getId() + ") " + e.toString(); org.apache.log4j.Logger httpdLogger = LogManager.getLogger("water.api.RequestServer"); if (e.kind == Kind.INFO) { httpdLogger.info(s); } else { httpdLogger.error(s); } return; } String s = e.toString(); if (e.kind == Kind.FATL) { l4j.fatal(s); } else if (e.kind == Kind.ERRR) { l4j.error(s); } else if (e.kind == Kind.WARN) { l4j.warn(s); } else if (e.kind == Kind.INFO) { l4j.info(s); } else if (e.kind == Kind.DEBG) { l4j.debug(s); } else if (e.kind == Kind.TRAC) { l4j.trace(s); } else { // Choose error by default if we can't figure out the right logging level. l4j.error(s); } } /** the actual write code. */ private static void write0(Event e, boolean printOnOut, boolean logToKV) { org.apache.log4j.Logger l4j = getLog4jLogger(); // If no logger object exists, try to build one. // Disable for debug, causes problems for multiple nodes per VM if ((l4j == null) && !loggerCreateWasCalled && !H2O.DEBUG) { if (H2O.SELF != null) { File dir; boolean windowsPath = H2O.ICE_ROOT.toString().matches("^[a-zA-Z]:.*"); // Use ice folder if local, or default if (windowsPath) dir = new File(H2O.ICE_ROOT.toString()); else if( H2O.ICE_ROOT.getScheme() == null || Schemes.FILE.equals(H2O.ICE_ROOT.getScheme()) ) dir = new File(H2O.ICE_ROOT.getPath()); else dir = new File(H2O.DEFAULT_ICE_ROOT()); loggerCreateWasCalled = true; l4j = createLog4jLogger(dir.toString()); } } // Log if we can, buffer if we cannot. if (l4j == null) { // Calling toString has side-effects about how the output looks. So call // it early here, even if we're just going to buffer the event. e.toString(); // buffer. synchronized (startupLogEventsLock) { if (startupLogEvents != null) { startupLogEvents.add(e); } else { // there is an inherent race condition here where we might drop a message // during startup. this is only a danger in multithreaded situations. // it's ok, just be aware of it. } } } else { // drain buffer if it exists. for performance reasons, don't enter // lock unless the buffer exists. if (startupLogEvents != null) { synchronized (startupLogEventsLock) { for (int i = 0; i < startupLogEvents.size(); i++) { Event bufferedEvent = startupLogEvents.get(i); log0(l4j, bufferedEvent); } startupLogEvents = null; } } // log. log0(l4j, e); } // if( Paxos._cloudLocked && logToKV ) logToKV(e.when.startAsString(), e.thread, e.kind, e.sys, e.body(0)); if(printOnOut || printAll) unwrap(System.out, e.toShortString()); e.printMe = false; } /** We also log events to the store. */ private static void logToKV(final String date, final String thr, final Kind kind, final Sys sys, final String msg) { // Make the LOG_KEY lazily, since we cannot make it before the cloud forms if( LOG_KEY == null ) if( !Paxos._cloudLocked ) return; // No K/V logging before cloud formed synchronized(Log.class) { if( LOG_KEY == null ) LOG_KEY = Key.make("Log", (byte) 0, Key.BUILT_IN_KEY); } final long pid = PID; // Run locally final H2ONode h2o = H2O.SELF; // Run locally new TAtomic<LogStr>() { @Override public LogStr atomic(LogStr l) { return new LogStr(l, date, h2o, pid, thr, kind, sys, msg); } }.fork(LOG_KEY); } /** Record an exception to the log file and store. */ static public <T extends Throwable> T err(Sys t, String msg, T exception) { Event e = Event.make(t, Kind.ERRR, exception, msg ); write(e,true,false); return exception; } /** Record a message to the log file and store. */ static public void err(Sys t, String msg) { Event e = Event.make(t, Kind.ERRR, null, msg ); write(e,true,false); } /** Record an exception to the log file and store. */ static public <T extends Throwable> T err(String msg, T exception) { return err(Sys.WATER, msg, exception); } /** Record a message to the log file and store. */ static public void err(String msg) { err(Sys.WATER, msg); } /** Record an exception to the log file and store. */ static public <T extends Throwable> T err(Sys t, T exception) { return err(t, "", exception); } /** Record an exception to the log file and store. */ static public <T extends Throwable> T err(T exception) { return err(Sys.WATER, "", exception); } /** Record an exception to the log file and store and return a new * RuntimeException that wraps around the exception. */ static public RuntimeException errRTExcept(Throwable exception) { return new RuntimeException(err(Sys.WATER, "", exception)); } /** Log a warning to standard out, the log file and the store. */ static public <T extends Throwable> T warn(Sys t, String msg, T exception) { Event e = Event.make(t, Kind.WARN, exception, msg); write(e,true,true); return exception; } /** Log a warning to standard out, the log file and the store. */ static public Throwable warn(Sys t, String msg) { return warn(t, msg, null); } /** Log a warning to standard out, the log file and the store. */ static public Throwable warn(String msg) { return warn(Sys.WATER, msg, null); } /** Log an information message to standard out, the log file and the store. */ static public void info_no_stdout(Sys t, Object... objects) { Event e = Event.make(t, Kind.INFO, null, objects); write(e,false,true); } static public void info_no_DKV(Sys t, Object... objects) { Event e = Event.make(t, Kind.INFO, null, objects); write(e,false,false); } /** Log an information message to standard out, the log file and the store. */ static public void info(Sys t, Object... objects) { Event e = Event.make(t, Kind.INFO, null, objects); write(e,true,true); } /** Log an information message to standard out, the log file and the store. */ static public void info_no_stdout(Object... objects) { info_no_stdout(Sys.WATER, objects); } /** Log an information message to standard out, the log file and the store. */ static public void info(Object... objects) { info(Sys.WATER, objects); } /** Log a debug message to the log file and the store if the subsystem's flag is set. */ static public void debug(Object... objects) { if (flag(Sys.WATER) == false) return; Event e = Event.make(Sys.WATER, Kind.DEBG, null, objects); write(e,false,true); } /** Log a debug message to the log file and the store if the subsystem's flag is set. */ static public void debug(Sys t, Object... objects) { if (flag(t) == false) return; Event e = Event.make( t, Kind.DEBG, null, objects); write(e,false,true); } /** Log a debug message to the log file and the store if the subsystem's flag is set. */ static public void trace(Object... objects) { if (flag(Sys.WATER) == false) return; Event e = Event.make(Sys.WATER, Kind.TRAC, null, objects); write(e,false,true); } /** Temporary log statement. Search for references to make sure they have been removed. */ static public void tmp(Object... objects) { info(objects); } public static String fixedLength(String s, int length) { String r = padRight(s, length); if( r.length() > length ) { int a = Math.max(r.length() - length + 1, 0); int b = Math.max(a, r.length()); r = "#" + r.substring(a, b); } return r; } public static String padRight(String stringToPad, int size) { StringBuilder strb = new StringBuilder(stringToPad); while( strb.length() < size ) if( strb.length() < size ) strb.append(' '); return strb.toString(); } /// ==== FROM OLD LOG ==== // Survive "die" calls - used in some debugging modes public static boolean _dontDie; // Return process ID, or -1 if not supported private static long getPid() { try { String n = ManagementFactory.getRuntimeMXBean().getName(); int i = n.indexOf('@'); if( i == -1 ) return -1; return Long.parseLong(n.substring(0, i)); } catch( Throwable t ) { return -1; } } // Print to the original STDERR & die public static void die(String s) { System.err.println(s); if( !_dontDie ) H2O.exit(-1); } /** Print a message to the stream without the logging information. */ public static void unwrap(PrintStream stream, String s) { if( stream instanceof Wrapper ) ((Wrapper) stream).printlnParent(s); else stream.println(s); } public static PrintStream unwrap(PrintStream stream){ return stream instanceof Wrapper ? ((Wrapper)stream).parent: stream; } public static void log(File file, PrintStream stream) throws Exception { BufferedReader reader = new BufferedReader(new FileReader(file)); try { for( ;; ) { String line = reader.readLine(); if( line == null ) break; stream.println(line); } } finally { reader.close(); } } public static final class Wrapper extends PrintStream { PrintStream parent; Wrapper(PrintStream parent) { super(parent); this.parent=parent; } private static String log(Locale l, boolean nl, String format, Object... args) { String msg = String.format(l, format, args); Event e = Event.make(Sys.WATER,Kind.INFO,null, msg); Log.write(e,false,true); return e.toShortString()+NL; } @Override public PrintStream printf(String format, Object... args) { super.print(log(null, false, format, args)); return this; } @Override public PrintStream printf(Locale l, String format, Object... args) { super.print(log(l, false, format, args)); return this; } @Override public void println(String x) { super.print(log(null, true, "%s", x)); } void printlnParent(String s) { super.println(s); } } // Class to hold a ring buffer of log messages in the K/V store public static class LogStr extends Iced { public static final int MAX = 1024; // Number of log entries public final int _idx; // Index into the ring buffer public final byte _kinds[]; public final byte _syss[]; public final String _dates[]; public final H2ONode _h2os[]; public final long _pids[]; public final String _thrs[]; public final String _msgs[]; LogStr(LogStr l, String date, H2ONode h2o, long pid, String thr, Kind kind, Sys sys, String msg) { _dates = l == null ? new String[MAX] : l._dates; _h2os = l == null ? new H2ONode[MAX] : l._h2os; _pids = l == null ? new long[MAX] : l._pids; _thrs = l == null ? new String[MAX] : l._thrs; _kinds = l == null ? new byte[MAX] : l._kinds; _syss = l == null ? new byte[MAX] : l._syss; _msgs = l == null ? new String[MAX] : l._msgs; _idx = l == null ? 0 : (l._idx + 1) & (MAX - 1); _dates[_idx] = date; _h2os[_idx] = h2o; _pids[_idx] = pid; _thrs[_idx] = thr; _kinds[_idx] = (byte) kind.ordinal(); _syss[_idx] = (byte) sys.ordinal(); _msgs[_idx] = msg; } } /** * POST stands for "Power on self test". * Stamp a POST code to /tmp. * This is for bringup, when no logging or stdout I/O is reliable. * (Especially when embedded, such as in hadoop mapreduce, for example.) * * @param n POST code. * @param s String to emit. */ // private static final Object postLock = new Object(); public static void POST(int n, String s) { // DO NOTHING UNLESS ENABLED BY REMOVING THIS RETURN! return; // synchronized (postLock) { // File f = new File ("/tmp/h2o.POST"); // if (! f.exists()) { // boolean success = f.mkdirs(); // if (! success) { // try { System.err.print ("Exiting from POST now!"); } catch (Exception _) {} // H2O.exit (0); // } // } // // f = new File ("/tmp/h2o.POST/" + n); // try { // f.createNewFile(); // FileWriter fstream = new FileWriter(f.getAbsolutePath(), true); // BufferedWriter out = new BufferedWriter(fstream); // out.write(s + "\n"); // out.close(); // } // catch (Exception e) { // try { System.err.print ("Exiting from POST now!"); } catch (Exception _) {} // H2O.exit (0); // } // } } public static void POST(int n, Exception e) { if (e.getMessage() != null) { POST(n, e.getMessage()); } POST(n, e.toString()); StackTraceElement[] els = e.getStackTrace(); for (int i = 0; i < els.length; i++) { POST(n, els[i].toString()); } } public static void POST(int n) { POST(n, ""); } public static void main(String[]args) { Log.info("hi"); Log.info("h","i"); unwrap(System.out,"hi"); unwrap(System.err,"hi"); Log.info("ho ",new Object(){ int i; public String toString() { if (i++ ==0) throw new OutOfMemoryError(); else return super.toString(); } } ); Log.info("ha ",new Object(){ int i; public String toString() { if (i++ ==0) throw new OutOfMemoryError(); else return super.toString(); } } ); Log.info("hi"); Log.info("hi"); Log.info("hi"); } }