blob: 95bf7c2c96c91d3319071ac2ef22545b040c0ba7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.accumulo.server.watcher;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.apache.accumulo.core.Constants;
import org.apache.accumulo.core.util.HostAndPort;
import org.apache.accumulo.core.zookeeper.ZooUtil;
import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
import org.apache.log4j.Appender;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.FileWatchdog;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
/**
* Watcher that updates the monitor's log4j port from ZooKeeper in a system property
*/
public class MonitorLog4jWatcher extends FileWatchdog implements Watcher {
private static final Logger log = Logger.getLogger(MonitorLog4jWatcher.class);
private static final String HOST_PROPERTY_NAME = "org.apache.accumulo.core.host.log";
private static final String PORT_PROPERTY_NAME = "org.apache.accumulo.core.host.log.port";
private final Object lock;
private final Log4jConfiguration logConfig;
private boolean loggingDisabled = false;
protected String path;
public MonitorLog4jWatcher(String instance, String filename) {
super(filename);
this.path = ZooUtil.getRoot(instance) + Constants.ZMONITOR_LOG4J_ADDR;
this.lock = new Object();
this.logConfig = new Log4jConfiguration(filename);
doOnChange();
}
boolean isUsingProperties() {
return logConfig.isUsingProperties();
}
String getPath() {
return path;
}
@Override
public void run() {
try {
// Initially set the logger if the Monitor's log4j advertisement node exists
if (ZooReaderWriter.getInstance().exists(path, this))
updateMonitorLog4jLocation();
log.info("Set watch for Monitor Log4j watcher");
} catch (Exception e) {
log.error("Unable to set watch for Monitor Log4j watcher on " + path);
}
super.run();
}
@Override
public void doOnChange() {
// this method gets called in the parent class' constructor
// I'm not sure of a better way to get around this. The final modifier helps though.
if (null == lock) {
return;
}
synchronized (lock) {
// We might triggered by file-reloading or from ZK update.
// Either way will result in log-forwarding being restarted
loggingDisabled = false;
log.info("Enabled log-forwarding");
logConfig.resetLogger();
}
}
@Override
public void process(WatchedEvent event) {
// We got an update, process the data in the node
updateMonitorLog4jLocation();
if (event.getPath() != null) {
try {
ZooReaderWriter.getInstance().exists(event.getPath(), this);
} catch (Exception ex) {
log.error("Unable to reset watch for Monitor Log4j watcher", ex);
}
}
}
/**
* Read the host and port information for the Monitor's log4j socket and update the system
* properties so that, on logger refresh, it sees the new information.
*/
protected void updateMonitorLog4jLocation() {
try {
String hostPortString = new String(ZooReaderWriter.getInstance().getData(path, null), UTF_8);
HostAndPort hostAndPort = HostAndPort.fromString(hostPortString);
System.setProperty(HOST_PROPERTY_NAME, hostAndPort.getHost());
System.setProperty(PORT_PROPERTY_NAME, Integer.toString(hostAndPort.getPort()));
log.info("Changing monitor log4j address to " + hostAndPort.toString());
doOnChange();
} catch (NoNodeException e) {
// Not sure on the synchronization guarantees for Loggers and Appenders
// on configuration reload
synchronized (lock) {
// Don't need to try to re-disable'ing it.
if (loggingDisabled) {
return;
}
Logger logger = LogManager.getLogger("org.apache.accumulo");
if (null != logger) {
// TODO ACCUMULO-2343 Create a specific appender for log-forwarding to the monitor
// that can replace the AsyncAppender+SocketAppender.
Appender appender = logger.getAppender("ASYNC");
if (null != appender) {
log.info("Closing log-forwarding appender");
appender.close();
log.info("Removing log-forwarding appender");
logger.removeAppender(appender);
loggingDisabled = true;
}
}
}
} catch (IllegalArgumentException e) {
log.error("Could not parse host and port information", e);
} catch (Exception e) {
log.error("Error reading zookeeper data for Monitor Log4j watcher", e);
}
}
}