blob: fb21f80ccf311f7ea8de8cda5e47aedbc242206e [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.logging.log4j.jms.appender;
import java.io.Serializable;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.jms.JMSException;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AbstractManager;
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.PluginAliases;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
import org.apache.logging.log4j.core.net.JndiManager;
import org.apache.logging.log4j.jms.appender.JmsManager.JmsManagerConfiguration;
/**
* Generic JMS Appender plugin for both queues and topics. This Appender replaces the previous split ones. However,
* configurations set up for the 2.0 version of the JMS appenders will still work.
*/
@Plugin(name = "JMS", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
@PluginAliases({ "JMSQueue", "JMSTopic" })
public class JmsAppender extends AbstractAppender {
public static class Builder implements org.apache.logging.log4j.core.util.Builder<JmsAppender> {
public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000;
@PluginBuilderAttribute
@Required(message = "A name for the JmsAppender must be specified")
private String name;
@PluginBuilderAttribute
private String factoryName;
@PluginBuilderAttribute
private String providerUrl;
@PluginBuilderAttribute
private String urlPkgPrefixes;
@PluginBuilderAttribute
private String securityPrincipalName;
@PluginBuilderAttribute(sensitive = true)
private String securityCredentials;
@PluginBuilderAttribute
@Required(message = "A javax.jms.ConnectionFactory JNDI name must be specified")
private String factoryBindingName;
@PluginBuilderAttribute
@PluginAliases({ "queueBindingName", "topicBindingName" })
@Required(message = "A javax.jms.Destination JNDI name must be specified")
private String destinationBindingName;
@PluginBuilderAttribute
private String userName;
@PluginBuilderAttribute(sensitive = true)
private char[] password;
@PluginElement("Layout")
private Layout<? extends Serializable> layout;
@PluginElement("Filter")
private Filter filter;
private long reconnectIntervalMillis = DEFAULT_RECONNECT_INTERVAL_MILLIS;
@PluginBuilderAttribute
private boolean ignoreExceptions = true;
@PluginBuilderAttribute
private boolean immediateFail;
// Programmatic access only for now.
private JmsManager jmsManager;
private Builder() {
}
@SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender
@Override
public JmsAppender build() {
JmsManager actualJmsManager = jmsManager;
JmsManagerConfiguration configuration = null;
if (actualJmsManager == null) {
final Properties jndiProperties = JndiManager.createProperties(factoryName, providerUrl, urlPkgPrefixes,
securityPrincipalName, securityCredentials, null);
configuration = new JmsManagerConfiguration(jndiProperties, factoryBindingName, destinationBindingName,
userName, password, false, reconnectIntervalMillis);
actualJmsManager = AbstractManager.getManager(name, JmsManager.FACTORY, configuration);
}
if (actualJmsManager == null) {
// JmsManagerFactory has already logged an ERROR.
return null;
}
if (layout == null) {
LOGGER.error("No layout provided for JmsAppender");
return null;
}
try {
return new JmsAppender(name, filter, layout, ignoreExceptions, actualJmsManager);
} catch (final JMSException e) {
// Never happens since the ctor no longer actually throws a JMSException.
throw new IllegalStateException(e);
}
}
public Builder setDestinationBindingName(final String destinationBindingName) {
this.destinationBindingName = destinationBindingName;
return this;
}
public Builder setFactoryBindingName(final String factoryBindingName) {
this.factoryBindingName = factoryBindingName;
return this;
}
public Builder setFactoryName(final String factoryName) {
this.factoryName = factoryName;
return this;
}
public Builder setFilter(final Filter filter) {
this.filter = filter;
return this;
}
public Builder setIgnoreExceptions(final boolean ignoreExceptions) {
this.ignoreExceptions = ignoreExceptions;
return this;
}
public Builder setImmediateFail(final boolean immediateFail) {
this.immediateFail = immediateFail;
return this;
}
public Builder setJmsManager(final JmsManager jmsManager) {
this.jmsManager = jmsManager;
return this;
}
public Builder setLayout(final Layout<? extends Serializable> layout) {
this.layout = layout;
return this;
}
public Builder setName(final String name) {
this.name = name;
return this;
}
public Builder setPassword(final char[] password) {
this.password = password;
return this;
}
/**
* Sets the Password.
* @param password The new password.
* @deprecated Use setPassword(char[])
* @return the Builder.
*/
@Deprecated
public Builder setPassword(final String password) {
this.password = password == null ? null : password.toCharArray();
return this;
}
public Builder setProviderUrl(final String providerUrl) {
this.providerUrl = providerUrl;
return this;
}
public Builder setReconnectIntervalMillis(final long reconnectIntervalMillis) {
this.reconnectIntervalMillis = reconnectIntervalMillis;
return this;
}
public Builder setSecurityCredentials(final String securityCredentials) {
this.securityCredentials = securityCredentials;
return this;
}
public Builder setSecurityPrincipalName(final String securityPrincipalName) {
this.securityPrincipalName = securityPrincipalName;
return this;
}
public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) {
this.urlPkgPrefixes = urlPkgPrefixes;
return this;
}
/**
* Sets the user name.
* @param username The user's name.
* @deprecated Use {@link #setUserName(String)}.
* @return the Builder.
*/
@Deprecated
public Builder setUsername(final String username) {
this.userName = username;
return this;
}
public Builder setUserName(final String userName) {
this.userName = userName;
return this;
}
/**
* Does not include the password.
*/
@Override
public String toString() {
return "Builder [name=" + name + ", factoryName=" + factoryName + ", providerUrl=" + providerUrl
+ ", urlPkgPrefixes=" + urlPkgPrefixes + ", securityPrincipalName=" + securityPrincipalName
+ ", securityCredentials=" + securityCredentials + ", factoryBindingName=" + factoryBindingName
+ ", destinationBindingName=" + destinationBindingName + ", username=" + userName + ", layout="
+ layout + ", filter=" + filter + ", ignoreExceptions=" + ignoreExceptions + ", jmsManager="
+ jmsManager + "]";
}
}
@PluginBuilderFactory
public static Builder newBuilder() {
return new Builder();
}
private volatile JmsManager manager;
/**
* @param name The Appender's name.
* @param filter The filter to attach to the Appender, if any.
* @param layout The layout to use to render the event.
* @param ignoreExceptions true if exceptions should be ignore, false otherwise.
* @param manager The JMSManager.
* @throws JMSException
* not thrown as of 2.9 but retained in the signature for compatibility, will be removed in 3.0.
*/
protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
final boolean ignoreExceptions, final JmsManager manager) throws JMSException {
super(name, filter, layout, ignoreExceptions);
this.manager = manager;
}
@Override
public void append(final LogEvent event) {
this.manager.send(event, toSerializable(event));
}
public JmsManager getManager() {
return manager;
}
@Override
public boolean stop(final long timeout, final TimeUnit timeUnit) {
setStopping();
boolean stopped = super.stop(timeout, timeUnit, false);
stopped &= this.manager.stop(timeout, timeUnit);
setStopped();
return stopped;
}
}