001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle; 021 022import java.io.OutputStream; 023import java.io.OutputStreamWriter; 024import java.io.PrintWriter; 025import java.io.Writer; 026import java.nio.charset.StandardCharsets; 027 028import com.puppycrawl.tools.checkstyle.api.AuditEvent; 029import com.puppycrawl.tools.checkstyle.api.AuditListener; 030import com.puppycrawl.tools.checkstyle.api.AutomaticBean; 031import com.puppycrawl.tools.checkstyle.api.LocalizedMessage; 032import com.puppycrawl.tools.checkstyle.api.SeverityLevel; 033 034/** 035 * Simple plain logger for text output. 036 * This is maybe not very suitable for a text output into a file since it 037 * does not need all 'audit finished' and so on stuff, but it looks good on 038 * stdout anyway. If there is really a problem this is what XMLLogger is for. 039 * It gives structure. 040 * 041 * @author <a href="mailto:stephane.bailliez@wanadoo.fr">Stephane Bailliez</a> 042 * @see XMLLogger 043 */ 044public class DefaultLogger extends AutomaticBean implements AuditListener { 045 046 /** 047 * A key pointing to the add exception 048 * message in the "messages.properties" file. 049 */ 050 public static final String ADD_EXCEPTION_MESSAGE = "DefaultLogger.addException"; 051 /** 052 * A key pointing to the started audit 053 * message in the "messages.properties" file. 054 */ 055 public static final String AUDIT_STARTED_MESSAGE = "DefaultLogger.auditStarted"; 056 /** 057 * A key pointing to the finished audit 058 * message in the "messages.properties" file. 059 */ 060 public static final String AUDIT_FINISHED_MESSAGE = "DefaultLogger.auditFinished"; 061 062 /** Where to write info messages. **/ 063 private final PrintWriter infoWriter; 064 /** Close info stream after use. */ 065 private final boolean closeInfo; 066 067 /** Where to write error messages. **/ 068 private final PrintWriter errorWriter; 069 /** Close error stream after use. */ 070 private final boolean closeError; 071 072 /** Formatter for the log message. */ 073 private final AuditEventFormatter formatter; 074 075 /** 076 * Creates a new {@code DefaultLogger} instance. 077 * @param outputStream where to log infos and errors 078 * @param closeStreamsAfterUse if oS should be closed in auditFinished() 079 */ 080 public DefaultLogger(OutputStream outputStream, boolean closeStreamsAfterUse) { 081 // no need to close oS twice 082 this(outputStream, closeStreamsAfterUse, outputStream, false); 083 } 084 085 /** 086 * Creates a new <code>DefaultLogger</code> instance. 087 * @param infoStream the {@code OutputStream} for info messages. 088 * @param closeInfoAfterUse auditFinished should close infoStream. 089 * @param errorStream the {@code OutputStream} for error messages. 090 * @param closeErrorAfterUse auditFinished should close errorStream 091 */ 092 public DefaultLogger(OutputStream infoStream, 093 boolean closeInfoAfterUse, 094 OutputStream errorStream, 095 boolean closeErrorAfterUse) { 096 this(infoStream, closeInfoAfterUse, errorStream, closeErrorAfterUse, 097 new AuditEventDefaultFormatter()); 098 } 099 100 /** 101 * Creates a new {@code DefaultLogger} instance. 102 * 103 * @param infoStream the {@code OutputStream} for info messages 104 * @param closeInfoAfterUse auditFinished should close infoStream 105 * @param errorStream the {@code OutputStream} for error messages 106 * @param closeErrorAfterUse auditFinished should close errorStream 107 * @param messageFormatter formatter for the log message. 108 */ 109 public DefaultLogger(OutputStream infoStream, 110 boolean closeInfoAfterUse, 111 OutputStream errorStream, 112 boolean closeErrorAfterUse, 113 AuditEventFormatter messageFormatter) { 114 closeInfo = closeInfoAfterUse; 115 closeError = closeErrorAfterUse; 116 final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8); 117 infoWriter = new PrintWriter(infoStreamWriter); 118 119 if (infoStream == errorStream) { 120 errorWriter = infoWriter; 121 } 122 else { 123 final Writer errorStreamWriter = new OutputStreamWriter(errorStream, 124 StandardCharsets.UTF_8); 125 errorWriter = new PrintWriter(errorStreamWriter); 126 } 127 formatter = messageFormatter; 128 } 129 130 /** 131 * Print an Emacs compliant line on the error stream. 132 * If the column number is non zero, then also display it. 133 * @see AuditListener 134 **/ 135 @Override 136 public void addError(AuditEvent event) { 137 final SeverityLevel severityLevel = event.getSeverityLevel(); 138 if (severityLevel != SeverityLevel.IGNORE) { 139 final String errorMessage = formatter.format(event); 140 errorWriter.println(errorMessage); 141 } 142 } 143 144 @Override 145 public void addException(AuditEvent event, Throwable throwable) { 146 synchronized (errorWriter) { 147 final LocalizedMessage addExceptionMessage = new LocalizedMessage(0, 148 Definitions.CHECKSTYLE_BUNDLE, ADD_EXCEPTION_MESSAGE, 149 new String[] {event.getFileName()}, null, 150 LocalizedMessage.class, null); 151 errorWriter.println(addExceptionMessage.getMessage()); 152 throwable.printStackTrace(errorWriter); 153 } 154 } 155 156 @Override 157 public void auditStarted(AuditEvent event) { 158 final LocalizedMessage auditStartMessage = new LocalizedMessage(0, 159 Definitions.CHECKSTYLE_BUNDLE, AUDIT_STARTED_MESSAGE, null, null, 160 LocalizedMessage.class, null); 161 infoWriter.println(auditStartMessage.getMessage()); 162 infoWriter.flush(); 163 } 164 165 @Override 166 public void auditFinished(AuditEvent event) { 167 final LocalizedMessage auditFinishMessage = new LocalizedMessage(0, 168 Definitions.CHECKSTYLE_BUNDLE, AUDIT_FINISHED_MESSAGE, null, null, 169 LocalizedMessage.class, null); 170 infoWriter.println(auditFinishMessage.getMessage()); 171 closeStreams(); 172 } 173 174 @Override 175 public void fileStarted(AuditEvent event) { 176 // No need to implement this method in this class 177 } 178 179 @Override 180 public void fileFinished(AuditEvent event) { 181 infoWriter.flush(); 182 } 183 184 /** 185 * Flushes the output streams and closes them if needed. 186 */ 187 private void closeStreams() { 188 infoWriter.flush(); 189 if (closeInfo) { 190 infoWriter.close(); 191 } 192 193 errorWriter.flush(); 194 if (closeError) { 195 errorWriter.close(); 196 } 197 } 198}