import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
+import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static org.slf4j.Logger.ROOT_LOGGER_NAME;
Level level = Level.toLevel(value, Level.INFO);
if (!isAllowed(level)) {
- throw new IllegalArgumentException(String.format("log level %s in property %s is not a supported value (allowed levels are %s)",
+ throw new IllegalArgumentException(format("log level %s in property %s is not a supported value (allowed levels are %s)",
level, propertyKey, Arrays.toString(ALLOWED_ROOT_LOG_LEVELS)));
}
return level;
private static void ensureSupportedLevel(Level newLevel) {
if (!isAllowed(newLevel)) {
- throw new IllegalArgumentException(String.format("%s log level is not supported (allowed levels are %s)", newLevel, Arrays.toString(ALLOWED_ROOT_LOG_LEVELS)));
+ throw new IllegalArgumentException(format("%s log level is not supported (allowed levels are %s)", newLevel, Arrays.toString(ALLOWED_ROOT_LOG_LEVELS)));
}
}
return new NoRollingPolicy(ctx, filenamePrefix, logsDir, maxFiles);
} else {
- throw new MessageException(String.format("Unsupported value for property %s: %s", ROLLING_POLICY_PROPERTY, rollingPolicy));
+ throw new MessageException(format("Unsupported value for property %s: %s", ROLLING_POLICY_PROPERTY, rollingPolicy));
}
}
*/
package org.sonar.process;
+import static java.lang.String.format;
+
public enum ProcessId {
APP("app", 0, "sonar"),
sb.append(']');
return sb.toString();
}
+
+ public static ProcessId fromKey(String key) {
+ for (ProcessId processId : values()) {
+ if (processId.getKey().equals(key)) {
+ return processId;
+ }
+ }
+ throw new IllegalArgumentException(format("Process [%s] does not exist", key));
+ }
+
}
import java.util.HashSet;
import java.util.Set;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
public class ProcessIdTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Test
public void test_constants() {
assertThat(ProcessId.COMPUTE_ENGINE.getKey()).isEqualTo("ce");
assertThat(ipcIndices).hasSize(ProcessId.values().length);
assertThat(keys).hasSize(ProcessId.values().length);
}
+
+ @Test
+ public void fromKey_searches_process_by_its_key() {
+ assertThat(ProcessId.fromKey("app")).isEqualTo(ProcessId.APP);
+ assertThat(ProcessId.fromKey("ce")).isEqualTo(ProcessId.COMPUTE_ENGINE);
+ assertThat(ProcessId.fromKey("es")).isEqualTo(ProcessId.ELASTICSEARCH);
+ assertThat(ProcessId.fromKey("web")).isEqualTo(ProcessId.WEB_SERVER);
+ }
+
+ @Test
+ public void fromKey_throws_IAE_if_key_is_null() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Process [null] does not exist");
+
+ ProcessId.fromKey(null);
+ }
+
+ @Test
+ public void fromKey_throws_IAE_if_key_does_not_exist() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Process [foo] does not exist");
+
+ ProcessId.fromKey("foo");
+ }
}
package org.sonar.server.platform.ws;
import java.io.File;
+import java.net.HttpURLConnection;
import org.apache.commons.io.FileUtils;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
+import org.sonar.core.util.stream.Collectors;
+import org.sonar.process.ProcessId;
import org.sonar.server.platform.ServerLogging;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.MediaTypes;
+import static java.util.Arrays.stream;
+
public class LogsAction implements SystemWsAction {
+ private static final String PROCESS_PROPERTY = "process";
+
private final UserSession userSession;
private final ServerLogging serverLogging;
@Override
public void define(WebService.NewController controller) {
- controller.createAction("logs")
+ WebService.NewAction action = controller.createAction("logs")
.setDescription("Get system logs in plain-text format. Requires system administration permission.")
.setResponseExample(getClass().getResource("logs-example.log"))
.setSince("5.2")
.setHandler(this);
+
+ action
+ .createParam(PROCESS_PROPERTY)
+ .setPossibleValues(stream(ProcessId.values())
+ .map(ProcessId::getKey)
+ .sorted()
+ .collect(Collectors.toList(ProcessId.values().length)))
+ .setDefaultValue(ProcessId.APP.getKey())
+ .setSince("6.2")
+ .setDescription("Process to get logs from");
}
@Override
public void handle(Request wsRequest, Response wsResponse) throws Exception {
userSession.checkIsRoot();
- wsResponse.stream().setMediaType(MediaTypes.TXT);
- File file = serverLogging.getCurrentLogFile();
- if (file.exists()) {
+ String processKey = wsRequest.mandatoryParam(PROCESS_PROPERTY);
+ ProcessId processId = ProcessId.fromKey(processKey);
+
+ File logsDir = serverLogging.getLogsDir();
+ File file = new File(logsDir, processId.getLogFilenamePrefix() + ".log");
+ // filenames are defined in the enum LogProcess. Still to prevent any vulnerability,
+ // path is double-checked to prevent returning any file present on the file system.
+ if (file.exists() && file.getParentFile().equals(logsDir)) {
+ wsResponse.stream().setMediaType(MediaTypes.TXT);
FileUtils.copyFile(file, wsResponse.stream().output());
+ } else {
+ wsResponse.stream().setStatus(HttpURLConnection.HTTP_NOT_FOUND);
}
}
}
import java.io.File;
import java.io.IOException;
+import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.junit.Rule;
import org.junit.Test;
private LogsAction underTest = new LogsAction(userSession, serverLogging);
private WsActionTester actionTester = new WsActionTester(underTest);
+ @Test
+ public void values_of_process_parameter_are_names_of_processes() {
+ Set<String> values = actionTester.getDef().param("process").possibleValues();
+ // values are lower-case and alphabetically ordered
+ assertThat(values).containsExactly("app", "ce", "es", "web");
+ }
+
@Test
public void request_fails_with_ForbiddenException_when_user_is_not_logged_in() {
expectedException.expect(ForbiddenException.class);
}
@Test
- public void get_logs() throws IOException {
+ public void get_app_logs_by_default() throws IOException {
makeAuthenticatedUserRoot();
- File file = temp.newFile();
- FileUtils.write(file, "{logs}");
- when(serverLogging.getCurrentLogFile()).thenReturn(file);
+ createAllLogsFiles();
TestResponse response = actionTester.newRequest().execute();
assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
- assertThat(response.getInput()).isEqualTo("{logs}");
+ assertThat(response.getInput()).isEqualTo("{app}");
}
@Test
- public void get_empty_logs_if_file_does_not_exist() throws IOException {
+ public void return_404_not_found_if_file_does_not_exist() throws IOException {
makeAuthenticatedUserRoot();
- File file = temp.newFile();
- file.delete();
- when(serverLogging.getCurrentLogFile()).thenReturn(file);
+ createLogsDir();
TestResponse response = actionTester.newRequest().execute();
+ assertThat(response.getStatus()).isEqualTo(404);
+ }
+
+ @Test
+ public void get_ce_logs() throws IOException {
+ makeAuthenticatedUserRoot();
+
+ createAllLogsFiles();
+
+ TestResponse response = actionTester.newRequest()
+ .setParam("process", "ce")
+ .execute();
+ assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
+ assertThat(response.getInput()).isEqualTo("{ce}");
+ }
+
+ @Test
+ public void get_es_logs() throws IOException {
+ makeAuthenticatedUserRoot();
+
+ createAllLogsFiles();
+
+ TestResponse response = actionTester.newRequest()
+ .setParam("process", "es")
+ .execute();
+ assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
+ assertThat(response.getInput()).isEqualTo("{es}");
+ }
+
+ @Test
+ public void get_web_logs() throws IOException {
+ makeAuthenticatedUserRoot();
+
+ createAllLogsFiles();
+
+ TestResponse response = actionTester.newRequest()
+ .setParam("process", "web")
+ .execute();
assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
- assertThat(response.getInput()).isEqualTo("");
+ assertThat(response.getInput()).isEqualTo("{web}");
+ }
+
+ @Test
+ public void do_not_return_rotated_files() throws IOException {
+ makeAuthenticatedUserRoot();
+
+ File dir = createLogsDir();
+ FileUtils.write(new File(dir, "sonar.1.log"), "{old}");
+ FileUtils.write(new File(dir, "sonar.log"), "{recent}");
+
+ TestResponse response = actionTester.newRequest()
+ .setParam("process", "app")
+ .execute();
+ assertThat(response.getMediaType()).isEqualTo(MediaTypes.TXT);
+ assertThat(response.getInput()).isEqualTo("{recent}");
+ }
+
+ private File createAllLogsFiles() throws IOException {
+ File dir = createLogsDir();
+ FileUtils.write(new File(dir, "sonar.log"), "{app}");
+ FileUtils.write(new File(dir, "ce.log"), "{ce}");
+ FileUtils.write(new File(dir, "es.log"), "{es}");
+ FileUtils.write(new File(dir, "web.log"), "{web}");
+ return dir;
+ }
+
+ private File createLogsDir() throws IOException {
+ File dir = temp.newFolder();
+ when(serverLogging.getLogsDir()).thenReturn(dir);
+ return dir;
}
private void makeAuthenticatedUserRoot() {