/* * 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.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.google.common.base.Preconditions; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.routing.PurgePolicy; import org.apache.logging.log4j.core.appender.routing.RoutingAppender; 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.PluginFactory; import org.apache.logging.log4j.status.StatusLogger; /** * A purge policy for the {@link RoutingAppender} which awaits a notification from the application * about a key no longer being required, before it purges it. */ @Plugin(name = "LlapRoutingAppenderPurgePolicy", category = "Core", printObject = true) public class LlapRoutingAppenderPurgePolicy implements PurgePolicy { private static final Logger LOGGER = StatusLogger.getLogger(); private static final ConcurrentMap<String, LlapRoutingAppenderPurgePolicy> INSTANCES = new ConcurrentHashMap<>(); private final Set<String> knownAppenders = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); private final String name; // The Routing appender, which manages underlying appenders private RoutingAppender routingAppender; public LlapRoutingAppenderPurgePolicy(String name) { LOGGER.trace("Created " + LlapRoutingAppenderPurgePolicy.class.getName() + " with name=" + name); this.name=name; } private LlapRoutingAppenderPurgePolicy() { this("_NOOP_"); } @Override public void initialize(RoutingAppender routingAppender) { this.routingAppender = routingAppender; } @Override public void purge() { // Nothing to do here. This is not invoked by the log4j framework. Should likely not be in // the log4j interface } @Override public void update(String key, LogEvent event) { Marker marker = event.getMarker(); if (marker != null && marker.getName() != null && marker.getName().equals(Log4jQueryCompleteMarker.EOF_MARKER)) { LOGGER.debug("Received " + Log4jQueryCompleteMarker.EOF_MARKER + " for key. Attempting cleanup."); keyComplete(key); } else { if (knownAppenders.add(key)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered key: [" + key + "] on purgePolicyWithName=" + name + ", thisAddress=" + System.identityHashCode(this)); } } } } /** * Indicate that the specified key is no longer used. * @param key */ private void keyComplete(String key) { Preconditions.checkNotNull(key, "Key must be specified"); boolean removed = knownAppenders.remove(key); if (removed) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Deleting Appender for key: " + key); } routingAppender.deleteAppender(key); } else { LOGGER.trace("Ignoring call to remove unknown key: " + key); } } @PluginFactory public static PurgePolicy createPurgePolicy( @PluginAttribute("name") final String name) { // Name required for routing. Error out if it is not set. Preconditions.checkNotNull(name, "Name must be specified for " + LlapRoutingAppenderPurgePolicy.class.getName()); LlapRoutingAppenderPurgePolicy llapRoutingAppenderPurgePolicy = new LlapRoutingAppenderPurgePolicy(name); LlapRoutingAppenderPurgePolicy old = INSTANCES.putIfAbsent(name, llapRoutingAppenderPurgePolicy); if (old != null) { LOGGER.debug("Attempt to create multiple instances of " + LlapRoutingAppenderPurgePolicy.class.getName() + " with the name " + name + ". Using original instance"); llapRoutingAppenderPurgePolicy = old; } return llapRoutingAppenderPurgePolicy; } }