Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellcode-Bibliothek

© Kompilation durch diese Firma

[Weder Korrektheit noch Funktionsfähigkeit der Software werden zugesichert.]

Datei: Log.java   Sprache: JAVA

/*
 * Log.java - A class for logging events
 * :tabSize=4:indentSize=4:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 1999, 2003 Slava Pestov
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */


package org.gjt.sp.util;

//{{{ Imports
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Writer;

import java.text.DateFormat;

import java.util.*;

import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import javax.swing.ListModel;
import javax.swing.SwingUtilities;

import static java.text.DateFormat.MEDIUM;
//}}}

/**
 * This class provides methods for logging events. In terms of functionality,
 * it is somewhere in between <code>System.out.println()</code> and
 * full-blown logging packages such as log4j.<p>
 *
 * All events are logged to an in-memory buffer and optionally a stream,
 * and those with a high urgency (warnings and errors) are also printed
 * to standard output.<p>
 *
 * Logging of exception tracebacks is supported.<p>
 *
 * This class can also optionally redirect standard output and error
 * to the log, see {@link #init}.
 *
 * @author Slava Pestov
 * @version $Id: Log.java 25123 2020-04-04 22:40:51Z kpouer $
 */

public class Log
{
 //{{{ Constants
 /**
 * The maximum number of log messages that will be kept in memory.
 * @since jEdit 2.6pre5
 */

 public static int MAXLINES = 500;

 /**
 * Debugging message urgency. Should be used for messages only
 * useful when debugging a problem.
 * @since jEdit 2.2pre2
 */

 public static final int DEBUG = 1;

 /**
 * Message urgency. Should be used for messages which give more
 * detail than notices.
 * @since jEdit 2.2pre2
 */

 public static final int MESSAGE = 3;

 /**
 * Notice urgency. Should be used for messages that directly
 * affect the user.
 * @since jEdit 2.2pre2
 */

 public static final int NOTICE = 5;

 /**
 * Warning urgency. Should be used for messages that warrant
 * attention.
 * @since jEdit 2.2pre2
 */

 public static final int WARNING = 7;

 /**
 * Error urgency. Should be used for messages that signal a
 * failure.
 * @since jEdit 2.2pre2
 */

 public static final int ERROR = 9;
 //}}}

 //{{{ init() method
 /**
 * Initializes the log.
 * @param stdio If true, standard output and error will be
 * intercepted and sent to the log. The <code>urgency</code>
 * for these implicit log entries is <code>NOTICE</code>
 * and <code>ERROR</code> accordingly. Note that in such a situation
 * <code>System.out.print()</code> calls will not appear on standard
 * output, if default output level is higher than <code>NOTICE</code>.
 * @param level Messages with this log level or higher will
 * be printed to the system console.
 * @since jEdit 3.2pre4
 */

 public static void init(boolean stdio, int level)
 {
  if(stdio && System.out == realOut && System.err == realErr)
  {
   System.setOut(createPrintStream(NOTICE,null));
   System.setErr(createPrintStream(ERROR,null));
  }

  Log.level = level;

  // Log some stuff
  log(MESSAGE,Log.class,"When reporting bugs, please"
   + " include the following information:");
  String[] props = {
   "java.version""java.vm.version""java.vm.name",
   "java.runtime.version""java.runtime.name",
   "java.vendor""java.compiler""os.name""os.version",
   "os.arch""user.home""java.home",
   "java.class.path",
   };
  for (String prop : props)
  {
   log(MESSAGE, Log.class, prop + '=' + System.getProperty(prop));
  }
 } //}}}

 //{{{ setLogWriter() method
 /**
 * Writes all currently logged messages to this stream if there was no
 * stream set previously, and sets the stream to write future log
 * messages to.
 * @param stream The writer
 * @since jEdit 3.2pre4
 */

 public static void setLogWriter(Writer stream)
 {
  if(Log.stream == null && stream != null)
  {
   try
   {
    if(wrap)
    {
     for(int i = logLineCount; i < log.length; i++)
     {
      stream.write(log[i]);
      stream.write(lineSep);
     }
    }
    for(int i = 0; i < logLineCount; i++)
    {
     stream.write(log[i]);
     stream.write(lineSep);
    }

    stream.flush();
   }
   catch(Exception e)  // NOPMD
   {
    // do nothing, who cares -- well, PMD will call you on it.
   }
  }

  Log.stream = stream;
 } //}}}

