useocl / use

Home of the UML-based Specification Environment (USE)
GNU General Public License v2.0
43 stars 18 forks source link

Unify user and developer output in USE #60

Open h-man2 opened 1 year ago

h-man2 commented 1 year ago

Output in USE 7.0

Before a new Shell can be integrated (see #5), it needs to be defined how output to the shell and to different logs should be done.

Currently (USE 7.0.1), there are different technqiues used to output information. These techniques are:

1. org.tzi.use.util.USEWriter

classDiagram-v2
    class USEWriter {
      -out:PrintStream
      -noProtocolOut:PrintStream
      -err:PrintStream
      -log:ByteArrayOutputStream
      -logWriter:PrintStream
      -quietMode:boolean
      +getOut():PrintStream
      +getErr():PrintStream
      +getProtocolOut():PrintStream
      +protocol(line:String)
      +writeProtocolFile(out:OutputStream)
      +isQuietMode():boolean
      +setQuietMode(quietMode:boolean)
      +clearLog()
    }

    class LoggingOutputStreamDecorator {
        -out:OutputStream
        +write(b:int)
    }

    class OutputStream

    OutputStream <|-- LoggingOutputStreamDecorator
    USEWriter ..> LoggingOutputStreamDecorator: use

Description

The class USEWriter represents a singleton that logs output to an additional logWriter. This behaviour can be changed by setting the quiteModeflag. Further, the logWriter can be bypassed by using the original stream getNoProtocolOut().

It represents some kind of manager for the decorated streams and a data sink for all output of USE.

Functionality

  1. Log output to additional log-buffer
  2. Suppress output to default stream, but write to log-buffer by using setQuietMode
  3. Bypass log-buffer by using getNoProtocolOut

Usage

The USEWriter itself is used for the default outputs System.out and System.err at the very beginning of main, to be able to record all outputs of USE.

The addtional functionality of USEWriter is used as follows:

  1. Log output to additional log-buffer: Used by MainWindow to write a protocol to a file.
  2. Suppress output: No usage found in USE or USE plugins
  3. Bypass log-buffer: No usage found in USE or USE plugins

2. org.tzi.use.util.Log

classDiagram-v2    
    class Log {
      -fOut:PrintStream
      -fErr:PrintStream
      -fDbg:PrintStream

      -fDateFormat:DateFormat
      -fPrintTime:boolean

      -showWarnings:boolean
      -fDebug:boolean
      -fVerbose:boolean
      -fTrace:boolean

      -fPrintStackTraces:boolean

      -fDidOutput:boolean

      +setShowWarnings(showWarnings:boolean)$
      +isShowWarnings()$ boolean

      +setDebug(onOff:boolean)$
      +isDebug()$ boolean

      +setVerbose(onOff:boolean)$
      +isVerbose()$ boolean

      +setTrace(onOff:boolean)$
      +isTracing()$ boolean

      +setPrintStackTrace(onOff:boolean)$
      +isPrintStackTrace()$ boolean

      +setPrintTime(onOff:boolean)$
      +isPrintingTime()$ boolean

      +resetOutputFlag()$

      +out()$ PrintStream

      +error(s:String)$
      +error(location:Object, msg:String)$
      +error(e:Exception)$
      +error(s:String, e:Exception)$

      +warn(string:String)$

      +println(s:String)$
      +print(s:String)$
      +println()$

      +debug(s:String)$

      +verbose(s:String)$

      +trace(msg:String)$
      +trace(location:Object, msg:String)$
      +trace(location:Object, msg:String, flush:boolean)$
    }

Description

The class Log allows to separate between normal output, warning, errors and debug information. It is a static utility class.

Functionality

  1. Print errors
  2. Print warnings
  3. Print normal output
  4. Print debug information
  5. Print traces
  6. Print verbose information
  7. Output/suppress of exception stacktrace
  8. Can be queried if output was made (with reset functionality)
  9. Can print time information at beginning of normal output line

Inconsistencies

Usage

The class Log is used for different intensions:

  1. Providing output to the user supporting different levels of verbosity set by the user [OUT]
  2. Easing error handling (instead of throwing an exception, an error is logged) [ERR]
  3. Providing logging capabilities for USE developers [DEV]

While 1 and 3 are in my opinion valid usages, 2 isn't.

Log.println(String) is called from:
Log.error(String) is called from 104 places in the following classes:
Log.error(Object location, String msg) is called from 0 places

Log.error(Exception) is called from 3 places in the following classes:

Log.error(String s, Exception e) is called from 23 places in the following classes:

Log.warn(String string) is called from 17 places in the following classes:

Log.debug(String) is called from 130 places in the following classes:

Log.verbose(String) is called from 17 places in the following classes::

Log.trace(String) is called from 1 place in the following class:

Log.trace(Object location, String msg) is called from 11 places in the following classes:

Log.trace(Object location, String msg, boolean flush) is called from 1 place in the following classes:

3. Direct output to System.out and System.err

Many places access System.out or System.err. A detailed list can be generated using ArchUnit with the following test:

@ArchTest
public static final ArchRule noSystemOut = GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;

Shell

The class Shell outputs to System.err if there is an exception "on a lower level". This is done to be as less dependent as possible.

Sometimes output to the shell is done by System.out, without any reason. This should be replaced by some explicit handling of the shell output.

Options

Prints help on System.out

4. Usage of PrintWriter on execution

Some areas of USE pass loggers as arguments. For example, the USECompiler takes an argument PrintWriter err to report compilation errors. However, there is just one kind of output with no differentiation between errors, warnings, debug, ... on the USE application level. For this, sometimes awkward solutions are implemented. For example, the BaseParser uses System.err to provide a warning writer.

Proposed Solution

The new approach to output information should

  1. separate output for users and output for developers
  2. allow for multiple output targets, i.g., shell and logwindow
  3. allow for decorated output, i.g., output warnings in yellow, etc.

To achieve 1., a class UserOutput is proposed. This class is used instead of the currently used PrintWriter. To output information to developers, a well accepted logging framework should be used. This clearly separates user output from development information.

The proposed UserOutput class provides methods for different output levels for USE:

Originally posted by @h-man2 in https://github.com/useocl/use/issues/5#issuecomment-1359066237