org.apache.hadoop.hive.llap.log.LlapWrappedAppender Maven / Gradle / Ivy
/*
* 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.hadoop.hive.llap.log;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;
import com.google.common.base.Preconditions;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.RandomAccessFileAppender;
import org.apache.logging.log4j.core.config.AppenderControl;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.config.plugins.PluginNode;
import org.apache.logging.log4j.util.Strings;
/**
* An appender to wrap around RandomAccessFileAppender, and rename the file once the appender is
* closed.
* Could be potentially extended to other appenders by special casing them to figure out how files
* are to be handled.
*/
@Plugin(name = "LlapWrappedAppender", category = "Core",
elementType = "appender", printObject = true, deferChildren = true)
public class LlapWrappedAppender extends AbstractAppender {
private static final boolean DEFAULT_RENAME_FILES_ON_CLOSE = true;
private static final String DEFAULT_RENAMED_FILE_SUFFIX = ".done";
private final Node node;
private final Configuration config;
private final boolean renameFileOnClose;
private final String renamedFileSuffix;
private AtomicReference realAppender = new AtomicReference<>();
private AtomicReference appenderControl = new AtomicReference<>();
public LlapWrappedAppender(final String name, final Node node, final Configuration config,
boolean renameOnClose, String renamedFileSuffix) {
super(name, null, null);
this.node = node;
this.config = config;
this.renameFileOnClose = renameOnClose;
this.renamedFileSuffix = renamedFileSuffix;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
LlapWrappedAppender.class.getName() + " created with name=" + name + ", renameOnClose=" +
renameOnClose + ", renamedFileSuffix=" + renamedFileSuffix);
}
}
@Override
public void start() {
super.start();
}
@Override
public void append(LogEvent event) {
setupAppenderIfRequired(event);
if (appenderControl.get() != null) {
if (!(event.getMarker() != null && event.getMarker().getName() != null &&
event.getMarker().getName().equals(Log4jQueryCompleteMarker.EOF_MARKER))) {
appenderControl.get().callAppender(event);
} else {
LOGGER.debug("Not forwarding message with maker={}, marker.getName={}", event.getMarker(),
(event.getMarker() == null ? "nullMarker" : event.getMarker().getName()));
}
}
}
private void setupAppenderIfRequired(LogEvent event) {
if (appenderControl.get() == null) {
if (node.getType().getElementName().equalsIgnoreCase("appender")) {
for (final Node cnode : node.getChildren()) {
final Node appNode = new Node(cnode);
config.createConfiguration(appNode, event);
if (appNode.getObject() instanceof Appender) {
final Appender app = appNode.getObject();
app.start();
if (!(app instanceof RandomAccessFileAppender)) {
String message =
"Cannot handle appenders other than " + RandomAccessFileAppender.class.getName() +
". Found: " + app.getClass().getName();
LOGGER.error(message);
throw new IllegalStateException(message);
}
realAppender.set(app);
appenderControl.set(new AppenderControl(app, null, null));
if (LOGGER.isDebugEnabled()) {
RandomAccessFileAppender raf = (RandomAccessFileAppender) app;
LOGGER.debug(
"Setup new appender to write to file: " + raf.getFileName() + ", appenderName=" +
raf.getName() + ", appenderManagerName=" + raf.getManager().getName());
}
break;
}
}
if (appenderControl.get() == null) {
// Fail if mis-configured.
throw new RuntimeException(LlapWrappedAppender.class.getSimpleName() +
"name=" + getName() + " unable to setup actual appender." +
"Could not find child appender");
}
} else {
// Fail if mis-configured.
throw new RuntimeException(LlapWrappedAppender.class.getSimpleName() +
"name=" + getName() + " unable to setup actual appender." +
"Could not find child appender");
}
}
}
@Override
public void stop() {
if (!(this.isStopping() || this.isStopped())) {
super.stop();
if (appenderControl.get() != null) {
appenderControl.get().stop();
realAppender.get().stop();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"Stop invoked for " + ((RandomAccessFileAppender) realAppender.get()).getFileName());
}
if (realAppender.get() == null) {
LOGGER.info("RealAppender is null. Ignoring stop");
return;
}
RandomAccessFileAppender raf = (RandomAccessFileAppender) realAppender.get();
Path renamedPath = null;
if (renameFileOnClose) {
try {
// Look for a file to which we can move the existing file. With external services,
// it's possible for the service to be marked complete after each fragment.
int counter = 0;
while(true) {
renamedPath = getRenamedPath(raf.getFileName(), counter);
if (!Files.exists(renamedPath)) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Renaming file: " + raf.getFileName() + " to " + renamedPath);
}
Files.move(Paths.get(raf.getFileName()), renamedPath);
break;
}
counter++;
}
} catch (IOException e) {
// Bail on an exception - out of the loop.
LOGGER.warn("Failed to rename file: " + raf.getFileName() + " to " + renamedPath, e);
}
}
}
}
private Path getRenamedPath(String originalFileName, int iteration) {
Path renamedPath;
if (iteration == 0) {
renamedPath = Paths.get(originalFileName + renamedFileSuffix);
} else {
renamedPath = Paths.get(originalFileName + "." + iteration + renamedFileSuffix);
}
return renamedPath;
}
@PluginFactory
public static LlapWrappedAppender createAppender(
@PluginAttribute("name") final String name, // This isn't really used for anything.
@PluginAttribute("renameFileOnClose") final String renameFileOnCloseProvided,
@PluginAttribute("renamedFileSuffix") final String renamedFileSuffixProvided,
@PluginNode final Node node,
@PluginConfiguration final Configuration config
) {
if (config == null) {
LOGGER.error("PluginConfiguration not expected to be null");
return null;
}
if (node == null) {
LOGGER.error("Node must be specified as an appender specification");
return null;
}
boolean renameFileOnClose = DEFAULT_RENAME_FILES_ON_CLOSE;
if (Strings.isNotBlank(renameFileOnCloseProvided)) {
renameFileOnClose = Boolean.parseBoolean(renameFileOnCloseProvided);
}
String renamedFileSuffix = DEFAULT_RENAMED_FILE_SUFFIX;
if (Strings.isNotBlank(renamedFileSuffixProvided)) {
renamedFileSuffix = renamedFileSuffixProvided;
}
return new LlapWrappedAppender(name, node, config, renameFileOnClose, renamedFileSuffix);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy