Why System.out.println() Can Kill Your MyBatis Performance and How to Fix It
This article explains why MyBatis's default StdOutImpl logging, which relies on System.out.println, blocks threads and creates severe concurrency bottlenecks, and demonstrates how to replace it with asynchronous logging implementations like Slf4jImpl, configure log levels, and even create custom Log classes for optimal performance.
When using MyBatis or its derivatives, we often enable the default log output to troubleshoot issues via SQL logs. However, MyBatis logs lack timestamps, levels, and are left‑aligned because they use a class called
StdOutImplthat prints directly to
System.out.println().
Printing with
System.out.println()blocks the current thread, causing SQL queries to wait for the log output to finish before returning results. This is why most articles recommend using a Log implementation instead of
System.out.println(), as Log is asynchronous.
Worse Situation
Slow interface responses and high CPU usage can make the system almost unusable. Using
jstackmay reveal many web threads waiting to acquire the lock on
java.io.PrintStream. The
println()method synchronizes on the global
System.outPrintStream, causing all threads to queue their SQL logs, leading to severe concurrency performance problems, especially with bulk inserts or data exports.
How to Optimize
Built‑in Log Implementations
MyBatis provides other logging implementations. Replacing
StdOutImplwith
Slf4jImplsolves the performance issue and allows unified formatting and output locations.
<code><configuration>
<settings>
<setting name="logImpl" value="SLF4J"/>
</settings>
</configuration></code>For MyBatis‑Plus:
<code>mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl</code>For MyBatis Flex:
<code>@Configuration
public class MyBatisFlexConfig implements ConfigurationCustomizer {
@Override
public void customize(FlexConfiguration config) {
// Use Slf4j for logging
config.setLogImpl(Slf4jImpl.class);
}
}</code>Note that the MyBatis
Loginterface does not define an
infolevel; it only provides
debug,
trace,
warn, and
errormethods. To enable detailed logging, configure the logger level (e.g., TRACE) in your logging framework.
Custom Log Implementation
You can implement your own Log that outputs at the
infolevel:
<code>public class MyBatisLogImpl implements Log {
private final Logger log;
public MyBatisLogImpl(String clazz) {
log = LoggerFactory.getLogger(clazz);
System.out.println(clazz);
}
@Override public boolean isDebugEnabled() { return true; }
@Override public boolean isTraceEnabled() { return true; }
@Override public void error(String s, Throwable e) { log.error(s, e); }
@Override public void error(String s) { log.error(s); }
@Override public void debug(String s) { log.info(s); }
@Override public void trace(String s) { log.info(s); }
@Override public void warn(String s) { log.warn(s); }
}</code>MyBatis also logs some debug messages that are often unnecessary, such as session creation logs. These can be filtered out in the logging configuration, for example:
<code><!-- Exclude all logs from SqlSessionUtils -->
<logger name="org.mybatis.spring.SqlSessionUtils" level="OFF"/></code>Conclusion
System.out.println()is a far worse logging method than expected; it should be avoided not only in MyBatis (where
StdOutImpluses it) but in any system. Relying on unformatted
System.outoutput can cause thread blocking and severe concurrency performance degradation.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.