 //{{{ get/setBeepOnOutput method
 /**
 * @since jEdit 5.1pre1
 */

 public static boolean getBeepOnOutput()
 {
  return beepOnOutput;
 }

 /**
 * When <code>beepOnOutput</code> is set, every output going to standard
 * error is signaled by a standard beep. This is intended for debugging
 * purposes, to allow for immediate problem detection.
 * @since jEdit 5.1pre1
 */

 public static void setBeepOnOutput(boolean beepOnOutput)
 {
  Log.beepOnOutput = beepOnOutput;
 } //}}}
 
 public static void setMaxLines(int newMax)
 {
  if (newMax == MAXLINES)
   return;
  
  // find last non-null entry in log array
  int lineCount = 0;
  for (int i = 0; i < log.length; i++) 
  {
   if (log[i] == null)
    break;
   ++lineCount;
  }
  
  // copy entries from log to newLog
  String[] newLog = new String[newMax];
  if (newMax > lineCount)
  {
   System.arraycopy(log, 0, newLog, 0, lineCount);
  }
  else
  {
   // lineCount > newMax
   System.arraycopy(log, lineCount - newMax, newLog, 0, newMax);
   logLineCount = newMax;
  }
  
  MAXLINES = newMax;
  log = newLog;
  listModel.update(lineCount, true);
 }
 
 public static int getMaxLinex()
 {
  return MAXLINES; 
 }

 //{{{ flushStream() method
 /**
 * Flushes the log stream.
 * @since jEdit 2.6pre5
 */

 public static void flushStream()
 {
  if(stream != null)
  {
   try
   {
    stream.flush();
   }
   catch(IOException io)
   {
    io.printStackTrace(realErr);
   }
  }
 } //}}}

 //{{{ closeStream() method
 /**
 * Closes the log stream. Should be done before your program exits.
 * @since jEdit 2.6pre5
 */

 public static void closeStream()
 {
  if(stream != null)
  {
   try
   {
    stream.close();
    stream = null;
   }
   catch(IOException io)
   {
    io.printStackTrace(realErr);
   }
  }
 } //}}}

 //{{{ getLogListModel() method
 /**
 * Returns the list model for viewing the log contents.
 * @since jEdit 4.2pre1
 */

 public static ListModel<String> getLogListModel()
 {
  return listModel;
 } //}}}

 //{{{ log() method
 /**
 * Logs an exception with a message.
 *
 * If an exception is the cause of a call to {@link #log}, then
 * the exception should be explicitly provided so that it can
 * be presented to the (debugging) user in a useful manner
 * (not just the exception message, but also the exception stack trace)
 *
 * @since jEdit 4.3pre5
 */

 public static void log(int urgency, Object source, Object message,
  Throwable exception)
 {
  // We can do nicer here, but this is a start...
  log(urgency,source,message);
  log(urgency,source,exception);
 } //}}}

 //{{{ log() method
 /**
 * Logs a message. This method is thread-safe.<p>
 *
 * The following code sends a typical debugging message to the activity
 * log:
 * <pre>Log.log(Log.DEBUG,this,"counter = " + counter);</pre>
 * The corresponding activity log entry might read as follows:
 * <pre>[debug] JavaParser: counter = 15</pre>
 *
 * @param urgency The urgency; can be one of
 * <code>Log.DEBUG</code>, <code>Log.MESSAGE</code>,
 * <code>Log.NOTICE</code>, <code>Log.WARNING</code>, or
 * <code>Log.ERROR</code>.
 * @param source The source of the message, either an object or a
 * class instance. When writing log messages from macros, set
 * this parameter to <code>BeanShell.class</code> to make macro
 * errors easier to spot in the activity log.
 * @param message The message. This can either be a string or
 * an exception
 *
 * @since jEdit 2.2pre2
 */

