use
use copied to clipboard
Unify user and developer output in USE
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 quiteMode
flag. 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
- Log output to additional log-buffer
- Suppress output to default stream, but write to log-buffer by using
setQuietMode
- 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:
- Log output to additional log-buffer: Used by
MainWindow
to write a protocol to a file. - Suppress output: No usage found in USE or USE plugins
- 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
- Print errors
- Print warnings
- Print normal output
- Print debug information
- Print traces
- Print verbose information
- Output/suppress of exception stacktrace
- Can be queried if output was made (with reset functionality)
- Can print time information at beginning of normal output line
Inconsistencies
- Time information is only printed for normal output, not for other levels like warnings, errors, ...
- Normal output has
print
,println(String)
,println()
, all other levels not - Error output has location object, i. e, prints the classname of the object to "locate" the error. Other levels have not.
- Not all levels set
fDidOutput
- Order of output is unclear. If debug messages are turned of, traces could still be printed out.
Usage
The class Log
is used for different intensions:
- Providing output to the user supporting different levels of verbosity set by the user [OUT]
- Easing error handling (instead of throwing an exception, an error is logged) [ERR]
- 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:
- org.tzi.use.config.Options OUT
Here, also the commanline options
quiet
andcompileOnly
are queried. Should be considered in new version. - org.tzi.use.parser.shell.ShellCommandCompiler DEV
- org.tzi.use.main.shell.Shell OUT, ERR
- org.tzi.use.main.shell.ShellCoverageCommandProcessor OUT
Log.error(String)
is called from 104 places in the following classes:
- ~~org.tzi.use.analysis.metrics.AbstractShellCommandProcessor (unused class?)~~ deleted
- ~~org.tzi.use.analysis.metrics.GSMetricConfiguration~~ deleted
- org.tzi.use.gen.tool.GGenerator OUT
Suppresses warnings globally by switching them of
Log.setShowWarnings(false);
and restoring them afterwards. This is a strong argument for passing an output object around instead of using a static logger class. - org.tzi.use.gen.tool.GGeneratorArguments OUT
- org.tzi.use.uml.ocl.extension.ExtensionManager OUT
- org.tzi.use.uml.ocl.extension.ExtensionOperation OUT
Uses a also a StringWriter to capture errors from Ruby execution. Is this really needed?
ScriptContext
of Ruby just uses aWriter
. - org.tzi.use.uml.sys.DerivedLinkControllerDerivedEnd OUT
- org.tzi.use.uml.sys.MSystemState OUT
- org.tzi.use.main.Main OUT
- org.tzi.use.main.shell.HelpForCmd OUT
- org.tzi.use.main.shell.ReadlineStack ERR
- org.tzi.use.main.shell.Shell OUT, DEV, ERR
Uses also the functionality to reset the output flag:
Log.resetOutputFlag();
However,Log.didOutput()
is never called from USE. Maybe from plugins? - org.tzi.use.main.shell.ShellCoverageCommandProcessor OUT
- org.tzi.use.runtime.MainPluginRuntime
- org.tzi.use.runtime.gui.impl.PluginAction
- org.tzi.use.runtime.gui.impl.PluginActionDescriptor
- org.tzi.use.runtime.gui.impl.PluginActionProxy
- org.tzi.use.runtime.impl.Plugin
- org.tzi.use.runtime.impl.PluginDescriptor
- org.tzi.use.runtime.impl.PluginRuntime
- org.tzi.use.runtime.service.impl.PluginServiceDescriptor
- org.tzi.use.runtime.shell.impl.PluginShellCmd
- org.tzi.use.runtime.shell.impl.PluginShellCmdDescriptor
- org.tzi.use.runtime.util.ActionRegistry
- org.tzi.use.runtime.util.PluginParser
- org.tzi.use.runtime.util.PluginRegistry
- org.tzi.use.runtime.util.ServiceRegistry
- org.tzi.use.runtime.util.ShellCmdRegistry
Log.error(Object location, String msg)
is called from 0 places
Log.error(Exception)
is called from 3 places in the following classes:
- org.tzi.use.uml.ocl.expr.operations.Op_collection_max
- org.tzi.use.uml.ocl.expr.operations.Op_collection_min
- org.tzi.use.main.shell.ReadlineStack
Log.error(String s, Exception e)
is called from 23 places in the following classes:
- org.tzi.use.runtime.gui.impl.PluginActionDescriptor
- org.tzi.use.runtime.impl.PluginDescriptor
- org.tzi.use.runtime.service.impl.PluginServiceDescriptor
- org.tzi.use.runtime.shell.impl.PluginShellCmdDescriptor
- org.tzi.use.runtime.util.PluginRegistry
Log.warn(String string)
is called from 17 places in the following classes:
- org.tzi.use.uml.ocl.expr.ExpStdOp
- org.tzi.use.uml.ocl.extension.ExtensionManager
- org.tzi.use.uml.ocl.extension.ExtensionOperation
- org.tzi.use.uml.sys.MSystem
- org.tzi.use.uml.sys.MSystemState
- org.tzi.use.util.input.LineInput
- org.tzi.use.util.rubyintegration.RubyHelper
- org.tzi.use.main.shell.Shell
Log.debug(String)
is called from 130 places in the following classes:
- org.tzi.use.uml.ocl.expr.ThreadedEvaluator.Controller
- org.tzi.use.uml.ocl.expr.ThreadedEvaluator.Worker
- org.tzi.use.uml.ocl.extension.ExtensionManager
- org.tzi.use.uml.sys.MSystemState
- org.tzi.use.gui.main.MainWindow
- org.tzi.use.main.Main
- org.tzi.use.runtime.MainPluginRuntime
- org.tzi.use.runtime.gui.impl.PluginAction
- org.tzi.use.runtime.gui.impl.PluginActionDescriptor
- org.tzi.use.runtime.gui.impl.PluginActionProxy
- org.tzi.use.runtime.gui.impl.PluginMMHTMLPrintVisitor
- org.tzi.use.runtime.gui.impl.PluginMMPrintVisitor
- org.tzi.use.runtime.impl.PluginDescriptor
- org.tzi.use.runtime.impl.PluginRuntime
- org.tzi.use.runtime.service.impl.PluginServiceDescriptor
- org.tzi.use.runtime.shell.impl.PluginShellCmd
- org.tzi.use.runtime.shell.impl.PluginShellCmdDescriptor
- org.tzi.use.runtime.shell.impl.ShellExtensionPoint
- org.tzi.use.runtime.util.ActionRegistry
- org.tzi.use.runtime.util.PluginParser
- org.tzi.use.runtime.util.PluginRegistry
- org.tzi.use.runtime.util.ServiceRegistry
- org.tzi.use.runtime.util.ShellCmdRegistry
Log.verbose(String)
is called from 17 places in the following classes::
- org.tzi.use.gen.tool.GGenerator
- org.tzi.use.gui.main.MainWindow.ActionFileSaveScript
- org.tzi.use.main.Main
- org.tzi.use.main.shell.Shell
- org.tzi.use.runtime.MainPluginRuntime
Log.trace(String)
is called from 1 place in the following class:
- org.tzi.use.uml.ocl.expr.Evaluator
Log.trace(Object location, String msg)
is called from 11 places in the following classes:
- org.tzi.use.uml.ocl.expr.EvalContext
- org.tzi.use.uml.sys.MSystem
- org.tzi.use.uml.sys.MSystemState
- org.tzi.use.main.shell.Shell
Log.trace(Object location, String msg, boolean flush)
is called from 1 place in the following classes:
- org.tzi.use.util.Log
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
- separate output for users and output for developers
- allow for multiple output targets, i.g., shell and logwindow
- 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:
- Normal
- Error
- Warn
- Info
- Trace
Originally posted by @h-man2 in https://github.com/useocl/use/issues/5#issuecomment-1359066237