Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send all the Diagnostics logs to the stdout, instead of local files #16941

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -104,6 +104,13 @@ public class Diagnostics {
public static final HazelcastProperty FILENAME_PREFIX
= new HazelcastProperty("hazelcast.diagnostics.filename.prefix");

/**
* Send all the Diagnostics logs to the stdout, instead of storing on the file system.
* <p>
* The default is false.
*/
public static final HazelcastProperty STDOUT = new HazelcastProperty("hazelcast.diagnostics.stdout", false);

final AtomicReference<DiagnosticsPlugin[]> staticTasks = new AtomicReference<>(
new DiagnosticsPlugin[0]
);
Expand All @@ -114,10 +121,11 @@ public class Diagnostics {
final boolean includeEpochTime;
final File directory;

DiagnosticsLogFile diagnosticsLogFile;
DiagnosticsLogOutput diagnosticsLogOutput;

private final ConcurrentMap<Class<? extends DiagnosticsPlugin>, DiagnosticsPlugin> pluginsMap = new ConcurrentHashMap<>();
private final boolean enabled;
private final boolean stdout;

private ScheduledExecutorService scheduler;

Expand All @@ -130,11 +138,15 @@ public Diagnostics(String baseFileName, ILogger logger, String hzName, Hazelcast
this.includeEpochTime = properties.getBoolean(INCLUDE_EPOCH_TIME);
this.directory = new File(properties.getString(DIRECTORY));
this.enabled = properties.getBoolean(ENABLED);
this.stdout = properties.getBoolean(STDOUT);
}

// just for testing (returns the current file the system is writing to)
public File currentFile() {
return diagnosticsLogFile.file;
public File currentFile() throws UnsupportedOperationException {
if (stdout) {
throw new UnsupportedOperationException();
}
return ((DiagnosticsLogFile) diagnosticsLogOutput).file;
}

/**
Expand Down Expand Up @@ -207,7 +219,12 @@ public void start() {
return;
}

this.diagnosticsLogFile = new DiagnosticsLogFile(this);
if (stdout) {
buraksezer marked this conversation as resolved.
Show resolved Hide resolved
this.diagnosticsLogOutput = new DiagnosticsLogStdout(this);
} else {
this.diagnosticsLogOutput = new DiagnosticsLogFile(this);
}

this.scheduler = new ScheduledThreadPoolExecutor(1, new DiagnosticSchedulerThreadFactory());

logger.info("Diagnostics started");
Expand All @@ -234,7 +251,7 @@ private class WritePluginTask implements Runnable {
@Override
public void run() {
try {
diagnosticsLogFile.write(plugin);
diagnosticsLogOutput.write(plugin);
} catch (Throwable t) {
// we need to catch any exception; otherwise the task is going to be removed by the scheduler
logger.severe(t);
Expand Down
Expand Up @@ -41,7 +41,7 @@
* <p>
* Should only be called from the {@link Diagnostics}.
*/
final class DiagnosticsLogFile {
final class DiagnosticsLogFile implements DiagnosticsLogOutput {

private static final int ONE_MB = 1024 * 1024;

Expand Down
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hazelcast.internal.diagnostics;

public interface DiagnosticsLogOutput {
buraksezer marked this conversation as resolved.
Show resolved Hide resolved
void write(DiagnosticsPlugin plugin);
}
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hazelcast.internal.diagnostics;

import com.hazelcast.logging.ILogger;

import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;

/**
* Represents the DiagnosticsLogStdout.
* <p>
* Should only be called from the {@link Diagnostics}.
*/
final class DiagnosticsLogStdout implements DiagnosticsLogOutput {
private final Diagnostics diagnostics;
private final ILogger logger;
private final DiagnosticsLogWriterImpl logWriter;
private final PrintWriter printWriter;
private boolean staticPluginsRendered;

DiagnosticsLogStdout(Diagnostics diagnostics) {
this.diagnostics = diagnostics;
this.logger = diagnostics.logger;
this.logWriter = new DiagnosticsLogWriterImpl(diagnostics.includeEpochTime);
this.printWriter = newWriter();
logWriter.init(printWriter);
}

public void write(DiagnosticsPlugin plugin) {
try {
if (!staticPluginsRendered) {
renderStaticPlugins();
staticPluginsRendered = true;
}

renderPlugin(plugin);
printWriter.flush();
} catch (RuntimeException e) {
logger.warning("Failed to write stdout: ", e);
}
}

private void renderStaticPlugins() {
for (DiagnosticsPlugin plugin : diagnostics.staticTasks.get()) {
renderPlugin(plugin);
}
}

private void renderPlugin(DiagnosticsPlugin plugin) {
plugin.run(logWriter);
}

private PrintWriter newWriter() {
CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
return new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out, encoder), Short.MAX_VALUE));
}
}
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hazelcast.internal.diagnostics;

import com.hazelcast.config.Config;
import com.hazelcast.test.HazelcastSerialClassRunner;
import com.hazelcast.test.HazelcastTestSupport;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.After;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;


import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.IOException;

import static com.hazelcast.internal.nio.IOUtil.closeResource;
import static com.hazelcast.internal.util.StringUtil.LINE_SEPARATOR;
import static org.junit.Assert.assertNotNull;

@RunWith(HazelcastSerialClassRunner.class)
@Category(QuickTest.class)
public class DiagnosticsLogStdoutTest extends HazelcastTestSupport {

private PrintStream stdout;
private ByteArrayOutputStream fakeStdout;

@After
public void teardown() {
System.setOut(stdout);
}

@Test
public void testStdoutContent() {
setup();
assertTrueEventually(() -> {
String content = stdoutToString(fakeStdout);
assertNotNull(content);

assertContains(content, "SystemProperties[");
assertContains(content, "BuildInfo[");
assertContains(content, "ConfigProperties[");
assertContains(content, "Metric[");

});
}

private void setup() {
stdout = System.out;
fakeStdout = new ByteArrayOutputStream();
System.setOut(new PrintStream(fakeStdout));

Config config = new Config()
.setProperty(Diagnostics.ENABLED.getName(), "true")
.setProperty(Diagnostics.STDOUT.getName(), "true")
.setProperty(MetricsPlugin.PERIOD_SECONDS.getName(), "1");

createHazelcastInstance(config);
}

private static String stdoutToString(ByteArrayOutputStream out) {
byte[] content = out.toByteArray();
BufferedReader br = null;
InputStream is;
try {
is = new ByteArrayInputStream(content);
br = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = br.readLine();

while (line != null) {
sb.append(line);
sb.append(LINE_SEPARATOR);
line = br.readLine();
}
return sb.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
closeResource(br);
}
}
}
Expand Up @@ -114,7 +114,7 @@ private void setup(String maxFileSize) {
HazelcastInstance hz = createHazelcastInstance(config);

diagnostics = AbstractDiagnosticsPluginTest.getDiagnostics(hz);
diagnosticsLogFile = diagnostics.diagnosticsLogFile;
diagnosticsLogFile = (DiagnosticsLogFile) diagnostics.diagnosticsLogOutput;
metricsRegistry = getMetricsRegistry(hz);
}

Expand Down
Expand Up @@ -125,15 +125,15 @@ public void start_whenDisabled() throws Exception {
Diagnostics diagnostics = newDiagnostics(new Config().setProperty(Diagnostics.ENABLED.getName(), "false"));
diagnostics.start();

assertNull("DiagnosticsLogFile should be null", diagnostics.diagnosticsLogFile);
assertNull("DiagnosticsLogFile should be null", diagnostics.diagnosticsLogOutput);
}

@Test
public void start_whenEnabled() throws Exception {
Diagnostics diagnostics = newDiagnostics(new Config().setProperty(Diagnostics.ENABLED.getName(), "true"));
diagnostics.start();

assertNotNull("DiagnosticsLogFile should not be null", diagnostics.diagnosticsLogFile);
assertNotNull("DiagnosticsLogFile should not be null", diagnostics.diagnosticsLogOutput);
}

private Diagnostics newDiagnostics(Config config) throws Exception {
Expand Down