 public static void log(int urgency, Object source, Object message)
 {
  String _source;
  if(source == null)
  {
   _source = Thread.currentThread().getName();
   if(_source == null)
   {
    _source = Thread.currentThread().getClass().getName();
   }
  }
  else if(source instanceof Class)
   _source = ((Class<?>)source).getName();
  else
   _source = source.getClass().getName();
  int index = _source.lastIndexOf('.');
  if(index != -1)
   _source = _source.substring(index+1);

  if(message instanceof Throwable)
  {
   _logException(urgency,source,(Throwable)message);
  }
  else
  {
   String _message = String.valueOf(message);
   // If multiple threads log stuff, we don't want
   // the output to get mixed up
   synchronized(LOCK)
   {
    StringTokenizer st = new StringTokenizer(
     _message,"\r\n");
    int lineCount = 0;
    boolean oldWrap = wrap;
    while(st.hasMoreTokens())
    {
     lineCount++;
     _log(urgency,_source,st.nextToken()
      .replace('\t',' '));
    }
    listModel.update(lineCount,oldWrap);
   }
  }
 } //}}}

 //{{{ Private members

 //{{{ Instance variables
 private static final Object LOCK;
 private static String[] log;
 private static int logLineCount;
 private static boolean wrap;
 private static int level;
 private static Writer stream;
 private static final String lineSep;
 private static final PrintStream realOut;
 private static final PrintStream realErr;
 private static final LogListModel listModel;
 private static final DateFormat timeFormat;
 private static final int MAX_THROWABLES = 10;
 public static final List<Throwable> throwables;
 // initialized externally through setBeepOnOutput method
 private static boolean beepOnOutput = false;
 // to prevent too much beeping we remember last beep time
 private static long lastBeepTime = 0;
 //}}}

 //{{{ Class initializer
 static
 {
  LOCK = new Object();
  level = WARNING;

  realOut = System.out;
  realErr = System.err;

  log = new String[MAXLINES];
  lineSep = System.getProperty("line.separator");
  listModel = new LogListModel();
  
  timeFormat = DateFormat.getTimeInstance(MEDIUM);
  throwables = Collections.synchronizedList(new ArrayList<Throwable>(MAX_THROWABLES));
 } //}}}

 //{{{ createPrintStream() method
 private static PrintStream createPrintStream(final int urgency,
  final Object source)
 {
  return new LogPrintStream(urgency, source);
 } //}}}

 //{{{ _logException() method
 private static void _logException(final int urgency,
  final Object source,
  final Throwable message)
 {
  PrintStream out = createPrintStream(urgency,source);
  if (urgency >= level)
  {
   synchronized (throwables)
   {
    if (throwables.size() == MAX_THROWABLES)
    {
     throwables.remove(0);
    }
    throwables.add(message);
   }
  }
  synchronized(LOCK)
  {
   message.printStackTrace(out);
  }
 } //}}}

