@@ -22,7 +22,6 @@ package org.sonar.ce.logging; | |||
import ch.qos.logback.classic.Level; | |||
import ch.qos.logback.classic.Logger; | |||
import ch.qos.logback.classic.LoggerContext; | |||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import ch.qos.logback.core.Appender; | |||
import ch.qos.logback.core.ConsoleAppender; | |||
@@ -40,6 +39,7 @@ import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.process.Props; | |||
import org.sonar.process.logging.LogbackHelper; | |||
import org.sonar.process.logging.PatternLayoutEncoder; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.slf4j.Logger.ROOT_LOGGER_NAME; |
@@ -22,7 +22,6 @@ package org.sonar.application; | |||
import ch.qos.logback.classic.Level; | |||
import ch.qos.logback.classic.Logger; | |||
import ch.qos.logback.classic.LoggerContext; | |||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import ch.qos.logback.core.ConsoleAppender; | |||
import ch.qos.logback.core.FileAppender; | |||
@@ -32,6 +31,7 @@ import org.sonar.application.process.StreamGobbler; | |||
import org.sonar.process.ProcessId; | |||
import org.sonar.process.logging.LogLevelConfig; | |||
import org.sonar.process.logging.LogbackHelper; | |||
import org.sonar.process.logging.PatternLayoutEncoder; | |||
import org.sonar.process.logging.RootLoggerConfig; | |||
import static org.slf4j.Logger.ROOT_LOGGER_NAME; |
@@ -22,7 +22,6 @@ package org.sonar.application; | |||
import ch.qos.logback.classic.Level; | |||
import ch.qos.logback.classic.Logger; | |||
import ch.qos.logback.classic.LoggerContext; | |||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import ch.qos.logback.core.Appender; | |||
import ch.qos.logback.core.ConsoleAppender; | |||
@@ -44,6 +43,7 @@ import org.sonar.application.config.AppSettings; | |||
import org.sonar.application.config.TestAppSettings; | |||
import org.sonar.process.logging.LogbackHelper; | |||
import org.sonar.process.logging.LogbackJsonLayout; | |||
import org.sonar.process.logging.PatternLayoutEncoder; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.slf4j.Logger.ROOT_LOGGER_NAME; |
@@ -0,0 +1,44 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.process.logging; | |||
import ch.qos.logback.classic.pattern.ClassicConverter; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import java.util.regex.Pattern; | |||
/** | |||
* Escapes log message which contains CR LF sequence | |||
*/ | |||
public class EscapedMessageConverter extends ClassicConverter { | |||
private static final Pattern CR_PATTERN = Pattern.compile("\r"); | |||
private static final Pattern LF_PATTERN = Pattern.compile("\n"); | |||
public String convert(ILoggingEvent event) { | |||
String formattedMessage = event.getFormattedMessage(); | |||
if (formattedMessage != null) { | |||
String result = CR_PATTERN.matcher(formattedMessage).replaceAll("\\\\r"); | |||
result = LF_PATTERN.matcher(result).replaceAll("\\\\n"); | |||
return result; | |||
} | |||
return null; | |||
} | |||
} |
@@ -22,7 +22,6 @@ package org.sonar.process.logging; | |||
import ch.qos.logback.classic.Level; | |||
import ch.qos.logback.classic.Logger; | |||
import ch.qos.logback.classic.LoggerContext; | |||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||
import ch.qos.logback.classic.joran.JoranConfigurator; | |||
import ch.qos.logback.classic.jul.LevelChangePropagator; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; |
@@ -0,0 +1,49 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.process.logging; | |||
import ch.qos.logback.classic.PatternLayout; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import ch.qos.logback.core.pattern.PatternLayoutEncoderBase; | |||
import com.google.common.collect.ImmutableMap; | |||
import java.util.Map; | |||
public class PatternLayoutEncoder extends PatternLayoutEncoderBase<ILoggingEvent> { | |||
@Override | |||
public void start() { | |||
PatternLayout patternLayout = new PatternLayout(); | |||
patternLayout.getDefaultConverterMap().putAll(getEscapedMessageConverterConfig()); | |||
patternLayout.setContext(context); | |||
patternLayout.setPattern(getPattern()); | |||
patternLayout.setOutputPatternAsHeader(outputPatternAsHeader); | |||
patternLayout.start(); | |||
this.layout = patternLayout; | |||
super.start(); | |||
} | |||
private static Map<String, String> getEscapedMessageConverterConfig() { | |||
return ImmutableMap.of( | |||
"m", EscapedMessageConverter.class.getName(), | |||
"msg", EscapedMessageConverter.class.getName(), | |||
"message", EscapedMessageConverter.class.getName()); | |||
} | |||
} |
@@ -0,0 +1,65 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.process.logging; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class EscapedMessageConverterTest { | |||
private final EscapedMessageConverter underTest = new EscapedMessageConverter(); | |||
@Test | |||
public void convert_null_message() { | |||
ILoggingEvent event = createILoggingEvent(null); | |||
assertThat(underTest.convert(event)).isNull(); | |||
} | |||
@Test | |||
public void convert_simple_message() { | |||
ILoggingEvent event = createILoggingEvent("simple message"); | |||
assertThat(underTest.convert(event)).isEqualTo("simple message"); | |||
} | |||
@Test | |||
public void convert_message_with_CR() { | |||
ILoggingEvent event = createILoggingEvent("simple\r message\r with\r CR\r"); | |||
assertThat(underTest.convert(event)).isEqualTo("simple\\r message\\r with\\r CR\\r"); | |||
} | |||
@Test | |||
public void convert_message_with_LF() { | |||
ILoggingEvent event = createILoggingEvent("simple\n message\n with\n LF"); | |||
assertThat(underTest.convert(event)).isEqualTo("simple\\n message\\n with\\n LF"); | |||
} | |||
@Test | |||
public void convert_message_with_CRLF() { | |||
ILoggingEvent event = createILoggingEvent("simple\n\r\n message\r with\r\n CR LF"); | |||
assertThat(underTest.convert(event)).isEqualTo("simple\\n\\r\\n message\\r with\\r\\n CR LF"); | |||
} | |||
private static ILoggingEvent createILoggingEvent(String message) { | |||
return new TestILoggingEvent(message); | |||
} | |||
} |
@@ -21,7 +21,6 @@ package org.sonar.process.logging; | |||
import ch.qos.logback.classic.Level; | |||
import ch.qos.logback.classic.LoggerContext; | |||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import ch.qos.logback.classic.spi.LoggerContextListener; | |||
import ch.qos.logback.core.Appender; |
@@ -0,0 +1,49 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.process.logging; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.entry; | |||
public class PatternLayoutEncoderTest { | |||
PatternLayoutEncoder underTest = new PatternLayoutEncoder(); | |||
@Before | |||
public void before() { | |||
underTest.start(); | |||
} | |||
@Test | |||
public void start_should_initialize_escaped_message_converter() { | |||
assertThat(underTest.getLayout()) | |||
.isInstanceOf(ch.qos.logback.classic.PatternLayout.class); | |||
assertThat(((ch.qos.logback.classic.PatternLayout) underTest.getLayout()).getDefaultConverterMap()) | |||
.contains( | |||
entry("m", EscapedMessageConverter.class.getName()), | |||
entry("msg", EscapedMessageConverter.class.getName()), | |||
entry("message", EscapedMessageConverter.class.getName())); | |||
} | |||
} |
@@ -0,0 +1,110 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.process.logging; | |||
import ch.qos.logback.classic.Level; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import ch.qos.logback.classic.spi.IThrowableProxy; | |||
import ch.qos.logback.classic.spi.LoggerContextVO; | |||
import java.util.Map; | |||
import org.slf4j.Marker; | |||
public class TestILoggingEvent implements ILoggingEvent { | |||
private String formattedMessage; | |||
public TestILoggingEvent(String formattedMessage) { | |||
this.formattedMessage = formattedMessage; | |||
} | |||
@Override | |||
public String getThreadName() { | |||
return null; | |||
} | |||
@Override | |||
public Level getLevel() { | |||
return null; | |||
} | |||
@Override | |||
public String getMessage() { | |||
return null; | |||
} | |||
@Override | |||
public Object[] getArgumentArray() { | |||
return null; | |||
} | |||
@Override | |||
public String getFormattedMessage() { | |||
return this.formattedMessage; | |||
} | |||
@Override | |||
public String getLoggerName() { | |||
return null; | |||
} | |||
@Override | |||
public LoggerContextVO getLoggerContextVO() { | |||
return null; | |||
} | |||
@Override | |||
public IThrowableProxy getThrowableProxy() { | |||
return null; | |||
} | |||
@Override | |||
public StackTraceElement[] getCallerData() { | |||
return new StackTraceElement[0]; | |||
} | |||
@Override | |||
public boolean hasCallerData() { | |||
return false; | |||
} | |||
@Override | |||
public Marker getMarker() { | |||
return null; | |||
} | |||
@Override | |||
public Map<String, String> getMDCPropertyMap() { | |||
return null; | |||
} | |||
@Override | |||
public Map<String, String> getMdc() { | |||
return null; | |||
} | |||
@Override | |||
public long getTimeStamp() { | |||
return 0; | |||
} | |||
@Override | |||
public void prepareForDeferredProcessing() { | |||
} | |||
} |
@@ -22,7 +22,6 @@ package org.sonar.server.app; | |||
import ch.qos.logback.classic.Level; | |||
import ch.qos.logback.classic.Logger; | |||
import ch.qos.logback.classic.LoggerContext; | |||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |||
import ch.qos.logback.classic.spi.ILoggingEvent; | |||
import ch.qos.logback.core.Appender; | |||
import ch.qos.logback.core.AppenderBase; | |||
@@ -48,6 +47,7 @@ import org.junit.rules.TemporaryFolder; | |||
import org.sonar.process.Props; | |||
import org.sonar.process.logging.LogbackHelper; | |||
import org.sonar.process.logging.LogbackJsonLayout; | |||
import org.sonar.process.logging.PatternLayoutEncoder; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.slf4j.Logger.ROOT_LOGGER_NAME; | |||
@@ -508,10 +508,10 @@ public class WebServerProcessLoggingTest { | |||
LoggerContext context = underTest.configure(props); | |||
Logger rootLogger = context.getLogger(ROOT_LOGGER_NAME); | |||
OutputStreamAppender appender = (OutputStreamAppender)rootLogger.getAppender("file_web"); | |||
OutputStreamAppender appender = (OutputStreamAppender) rootLogger.getAppender("file_web"); | |||
Encoder<ILoggingEvent> encoder = appender.getEncoder(); | |||
assertThat(encoder).isInstanceOf(LayoutWrappingEncoder.class); | |||
assertThat(((LayoutWrappingEncoder)encoder).getLayout()).isInstanceOf(LogbackJsonLayout.class); | |||
assertThat(((LayoutWrappingEncoder) encoder).getLayout()).isInstanceOf(LogbackJsonLayout.class); | |||
} | |||
private void verifyRootLogLevel(LoggerContext ctx, Level expected) { |