Fundamentals 8 min read

Creating a Custom FindBugs Rule to Detect System.out/System.err Usage

This guide explains how to prepare the FindBugs environment, define a custom rule that flags System.out and System.err calls, write the detector using BCEL/ASM, integrate it into FindBugs configuration files, repackage the tool, and verify the rule works on compiled Java bytecode.

360 Quality & Efficiency
360 Quality & Efficiency
360 Quality & Efficiency
Creating a Custom FindBugs Rule to Detect System.out/System.err Usage

Before proceeding, familiarize yourself with FindBugs and its usage.

Preparation: FindBugs analyzes compiled .class bytecode files using BCEL (and optionally ASM from version 1.1). Download the source distribution findbugs-3.0.1-source.zip to create custom detectors and the standard distribution findbugs-3.0.1.zip to replace findbugs.jar after modifications.

Custom rule workflow: 1) Define the rule you want to enforce. 2) Examine the bytecode of sample code. 3) Implement the detector. 4) Add the rule to the rule file.

1. Define the rule

Example rule: avoid using output statements similar to System.out or System.err .

package main;public class TestFindBugs {    public static void main(String[] args) {        System.out.println("123"); //bug        System.err.println("123"); //bug    }}

2. Analyze the sample bytecode

Use the Eclipse plugin Bytecode Outline ( http://andrei.gmxhome.de/bytecode/index.html ) to view the compiled bytecode:

// class version 51.0 (51)
// access flags 0x21
public class main/TestFindBugs { // compiled from: TestFindBugs.java

// access flags 0x1
public
()V
 L0
  LINENUMBER 3 L0
  ALOAD 0
  INVOKESPECIAL java/lang/Object.
()V
  RETURN
 L1
  LOCALVARIABLE this Lmain/TestFindBugs; L0 L1 0
  MAXSTACK = 1
  MAXLOCALS = 1

// access flags 0x9
public static main([Ljava/lang/String;)V
 L0
  LINENUMBER 5 L0
  GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  LDC "123"
  INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
 L1
  LINENUMBER 6 L1
  GETSTATIC java/lang/System.err : Ljava/io/PrintStream;
  LDC "123"
  INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
 L2
  LINENUMBER 7 L2
  RETURN
 L3
  LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
  MAXSTACK = 2
  MAXLOCALS = 1}

Key bytecode instructions identified:

GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
GETSTATIC java/lang/System.err : Ljava/io/PrintStream;

3. Implement the detector

package edu.umd.cs.findbugs.detect;import org.apache.bcel.classfile.Code;import edu.umd.cs.findbugs.BugInstance;import edu.umd.cs.findbugs.BugReporter;import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;/**
 * @author yuanwei3-iri
 * @category 代码中避免使用有类似System.out的输出语句
 */
public class ForbiddenSystemClass extends OpcodeStackDetector {
    BugReporter bugReporter;
    public ForbiddenSystemClass(BugReporter bugReporter) { this.bugReporter = bugReporter; }
    /** visit method, called when entering a bytecode method */
    @Override
    public void visit(Code obj) { super.visit(obj); }
    /** called for each opcode */
    @Override
    public void sawOpcode(int seen) {
        if (seen == GETSTATIC) {
            if (getClassConstantOperand().equals("java/lang/System")) {
                if (getNameConstantOperand().equals("out") || getNameConstantOperand().equals("err")) {
                    BugInstance bug = new BugInstance(this, "CJ_SYSTEMCLASS", NORMAL_PRIORITY)
                        .addClassAndMethod(this).addSourceLine(this, getPC());
                    bugReporter.reportBug(bug);
                }
            }
        }
    }
}

4. Add the rule to configuration files

Update findbugs.xml :

<FindbugsPlugin>
  <Detector class="edu.umd.cs.findbugs.detect.ForbiddenSystemClass" speed="fast" reports="CJ_SYSTEMCLASS" hidden="false" />
  <BugPattern abbrev="CJ_SYSTEMCLASS" type="CJ_SYSTEMCLASS" category="PERFORMANCE" />
</FindbugsPlugin>

Update message.xml :

<?xml version="1.0" encoding="UTF-8"?>
<MessageCollection>
  <Plugin>
    <ShortDescription>Default FindBugs plugin</ShortDescription>
    <Details><![CDATA[
This plugin contains all of the standard FindBugs detectors.
]]></Details>
  </Plugin>
  <Detector class="edu.umd.cs.findbugs.detect.ForbiddenSystemClass">
    <Details><![CDATA[
代码不能出现System.out
请使用log日志形式打印
]]></Details>
  </Detector>
  <BugPattern type="CJ_SYSTEMCLASS">
    <ShortDescription>代码不能出现System.out</ShortDescription>
    <LongDescription>{1}代码不能出现System.out,请使用log形式输出</LongDescription>
    <Details><![CDATA[
不能使用System.out和System.err,请使用log
]]></Details>
  </BugPattern>
  <BugCode abbrev="CJ_SYSTEMCLASS">影响性能的输出System.out</BugCode>
</MessageCollection>

Repackage the modified FindBugs jar with Maven:

mvn clean install -Dmaven.test.skip=true

Replace the original /lib/findbugs.jar in the runnable FindBugs distribution, run the findbugs command on the compiled class files, and verify that the custom rule correctly flags the prohibited System.out and System.err statements.

Result: The custom FindBugs rule successfully detects the intended output statements, demonstrating how to extend FindBugs with user‑defined detectors.

JavaBytecodeloggingStatic AnalysisFindBugsCustom Detector
360 Quality & Efficiency
Written by

360 Quality & Efficiency

360 Quality & Efficiency focuses on seamlessly integrating quality and efficiency in R&D, sharing 360’s internal best practices with industry peers to foster collaboration among Chinese enterprises and drive greater efficiency value.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.