 //{{{ _log() method
 private static void _log(int urgency, String source, String message)
 {
  String fullMessage = timeFormat.format(new Date()) + " ["+Thread.currentThread().getName()+"] [" + urgencyToString(urgency) + "] " + source
   + ": " + message;

  try
  {
   log[logLineCount] = fullMessage;
   if(++logLineCount >= log.length)
   {
    wrap = true;
    logLineCount = 0;
   }

   if(stream != null)
   {
    stream.write(fullMessage);
    stream.write(lineSep);
   }
  }
  catch(Exception e)
  {
   e.printStackTrace(realErr);
  }

  if(urgency >= level)
  {
   if(urgency == ERROR)
    realErr.println(fullMessage);
   else
    realOut.println(fullMessage);

   if (beepOnOutput)
   {
    long time = System.currentTimeMillis();
    
    if (time - lastBeepTime > 1000)
    {
     javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null); 
     lastBeepTime = System.currentTimeMillis();
    }
   }
  }
 } //}}}

 //{{{ urgencyToString() method
 private static String urgencyToString(int urgency)
 {
  switch(urgency)
  {
  case DEBUG:
   return "debug";
  case MESSAGE:
   return "message";
  case NOTICE:
   return "notice";
  case WARNING:
   return "warning";
  case ERROR:
   return "error";
  }

  throw new IllegalArgumentException("Invalid urgency: " + urgency);
 } //}}}

 //}}}

 //{{{ LogListModel class
 static class LogListModel implements ListModel<String>
 {
  final List<ListDataListener> listeners = new ArrayList<>();

  //{{{ fireIntervalAdded() method
  private void fireIntervalAdded(int index1, int index2)
  {
   for (ListDataListener listener : listeners)
   {
    listener.intervalAdded(new ListDataEvent(this,
     ListDataEvent.INTERVAL_ADDED, index1, index2));
   }
  } //}}}

  //{{{ fireIntervalRemoved() method
  private void fireIntervalRemoved(int index1, int index2)
  {
   for (ListDataListener listener : listeners)
   {
    listener.intervalRemoved(new ListDataEvent(
     this, ListDataEvent.INTERVAL_REMOVED, index1, index2));
   }
  } //}}}

  //{{{ addListDataListener() method
  @Override
  public void addListDataListener(ListDataListener listener)
  {
   listeners.add(listener);
  } //}}}

  //{{{ removeListDataListener() method
  @Override
  public void removeListDataListener(ListDataListener listener)
  {
   listeners.remove(listener);
  } //}}}

  //{{{ getElementAt() method
  @Override
  public String getElementAt(int index)
  {
   if(wrap)
   {
    if(index < MAXLINES - logLineCount)
     return log[index + logLineCount];
    else
     return log[index - MAXLINES + logLineCount];
   }
   else
    return log[index];
  } //}}}

  //{{{ getSize() method
  @Override
  public int getSize()
  {
   if(wrap)
    return MAXLINES;
   else
    return logLineCount;
  } //}}}

  //{{{ update() method
  void update(final int lineCount, final boolean oldWrap)
  {
   if(lineCount == 0 || listeners.isEmpty())
    return;

   SwingUtilities.invokeLater(() ->
   {
    if(wrap)
    {
     if(oldWrap)
      fireIntervalRemoved(0,lineCount - 1);
     else
     {
      fireIntervalRemoved(0,
       logLineCount);
     }
     fireIntervalAdded(
      MAXLINES - lineCount + 1,
      MAXLINES);
    }
    else
    {
     fireIntervalAdded(
      logLineCount - lineCount + 1,
      logLineCount);
    }
   });
  } //}}}
 } //}}}

 //{{{ LogPrintStream class
 /**
 * A print stream that uses the "Log" class to output the messages,
 * and has special treatment for the printf() function. Using this
 * stream has one caveat: printing messages that don't have a line
 * break at the end will have one added automatically...
 */

 private static class LogPrintStream extends PrintStream {

  private final ByteArrayOutputStream buffer;
  private final OutputStream orig;

  //{{{ LogPrintStream constructor
  LogPrintStream(int urgency, Object source)
  {
   super(new LogOutputStream(urgency, source));
   buffer = new ByteArrayOutputStream();
   orig = out;
  } //}}}

  //{{{ printf() method
  /**
 * This is a hack to allow "printf" to not print weird
 * stuff to the output. Since "printf" doesn't seem to
 * print the whole message in one shot, our output
 * stream above would break a line of log into several
 * lines; so we buffer the result of the printf call and
 * print the whole thing in one shot. A similar hack
 * would be needed for the "other" printf method, but
 * I'll settle for the common case only.
 */

  @Override
  public PrintStream printf(String format, Object... args)
  {
   synchronized (orig)
   {
    buffer.reset();
    out = buffer;
    super.printf(format, args);

    try
    {
     byte[] data = buffer.toByteArray();
     orig.write(data, 0, data.length);
     out = orig;
    }
    catch (IOException ioe)  // NOPMD
    {
     // don't do anything?
    }
    finally
    {
     buffer.reset();
    }
   }
   return this;
  } //}}}
 } //}}}

 //{{{ LogOutputStream class
 private static class LogOutputStream extends OutputStream
 {
  private final int  urgency;
  private final Object  source;

  //{{{ LogOutputStream constructor
  LogOutputStream(int urgency, Object source)
  {
   this.urgency  = urgency;
   this.source  = source;
  } //}}}

  //{{{ write() method
  @Override
  public synchronized void write(int b)
  {
   byte[] barray = { (byte)b };
   write(barray,0,1);
  } //}}}

  //{{{ write() method
  @Override
  public synchronized void write(byte[] b, int off, int len)
  {
   String str = new String(b,off,len);
   log(urgency,source,str);
  } //}}}
 } //}}}
}

[ zur Elbe Produktseite wechseln0.25Quellennavigators  Analyse erneut starten  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik