Avoid usage of System.err and System.out
One of the favorite code snippets used by developers is „System.out.println“ – it’s comfortable for quickly printing log or status messages to the console while hunting bugs. Modern IDEs provide shortcuts for this pattern – so why not use it?
Accessing System.out or System.err from an application is a problem if the system (especially as a server) goes to production:
– Messages are written directly to the console. This makes it difficult to redirect specific entries to separate files, configure the required log format (e.g. timestamp, current thread etc.) or specify rules for rollover, retention etc.
– There’s no way to use severity for messages (e.g. trace, debug, info, warn, error). There’s natural difference between an exception which needs to be logged and some debugging output. The latter one is not desired to be visible in a production system as it negatively affects performance and creates large log files which are hard to analyze.
For that reasons there’s usually a constraint that only a logging framework shall be used, e.g. SLF4j. But how can you avoid that messages – by intention or accidentally – are written to System.out or System.err?
With jQAssistant this rule can be expressed using a simple idea: Find all read operations on the fields „out“ and „err“ which are declared by „java.lang.System“:
<constraint id="logging:AvoidUseOfSystemConsole"> <description>A logger must be used instead of "System.out" or "System.err".</description> <cypher><![CDATA[ match (system:Type)-[:DECLARES]->(writer:Field), (type:Type)-[:DECLARES]->(method:Method), (method)-[reads:READS]->(writer) where system.fqn="java.lang.System" and writer.signature in [ "java.io.PrintStream out", "java.io.PrintStream err" ] return type.fqn as Type, method.name as Method, reads.lineNumber as LineNumber ]]></cypher> </constraint>
The situation becomes a bit more difficult if the application consists of a server and a command line client. For the latter one it shall be allowed or even necessary to use „System.out.println“. Thus the constraint can be limited to a specific set of packages which means adding another condition to the where clause:
... where system.fqn="java.lang.System" and writer.signature in [ "java.io.PrintStream out", "java.io.PrintStream err" ] and not type.fqn =~ "com\\.acme\\.client\\..*" ...