001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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 * @see XMLLogger 042 * @noinspection ClassWithTooManyConstructors 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 * @deprecated in order to fulfill demands of BooleanParameter IDEA check. 080 * @noinspection BooleanParameter 081 */ 082 @Deprecated 083 public DefaultLogger(OutputStream outputStream, boolean closeStreamsAfterUse) { 084 // no need to close oS twice 085 this(outputStream, closeStreamsAfterUse, outputStream, false); 086 } 087 088 /** 089 * Creates a new {@code DefaultLogger} instance. 090 * @param infoStream the {@code OutputStream} for info messages. 091 * @param closeInfoAfterUse auditFinished should close infoStream. 092 * @param errorStream the {@code OutputStream} for error messages. 093 * @param closeErrorAfterUse auditFinished should close errorStream 094 * @deprecated in order to fulfill demands of BooleanParameter IDEA check. 095 * @noinspection BooleanParameter 096 */ 097 @Deprecated 098 public DefaultLogger(OutputStream infoStream, 099 boolean closeInfoAfterUse, 100 OutputStream errorStream, 101 boolean closeErrorAfterUse) { 102 this(infoStream, closeInfoAfterUse, errorStream, closeErrorAfterUse, 103 new AuditEventDefaultFormatter()); 104 } 105 106 /** 107 * Creates a new {@code DefaultLogger} instance. 108 * 109 * @param infoStream the {@code OutputStream} for info messages 110 * @param closeInfoAfterUse auditFinished should close infoStream 111 * @param errorStream the {@code OutputStream} for error messages 112 * @param closeErrorAfterUse auditFinished should close errorStream 113 * @param messageFormatter formatter for the log message. 114 * @deprecated in order to fulfill demands of BooleanParameter IDEA check. 115 * @noinspection BooleanParameter, WeakerAccess 116 */ 117 @Deprecated 118 public DefaultLogger(OutputStream infoStream, 119 boolean closeInfoAfterUse, 120 OutputStream errorStream, 121 boolean closeErrorAfterUse, 122 AuditEventFormatter messageFormatter) { 123 closeInfo = closeInfoAfterUse; 124 closeError = closeErrorAfterUse; 125 final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8); 126 infoWriter = new PrintWriter(infoStreamWriter); 127 128 if (infoStream == errorStream) { 129 errorWriter = infoWriter; 130 } 131 else { 132 final Writer errorStreamWriter = new OutputStreamWriter(errorStream, 133 StandardCharsets.UTF_8); 134 errorWriter = new PrintWriter(errorStreamWriter); 135 } 136 formatter = messageFormatter; 137 } 138 139 /** 140 * Creates a new {@code DefaultLogger} instance. 141 * @param outputStream where to log infos and errors 142 * @param outputStreamOptions if {@code CLOSE} that should be closed in auditFinished() 143 */ 144 public DefaultLogger(OutputStream outputStream, OutputStreamOptions outputStreamOptions) { 145 // no need to close oS twice 146 this(outputStream, outputStreamOptions, outputStream, OutputStreamOptions.NONE); 147 } 148 149 /** 150 * Creates a new {@code DefaultLogger} instance. 151 * @param infoStream the {@code OutputStream} for info messages. 152 * @param infoStreamOptions if {@code CLOSE} info should be closed in auditFinished() 153 * @param errorStream the {@code OutputStream} for error messages. 154 * @param errorStreamOptions if {@code CLOSE} error should be closed in auditFinished() 155 */ 156 public DefaultLogger(OutputStream infoStream, 157 OutputStreamOptions infoStreamOptions, 158 OutputStream errorStream, 159 OutputStreamOptions errorStreamOptions) { 160 this(infoStream, infoStreamOptions, errorStream, errorStreamOptions, 161 new AuditEventDefaultFormatter()); 162 } 163 164 /** 165 * Creates a new {@code DefaultLogger} instance. 166 * 167 * @param infoStream the {@code OutputStream} for info messages 168 * @param infoStreamOptions if {@code CLOSE} info should be closed in auditFinished() 169 * @param errorStream the {@code OutputStream} for error messages 170 * @param errorStreamOptions if {@code CLOSE} error should be closed in auditFinished() 171 * @param messageFormatter formatter for the log message. 172 * @noinspection WeakerAccess 173 */ 174 public DefaultLogger(OutputStream infoStream, 175 OutputStreamOptions infoStreamOptions, 176 OutputStream errorStream, 177 OutputStreamOptions errorStreamOptions, 178 AuditEventFormatter messageFormatter) { 179 if (infoStreamOptions == null) { 180 throw new IllegalArgumentException("Parameter infoStreamOptions can not be null"); 181 } 182 closeInfo = infoStreamOptions == OutputStreamOptions.CLOSE; 183 if (errorStreamOptions == null) { 184 throw new IllegalArgumentException("Parameter errorStreamOptions can not be null"); 185 } 186 closeError = errorStreamOptions == OutputStreamOptions.CLOSE; 187 final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8); 188 infoWriter = new PrintWriter(infoStreamWriter); 189 190 if (infoStream == errorStream) { 191 errorWriter = infoWriter; 192 } 193 else { 194 final Writer errorStreamWriter = new OutputStreamWriter(errorStream, 195 StandardCharsets.UTF_8); 196 errorWriter = new PrintWriter(errorStreamWriter); 197 } 198 formatter = messageFormatter; 199 } 200 201 @Override 202 protected void finishLocalSetup() { 203 // No code by default 204 } 205 206 /** 207 * Print an Emacs compliant line on the error stream. 208 * If the column number is non zero, then also display it. 209 * @see AuditListener 210 **/ 211 @Override 212 public void addError(AuditEvent event) { 213 final SeverityLevel severityLevel = event.getSeverityLevel(); 214 if (severityLevel != SeverityLevel.IGNORE) { 215 final String errorMessage = formatter.format(event); 216 errorWriter.println(errorMessage); 217 } 218 } 219 220 @Override 221 public void addException(AuditEvent event, Throwable throwable) { 222 synchronized (errorWriter) { 223 final LocalizedMessage addExceptionMessage = new LocalizedMessage(1, 224 Definitions.CHECKSTYLE_BUNDLE, ADD_EXCEPTION_MESSAGE, 225 new String[] {event.getFileName()}, null, 226 LocalizedMessage.class, null); 227 errorWriter.println(addExceptionMessage.getMessage()); 228 throwable.printStackTrace(errorWriter); 229 } 230 } 231 232 @Override 233 public void auditStarted(AuditEvent event) { 234 final LocalizedMessage auditStartMessage = new LocalizedMessage(1, 235 Definitions.CHECKSTYLE_BUNDLE, AUDIT_STARTED_MESSAGE, null, null, 236 LocalizedMessage.class, null); 237 infoWriter.println(auditStartMessage.getMessage()); 238 infoWriter.flush(); 239 } 240 241 @Override 242 public void auditFinished(AuditEvent event) { 243 final LocalizedMessage auditFinishMessage = new LocalizedMessage(1, 244 Definitions.CHECKSTYLE_BUNDLE, AUDIT_FINISHED_MESSAGE, null, null, 245 LocalizedMessage.class, null); 246 infoWriter.println(auditFinishMessage.getMessage()); 247 closeStreams(); 248 } 249 250 @Override 251 public void fileStarted(AuditEvent event) { 252 // No need to implement this method in this class 253 } 254 255 @Override 256 public void fileFinished(AuditEvent event) { 257 infoWriter.flush(); 258 } 259 260 /** 261 * Flushes the output streams and closes them if needed. 262 */ 263 private void closeStreams() { 264 infoWriter.flush(); 265 if (closeInfo) { 266 infoWriter.close(); 267 } 268 269 errorWriter.flush(); 270 if (closeError) { 271 errorWriter.close(); 272 } 273 } 274 275}