Add quiet option to disable console logging (#20422)

This commit adds a -q/--quiet option to Elasticsearch so that it does not log anything in the console and closes stdout & stderr streams. This is useful for SystemD to avoid duplicate logs in both journalctl and /var/log/elasticsearch/elasticsearch.log while still allows the JVM to print error messages in stdout/stderr if needed.

closes #17220
This commit is contained in:
Tanguy Leroux 2016-09-12 16:56:13 +02:00
parent c7bfbe3e69
commit 6090c51fc5
11 changed files with 103 additions and 29 deletions

View File

@ -368,7 +368,8 @@ These are the linux flavors the Vagrantfile currently supports:
* debian-8 aka jessie, the current debian stable distribution
* centos-6
* centos-7
* fedora-22
* fedora-24
* oel-6 aka Oracle Enterprise Linux 6
* oel-7 aka Oracle Enterprise Linux 7
* sles-12
* opensuse-13
@ -377,7 +378,6 @@ We're missing the following from the support matrix because there aren't high
quality boxes available in vagrant atlas:
* sles-11
* oel-6
We're missing the follow because our tests are very linux/bash centric:

View File

@ -227,12 +227,12 @@ final class Bootstrap {
}
/**
* This method is invoked by {@link Elasticsearch#main(String[])}
* to startup elasticsearch.
* This method is invoked by {@link Elasticsearch#main(String[])} to startup elasticsearch.
*/
static void init(
final boolean foreground,
final Path pidFile,
final boolean quiet,
final Map<String, String> esSettings) throws BootstrapException, NodeValidationException {
// Set the system property before anything has a chance to trigger its use
initLoggerPrefix();
@ -259,8 +259,9 @@ final class Bootstrap {
}
}
final boolean closeStandardStreams = (foreground == false) || quiet;
try {
if (!foreground) {
if (closeStandardStreams) {
final Logger rootLogger = ESLoggerFactory.getRootLogger();
final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
if (maybeConsoleAppender != null) {
@ -285,7 +286,7 @@ final class Bootstrap {
INSTANCE.start();
if (!foreground) {
if (closeStandardStreams) {
closeSysError();
}
} catch (NodeValidationException | RuntimeException e) {

View File

@ -26,7 +26,7 @@ import java.util.Map;
* Wrapper exception for checked exceptions thrown during the bootstrap process. Methods invoked
* during bootstrap should explicitly declare the checked exceptions that they can throw, rather
* than declaring the top-level checked exception {@link Exception}. This exception exists to wrap
* these checked exceptions so that {@link Bootstrap#init(boolean, Path, Map)} does not have to
* these checked exceptions so that {@link Bootstrap#init(boolean, Path, boolean, Map)} does not have to
* declare all of these checked exceptions.
*/
class BootstrapException extends Exception {

View File

@ -44,6 +44,7 @@ class Elasticsearch extends SettingCommand {
private final OptionSpecBuilder versionOption;
private final OptionSpecBuilder daemonizeOption;
private final OptionSpec<Path> pidfileOption;
private final OptionSpecBuilder quietOption;
// visible for testing
Elasticsearch() {
@ -58,6 +59,10 @@ class Elasticsearch extends SettingCommand {
.availableUnless(versionOption)
.withRequiredArg()
.withValuesConvertedBy(new PathConverter());
quietOption = parser.acceptsAll(Arrays.asList("q", "quiet"),
"Turns off standard ouput/error streams logging in console")
.availableUnless(versionOption)
.availableUnless(daemonizeOption);
}
/**
@ -92,17 +97,19 @@ class Elasticsearch extends SettingCommand {
final boolean daemonize = options.has(daemonizeOption);
final Path pidFile = pidfileOption.value(options);
final boolean quiet = options.has(quietOption);
try {
init(daemonize, pidFile, settings);
init(daemonize, pidFile, quiet, settings);
} catch (NodeValidationException e) {
throw new UserException(ExitCodes.CONFIG, e.getMessage());
}
}
void init(final boolean daemonize, final Path pidFile, final Map<String, String> esSettings) throws NodeValidationException {
void init(final boolean daemonize, final Path pidFile, final boolean quiet, final Map<String, String> esSettings)
throws NodeValidationException {
try {
Bootstrap.init(!daemonize, pidFile, esSettings);
Bootstrap.init(!daemonize, pidFile, quiet, esSettings);
} catch (BootstrapException | RuntimeException e) {
// format exceptions to the console in a special way
// to avoid 2MB stacktraces from guice, etc.

View File

@ -43,6 +43,9 @@ public class ElasticsearchCliTests extends ESElasticsearchCliTestCase {
runTestThatVersionIsMutuallyExclusiveToOtherOptions("--version", "--daemonize");
runTestThatVersionIsMutuallyExclusiveToOtherOptions("--version", "-p", "/tmp/pid");
runTestThatVersionIsMutuallyExclusiveToOtherOptions("--version", "--pidfile", "/tmp/pid");
runTestThatVersionIsMutuallyExclusiveToOtherOptions("--version", "-q");
runTestThatVersionIsMutuallyExclusiveToOtherOptions("--version", "--quiet");
runTestThatVersionIsReturned("-V");
runTestThatVersionIsReturned("--version");
}
@ -66,7 +69,7 @@ public class ElasticsearchCliTests extends ESElasticsearchCliTestCase {
}
private void runTestVersion(int expectedStatus, Consumer<String> outputConsumer, String... args) throws Exception {
runTest(expectedStatus, false, outputConsumer, (foreground, pidFile, esSettings) -> {}, args);
runTest(expectedStatus, false, outputConsumer, (foreground, pidFile, quiet, esSettings) -> {}, args);
}
public void testPositionalArgs() throws Exception {
@ -74,21 +77,21 @@ public class ElasticsearchCliTests extends ESElasticsearchCliTestCase {
ExitCodes.USAGE,
false,
output -> assertThat(output, containsString("Positional arguments not allowed, found [foo]")),
(foreground, pidFile, esSettings) -> {},
(foreground, pidFile, quiet, esSettings) -> {},
"foo"
);
runTest(
ExitCodes.USAGE,
false,
output -> assertThat(output, containsString("Positional arguments not allowed, found [foo, bar]")),
(foreground, pidFile, esSettings) -> {},
(foreground, pidFile, quiet, esSettings) -> {},
"foo", "bar"
);
runTest(
ExitCodes.USAGE,
false,
output -> assertThat(output, containsString("Positional arguments not allowed, found [foo]")),
(foreground, pidFile, esSettings) -> {},
(foreground, pidFile, quiet, esSettings) -> {},
"-E", "foo=bar", "foo", "-E", "baz=qux"
);
}
@ -109,7 +112,7 @@ public class ElasticsearchCliTests extends ESElasticsearchCliTestCase {
expectedStatus,
expectedInit,
outputConsumer,
(foreground, pidFile, esSettings) -> assertThat(pidFile.toString(), equalTo(expectedPidFile.toString())),
(foreground, pidFile, quiet, esSettings) -> assertThat(pidFile.toString(), equalTo(expectedPidFile.toString())),
args);
}
@ -124,7 +127,22 @@ public class ElasticsearchCliTests extends ESElasticsearchCliTestCase {
ExitCodes.OK,
true,
output -> {},
(foreground, pidFile, esSettings) -> assertThat(foreground, equalTo(!expectedDaemonize)),
(foreground, pidFile, quiet, esSettings) -> assertThat(foreground, equalTo(!expectedDaemonize)),
args);
}
public void testThatParsingQuietOptionWorks() throws Exception {
runQuietTest(true, "-q");
runQuietTest(true, "--quiet");
runQuietTest(false);
}
private void runQuietTest(final boolean expectedQuiet, final String... args) throws Exception {
runTest(
ExitCodes.OK,
true,
output -> {},
(foreground, pidFile, quiet, esSettings) -> assertThat(quiet, equalTo(expectedQuiet)),
args);
}
@ -133,7 +151,7 @@ public class ElasticsearchCliTests extends ESElasticsearchCliTestCase {
ExitCodes.OK,
true,
output -> {},
(foreground, pidFile, esSettings) -> {
(foreground, pidFile, quiet, esSettings) -> {
assertThat(esSettings.size(), equalTo(2));
assertThat(esSettings, hasEntry("foo", "bar"));
assertThat(esSettings, hasEntry("baz", "qux"));
@ -147,7 +165,7 @@ public class ElasticsearchCliTests extends ESElasticsearchCliTestCase {
ExitCodes.USAGE,
false,
output -> assertThat(output, containsString("Setting [foo] must not be empty")),
(foreground, pidFile, esSettings) -> {},
(foreground, pidFile, quiet, esSettings) -> {},
"-E", "foo="
);
}
@ -157,7 +175,7 @@ public class ElasticsearchCliTests extends ESElasticsearchCliTestCase {
ExitCodes.USAGE,
false,
output -> assertThat(output, containsString("network.host is not a recognized option")),
(foreground, pidFile, esSettings) -> {},
(foreground, pidFile, quiet, esSettings) -> {},
"--network.host");
}

View File

@ -21,10 +21,17 @@ ExecStartPre=/usr/share/elasticsearch/bin/elasticsearch-systemd-pre-exec
ExecStart=/usr/share/elasticsearch/bin/elasticsearch \
-p ${PID_DIR}/elasticsearch.pid \
--quiet \
-Edefault.path.logs=${LOG_DIR} \
-Edefault.path.data=${DATA_DIR} \
-Edefault.path.conf=${CONF_DIR}
# StandardOutput is configured to redirect to journalctl since
# some error messages may be logged in standard output before
# elasticsearch logging system is initialized. Elasticsearch
# stores its logs in /var/log/elasticsearch and does not use
# journalctl by default. If you also want to enable journalctl
# logging, you can simply remove the "quiet" option from ExecStart.
StandardOutput=journal
StandardError=inherit

View File

@ -18,13 +18,36 @@ sudo systemctl stop elasticsearch.service
--------------------------------------------
These commands provide no feedback as to whether Elasticsearch was started
successfully or not. Instead, this information will be written to the
`systemd` journal, which can be tailed as follows:
successfully or not. Instead, this information will be written in the log
files located in `/var/log/elasticsearch/`.
By default the Elasticsearch service doesn't log information in the `systemd`
journal. To enable `journalctl` logging, the `--quiet` option must be removed
from the `ExecStart` command line in the `elasticsearch.service` file.
When `systemd` logging is enabled, the logging information are available using
the `journalctl` commands:
To tail the journal:
[source,sh]
--------------------------------------------
sudo journalctl -f
--------------------------------------------
Log files can be found in `/var/log/elasticsearch/`.
To list journal entries for the elasticsearch service:
[source,sh]
--------------------------------------------
sudo journalctl --unit elasticsearch
--------------------------------------------
To list journal entries for the elasticsearch service starting from a given time:
[source,sh]
--------------------------------------------
sudo journalctl --unit elasticsearch --since "2016-10-30 18:17:16"
--------------------------------------------
Check `man journalctl` or https://www.freedesktop.org/software/systemd/man/journalctl.html for
more command line options.

View File

@ -52,11 +52,14 @@ Elasticsearch can be started from the command line as follows:
./bin/elasticsearch
--------------------------------------------
By default, Elasticsearch runs in the foreground, prints its logs to `STDOUT`,
and can be stopped by pressing `Ctrl-C`.
By default, Elasticsearch runs in the foreground, prints its logs to the
standard output (`stdout`), and can be stopped by pressing `Ctrl-C`.
include::check-running.asciidoc[]
Log printing to `stdout` can be disabled using the `-q` or `--quiet`
option on the command line.
[[setup-installation-daemon]]
==== Running as a daemon

View File

@ -38,7 +38,7 @@ public class EvilElasticsearchCliTests extends ESElasticsearchCliTestCase {
ExitCodes.OK,
true,
output -> {},
(foreground, pidFile, esSettings) -> {
(foreground, pidFile, quiet, esSettings) -> {
assertThat(esSettings.size(), equalTo(1));
assertThat(esSettings, hasEntry("path.home", value));
});
@ -49,7 +49,7 @@ public class EvilElasticsearchCliTests extends ESElasticsearchCliTestCase {
ExitCodes.OK,
true,
output -> {},
(foreground, pidFile, esSettings) -> {
(foreground, pidFile, quiet, esSettings) -> {
assertThat(esSettings.size(), equalTo(1));
assertThat(esSettings, hasEntry("path.home", commandLineValue));
},

View File

@ -68,9 +68,24 @@ setup() {
# starting Elasticsearch so we don't have to wait for elasticsearch to scan for
# them.
install_elasticsearch_test_scripts
# Capture the current epoch in millis
run date +%s
epoch="$output"
systemctl start elasticsearch.service
wait_for_elasticsearch_status
assert_file_exist "/var/run/elasticsearch/elasticsearch.pid"
assert_file_exist "/var/log/elasticsearch/elasticsearch.log"
# Converts the epoch back in a human readable format
run date --date=@$epoch "+%Y-%m-%d %H:%M:%S"
since="$output"
# Verifies that no new entries in journald have been added
# since the last start
run journalctl _SYSTEMD_UNIT=elasticsearch.service --since "$since"
[ "$status" -eq 1 ]
}
@test "[SYSTEMD] start (running)" {

View File

@ -32,7 +32,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
abstract class ESElasticsearchCliTestCase extends ESTestCase {
interface InitConsumer {
void accept(final boolean foreground, final Path pidFile, final Map<String, String> esSettings);
void accept(final boolean foreground, final Path pidFile, final boolean quiet, final Map<String, String> esSettings);
}
void runTest(
@ -46,9 +46,9 @@ abstract class ESElasticsearchCliTestCase extends ESTestCase {
final AtomicBoolean init = new AtomicBoolean();
final int status = Elasticsearch.main(args, new Elasticsearch() {
@Override
void init(final boolean daemonize, final Path pidFile, final Map<String, String> esSettings) {
void init(final boolean daemonize, final Path pidFile, final boolean quiet, final Map<String, String> esSettings) {
init.set(true);
initConsumer.accept(!daemonize, pidFile, esSettings);
initConsumer.accept(!daemonize, pidFile, quiet, esSettings);
}
}, terminal);
assertThat(status, equalTo(expectedStatus));