All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.internal.diagnostics.SystemLogPlugin Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * 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.diagnostics;

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.LifecycleEvent;
import com.hazelcast.core.LifecycleListener;
import com.hazelcast.core.Member;
import com.hazelcast.core.MembershipAdapter;
import com.hazelcast.core.MembershipEvent;
import com.hazelcast.core.MigrationEvent;
import com.hazelcast.core.MigrationListener;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.Connection;
import com.hazelcast.nio.ConnectionListenable;
import com.hazelcast.nio.ConnectionListener;
import com.hazelcast.spi.impl.NodeEngineImpl;
import com.hazelcast.spi.properties.HazelcastProperties;
import com.hazelcast.spi.properties.HazelcastProperty;

import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * The {@link SystemLogPlugin} is responsible for:
 * 
    *
  1. Showing lifecycle changes like shutting down, merging etc.
  2. *
  3. Show connection creation and connection closing. Also the causes of closes are included.
  4. *
  5. Showing membership changes.
  6. *
  7. Optionally showing partition migration
  8. *
* * This plugin is very useful to get an idea what is happening inside a cluster; especially when there are connection related * problems. * * This cluster has a low overhead and is meant to run in production. */ public class SystemLogPlugin extends DiagnosticsPlugin { /** * If this plugin is enabled. */ public static final HazelcastProperty ENABLED = new HazelcastProperty(Diagnostics.PREFIX + ".systemlog.enabled", "true"); /** * If logging partition migration is enabled. Because there can be so many partitions, logging the partition migration * can be very noisy. */ public static final HazelcastProperty LOG_PARTITIONS = new HazelcastProperty(Diagnostics.PREFIX + ".systemlog.partitions", "false"); /** * Currently the Diagnostic is scheduler based, so each task gets to run as often at is has been configured. This works * fine for most plugins, but if there are outside events triggering the need to write, this scheduler based model is * not the right fit. In the future the Diagnostics need to be improved. */ private static final long PERIOD_MILLIS = SECONDS.toMillis(1); private final Queue logQueue = new ConcurrentLinkedQueue(); private final ConnectionListenable connectionObservable; private final HazelcastInstance hazelcastInstance; private final Address thisAddress; private final boolean logPartitions; private final boolean enabled; public SystemLogPlugin(NodeEngineImpl nodeEngine) { this(nodeEngine.getProperties(), nodeEngine.getNode().connectionManager, nodeEngine.getHazelcastInstance(), nodeEngine.getLogger(SystemLogPlugin.class)); } public SystemLogPlugin(HazelcastProperties properties, ConnectionListenable connectionObservable, HazelcastInstance hazelcastInstance, ILogger logger) { super(logger); this.connectionObservable = connectionObservable; this.hazelcastInstance = hazelcastInstance; this.thisAddress = getThisAddress(hazelcastInstance); this.logPartitions = properties.getBoolean(LOG_PARTITIONS); this.enabled = properties.getBoolean(ENABLED); } private Address getThisAddress(HazelcastInstance hazelcastInstance) { try { return hazelcastInstance.getCluster().getLocalMember().getAddress(); } catch (UnsupportedOperationException e) { return null; } } @Override public long getPeriodMillis() { if (!enabled) { return DiagnosticsPlugin.DISABLED; } return PERIOD_MILLIS; } @Override public void onStart() { logger.info("Plugin:active: logPartitions:" + logPartitions); connectionObservable.addConnectionListener(new ConnectionListenerImpl()); hazelcastInstance.getCluster().addMembershipListener(new MembershipListenerImpl()); if (logPartitions) { hazelcastInstance.getPartitionService().addMigrationListener(new MigrationListenerImpl()); } hazelcastInstance.getLifecycleService().addLifecycleListener(new LifecycleListenerImpl()); } @Override public void run(DiagnosticsLogWriter writer) { for (; ; ) { Object item = logQueue.poll(); if (item == null) { return; } if (item instanceof LifecycleEvent) { render(writer, (LifecycleEvent) item); } else if (item instanceof MembershipEvent) { render(writer, (MembershipEvent) item); } else if (item instanceof MigrationEvent) { render(writer, (MigrationEvent) item); } else if (item instanceof ConnectionEvent) { ConnectionEvent event = (ConnectionEvent) item; render(writer, event); } } } private void render(DiagnosticsLogWriter writer, LifecycleEvent event) { writer.startSection("Lifecycle"); writer.writeEntry(event.getState().name()); writer.endSection(); } private void render(DiagnosticsLogWriter writer, MembershipEvent event) { switch (event.getEventType()) { case MembershipEvent.MEMBER_ADDED: writer.startSection("MemberAdded"); break; case MembershipEvent.MEMBER_REMOVED: writer.startSection("MemberRemoved"); break; default: return; } writer.writeKeyValueEntry("member", event.getMember().getAddress().toString()); // writing the members writer.startSection("Members"); Set members = event.getMembers(); if (members != null) { boolean first = true; for (Member member : members) { if (member.getAddress().equals(thisAddress)) { if (first) { writer.writeEntry(member.getAddress().toString() + ":this:master"); } else { writer.writeEntry(member.getAddress().toString() + ":this"); } } else if (first) { writer.writeEntry(member.getAddress().toString() + ":master"); } else { writer.writeEntry(member.getAddress().toString()); } first = false; } } writer.endSection(); // ending the outer section writer.endSection(); } private void render(DiagnosticsLogWriter writer, MigrationEvent event) { switch (event.getStatus()) { case STARTED: writer.startSection("MigrationStarted"); break; case COMPLETED: writer.startSection("MigrationCompleted"); break; case FAILED: writer.startSection("MigrationFailed"); break; default: return; } Member oldOwner = event.getOldOwner(); writer.writeKeyValueEntry("oldOwner", oldOwner == null ? "null" : oldOwner.getAddress().toString()); writer.writeKeyValueEntry("newOwner", event.getNewOwner().getAddress().toString()); writer.writeKeyValueEntry("partitionId", event.getPartitionId()); writer.endSection(); } @SuppressWarnings("ThrowableResultOfMethodCallIgnored") private void render(DiagnosticsLogWriter writer, ConnectionEvent event) { if (event.added) { writer.startSection("ConnectionAdded"); } else { writer.startSection("ConnectionRemoved"); } Connection connection = event.connection; writer.writeEntry(connection.toString()); writer.writeKeyValueEntry("type", connection.getType().name()); writer.writeKeyValueEntry("isAlive", connection.isAlive()); if (!event.added) { String closeReason = connection.getCloseReason(); Throwable closeCause = connection.getCloseCause(); if (closeReason == null && closeCause != null) { closeReason = closeCause.getMessage(); } writer.writeKeyValueEntry("closeReason", closeReason == null ? "Unknown" : closeReason); if (closeCause != null) { writer.startSection("CloseCause"); String s = closeCause.getClass().getName(); String message = closeCause.getMessage(); writer.writeEntry((message != null) ? (s + ": " + message) : s); for (StackTraceElement element : closeCause.getStackTrace()) { writer.writeEntry(element.toString()); } writer.endSection(); } } writer.endSection(); } private class LifecycleListenerImpl implements LifecycleListener { @Override public void stateChanged(LifecycleEvent event) { logQueue.add(event); } } private static final class ConnectionEvent { final boolean added; final Connection connection; private ConnectionEvent(boolean added, Connection connection) { this.added = added; this.connection = connection; } } private class ConnectionListenerImpl implements ConnectionListener { @Override public void connectionAdded(Connection connection) { logQueue.add(new ConnectionEvent(true, connection)); } @Override public void connectionRemoved(Connection connection) { logQueue.add(new ConnectionEvent(false, connection)); } } private class MembershipListenerImpl extends MembershipAdapter { @Override public void memberAdded(MembershipEvent event) { logQueue.add(event); } @Override public void memberRemoved(MembershipEvent event) { logQueue.add(event); } } private class MigrationListenerImpl implements MigrationListener { @Override public void migrationStarted(MigrationEvent event) { logQueue.add(event); } @Override public void migrationCompleted(MigrationEvent event) { logQueue.add(event); } @Override public void migrationFailed(MigrationEvent event) { logQueue.add(event); } } }