
com.hazelcast.internal.monitors.PerformanceLogFile Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2016, 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.monitors;
import com.hazelcast.core.Member;
import com.hazelcast.instance.BuildInfo;
import com.hazelcast.instance.BuildInfoProvider;
import com.hazelcast.instance.GroupProperties;
import com.hazelcast.instance.HazelcastInstanceImpl;
import com.hazelcast.instance.Node;
import com.hazelcast.internal.management.dto.SlowOperationDTO;
import com.hazelcast.internal.metrics.MetricsRegistry;
import com.hazelcast.internal.metrics.renderers.CommaSeparatedKeyValueProbeRenderer;
import com.hazelcast.internal.metrics.renderers.HumanFriendlyProbeRenderer;
import com.hazelcast.internal.metrics.renderers.ProbeRenderer;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Address;
import com.hazelcast.spi.impl.operationservice.InternalOperationService;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import static com.hazelcast.instance.GroupProperty.PERFORMANCE_MONITOR_MAX_ROLLED_FILE_COUNT;
import static com.hazelcast.instance.GroupProperty.PERFORMANCE_MONITOR_MAX_ROLLED_FILE_SIZE_MB;
import static com.hazelcast.nio.IOUtil.closeResource;
import static java.lang.Math.round;
import static java.lang.String.format;
import static java.lang.System.currentTimeMillis;
import static java.lang.System.getProperties;
import static java.lang.System.getProperty;
import static java.util.Collections.sort;
/**
* Represents the PerformanceLogFile.
*
* Should only be called from the {@link PerformanceMonitor}.
*/
final class PerformanceLogFile {
private static final int ONE_MB = 1024 * 1024;
volatile File logFile;
private final MetricsRegistry metricRegistry;
private final HazelcastInstanceImpl hazelcastInstance;
private final InternalOperationService operationService;
private final ILogger logger;
private final HeadRenderer headRenderer = new HeadRenderer();
private final BodyRenderer bodyRenderer;
private final String pathname;
private long lastModified;
private int index;
private BufferedWriter writer;
private boolean renderHead;
private int maxRollingFileCount;
private int maxRollingFileSizeBytes;
PerformanceLogFile(PerformanceMonitor performanceMonitor) {
this.bodyRenderer = new BodyRenderer(performanceMonitor.humanFriendlyFormat);
this.logger = performanceMonitor.logger;
this.hazelcastInstance = performanceMonitor.hazelcastInstance;
Node node = hazelcastInstance.node;
this.metricRegistry = node.nodeEngine.getMetricsRegistry();
this.operationService = node.nodeEngine.getOperationService();
this.pathname = getPathName();
GroupProperties props = node.getGroupProperties();
this.maxRollingFileCount = props.getInteger(PERFORMANCE_MONITOR_MAX_ROLLED_FILE_COUNT);
// we accept a float so it becomes easier to testing to create a small file.
this.maxRollingFileSizeBytes = round(ONE_MB * props.getFloat(PERFORMANCE_MONITOR_MAX_ROLLED_FILE_SIZE_MB));
if (logger.isFinestEnabled()) {
logger.finest("max rolling file size: " + maxRollingFileSizeBytes);
logger.finest("max rolling file count: " + maxRollingFileCount);
}
}
private String getPathName() {
Member localMember = hazelcastInstance.getCluster().getLocalMember();
Address address = localMember.getAddress();
String addressString = address.getHost().replace(":", "_") + "#" + address.getPort();
return "performance-" + addressString + "-" + currentTimeMillis() + "-%03d.log";
}
void render() {
try {
if (logFile == null) {
logFile = new File(format(pathname, index));
renderHead = true;
}
if (writer == null) {
FileOutputStream fos = new FileOutputStream(logFile, true);
CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
writer = new BufferedWriter(new OutputStreamWriter(fos, encoder));
}
if (renderHead) {
headRenderer.render(writer);
renderHead = false;
}
bodyRenderer.render(writer);
writer.flush();
lastModified = logFile.lastModified();
if (logFile.length() > maxRollingFileSizeBytes) {
rollover();
}
} catch (IOException e) {
log(e);
logFile = null;
closeResource(writer);
writer = null;
} catch (RuntimeException e) {
logger.severe("Failed to write PerformanceLogFile: " + logFile, e);
}
}
@SuppressFBWarnings({ "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" })
private void rollover() {
closeResource(writer);
writer = null;
logFile = null;
index++;
File file = new File(format(pathname, index - maxRollingFileCount));
// we don't care if the file was deleted or not.
file.delete();
}
private void log(IOException e) {
if (logger.isFinestEnabled()) {
logger.finest("PerformanceMonitor failed to output to file:"
+ logFile.getAbsolutePath() + " cause:" + e.getMessage(), e);
} else {
logger.warning("PerformanceMonitor failed to output to file:"
+ logFile.getAbsolutePath() + " cause:" + e.getMessage());
}
}
/**
* Checks if log file is forced to be rendered.
*/
boolean isRenderingForced() {
if (logFile == null) {
return false;
}
return logFile.lastModified() != lastModified;
}
private final class BodyRenderer {
private final SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
private final ProbeRenderer probeRenderer;
public BodyRenderer(boolean humanFriendlyFormat) {
probeRenderer = humanFriendlyFormat ? new HumanFriendlyProbeRenderer() : new CommaSeparatedKeyValueProbeRenderer();
}
private void render(BufferedWriter writer) throws IOException {
renderHeader(writer);
renderMetricsRegistry(writer);
writer.append("\n\n");
if (renderSlowOperation(writer)) {
writer.append("\n\n");
}
}
private void renderHeader(BufferedWriter writer) throws IOException {
writer.append("================[ Metrics ]============================================\n");
Calendar calendar = Calendar.getInstance();
writer.append(sdf.format(calendar.getTime())).append('\n');
writer.append("------------------------------------------------------------------------\n");
}
private void renderMetricsRegistry(BufferedWriter writer) throws IOException {
metricRegistry.render(probeRenderer);
writer.append(probeRenderer.getResult());
}
private boolean renderSlowOperation(BufferedWriter writer) throws IOException {
List slowOperations = operationService.getSlowOperationDTOs();
if (slowOperations.isEmpty()) {
return false;
}
writer.append("================[ Slow Operations ]=====================================\n");
int k = 1;
for (SlowOperationDTO slowOperation : slowOperations) {
writer.append("#" + k)
.append("\n " + slowOperation.operation)
.append("\n Invocations: " + slowOperation.totalInvocations)
.append("\n Stacktrace:\n");
writer.append(slowOperation.stackTrace).append("\n\n");
k++;
}
return true;
}
}
// Responsible for rendering the head of the file containing the system/config properties.
private final class HeadRenderer {
private void render(BufferedWriter writer) throws IOException {
renderBuildInfo(writer);
writer.append("\n\n");
renderSystemProperties(writer);
writer.append("\n\n");
renderConfigProperties(writer);
writer.append("\n\n");
writer.flush();
}
private void renderSystemProperties(BufferedWriter writer) throws IOException {
writer.append("================[ System Properties ]===================================\n");
writer.append(format("%-30s %4s%n", "property", "value"));
writer.append("------------------------------------------------------------------------\n");
List keys = new LinkedList();
keys.addAll(getProperties().keySet());
sort(keys);
for (Object key : keys) {
String keyString = (String) key;
if (ignore(keyString)) {
continue;
}
Object value = getProperty(keyString);
String s = formatKeyValue(keyString, value);
writer.append(s);
}
writer.append(formatKeyValue("jvm.args", getInputArgs()));
}
private String getInputArgs() {
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
List arguments = runtimeMxBean.getInputArguments();
StringBuffer sb = new StringBuffer();
sb.append("jvm.args=");
for (String argument : arguments) {
sb.append(argument);
sb.append(" ");
}
return sb.toString();
}
private void renderConfigProperties(BufferedWriter writer) throws IOException {
writer.append("=================[ Hazelcast Config ]===================================\n");
writer.append(format("%-60s %4s%n", "property", "value"));
writer.append("------------------------------------------------------------------------\n");
List keys = new LinkedList();
Properties properties = hazelcastInstance.getConfig().getProperties();
keys.addAll(properties.keySet());
sort(keys);
for (Object key : keys) {
String keyString = (String) key;
String value = properties.getProperty(keyString);
writer.append(format("%-60s %4s%n", keyString, value));
}
}
private void renderBuildInfo(BufferedWriter writer) throws IOException {
writer.append("====================[ Build Info ]======================================\n");
writer.append(format("%-30s %4s%n", "property", "value"));
writer.append("------------------------------------------------------------------------\n");
BuildInfo buildInfo = BuildInfoProvider.getBuildInfo();
writer.append(formatKeyValue("Version", buildInfo.getVersion()));
writer.append(formatKeyValue("Build", buildInfo.getBuild()));
writer.append(formatKeyValue("BuildNumber", buildInfo.getBuildNumber()));
writer.append(formatKeyValue("Revision", buildInfo.getVersion()));
writer.append(formatKeyValue("Enterprise", buildInfo.isEnterprise()));
}
private String formatKeyValue(String keyString, Object value) {
return format("%-30s %4s%n", keyString, value);
}
private boolean ignore(String systemProperty) {
if (systemProperty.startsWith("java.awt")) {
return true;
}
if (systemProperty.startsWith("java")
|| systemProperty.startsWith("hazelcast")
|| systemProperty.startsWith("sun")
|| systemProperty.startsWith("os")) {
return false;
}
return true;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy