Merge branch 'master' into LOG4J2-1851 # Conflicts: # src/changes/changes.xml
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/394c30f5 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/394c30f5 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/394c30f5 Branch: refs/heads/java9NoMultiRelease Commit: 394c30f594c9eac570a43313bf44be4bc60cded9 Parents: 6fd6dfc 1475bd9 Author: Mikael Ståldal <[email protected]> Authored: Mon Apr 10 10:59:27 2017 +0200 Committer: Mikael Ståldal <[email protected]> Committed: Mon Apr 10 10:59:27 2017 +0200 ---------------------------------------------------------------------- BUILDING.md | 2 +- README.md | 2 + RELEASE-NOTES.md | 74 ++++++++++--------- log4j-1.2-api/pom.xml | 2 +- log4j-api-scala_2.10/pom.xml | 2 +- log4j-api-scala_2.11/pom.xml | 2 +- log4j-api/pom.xml | 2 +- .../org/apache/logging/log4j/ThreadContext.java | 5 +- log4j-bom/pom.xml | 2 +- log4j-core-its/pom.xml | 2 +- log4j-core/pom.xml | 2 +- .../apache/logging/log4j/core/config/Order.java | 2 +- .../log4j/core/config/OrderComparator.java | 2 +- .../builder/api/ConfigurationBuilder.java | 15 ++++ .../api/KeyValuePairComponentBuilder.java | 25 +++++++ .../builder/api/PropertyComponentBuilder.java | 25 +++++++ .../impl/DefaultConfigurationBuilder.java | 11 +++ .../DefaultKeyValuePairComponentBuilder.java | 34 +++++++++ .../impl/DefaultPropertyComponentBuilder.java | 32 ++++++++ .../core/util/FilteredObjectInputStream.java | 67 +++++++++++++++++ .../builder/ConfigurationAssemblerTest.java | 6 +- .../builder/ConfigurationBuilderTest.java | 16 ++++ .../builder/CustomConfigurationFactory.java | 11 +++ log4j-distribution/pom.xml | 2 +- log4j-distribution/src/assembly/bin.xml | 4 +- log4j-flume-ng/pom.xml | 2 +- log4j-iostreams/pom.xml | 2 +- log4j-jcl/pom.xml | 2 +- log4j-jmx-gui/pom.xml | 2 +- log4j-jul/pom.xml | 2 +- log4j-liquibase/pom.xml | 2 +- log4j-nosql/pom.xml | 2 +- log4j-osgi/pom.xml | 2 +- log4j-perf/pom.xml | 2 +- log4j-samples/configuration/pom.xml | 2 +- log4j-samples/flume-common/pom.xml | 2 +- log4j-samples/flume-embedded/pom.xml | 2 +- log4j-samples/flume-remote/pom.xml | 2 +- log4j-samples/loggerProperties/pom.xml | 2 +- log4j-samples/pom.xml | 2 +- log4j-samples/scala-api/pom.xml | 2 +- .../log4j/server/AbstractSocketServer.java | 13 ++++ .../server/ObjectInputStreamLogEventBridge.java | 23 +++++- .../logging/log4j/server/TcpSocketServer.java | 25 ++++++- .../logging/log4j/server/UdpSocketServer.java | 18 ++++- log4j-slf4j-impl/pom.xml | 2 +- log4j-taglib/pom.xml | 2 +- log4j-to-slf4j/pom.xml | 2 +- log4j-web/pom.xml | 2 +- pom.xml | 31 +++++--- src/changes/changes.xml | 26 ++++++- src/site/markdown/maven-artifacts.md.vm | 2 +- src/site/resources/images/logo.png | Bin 40517 -> 40494 bytes .../logo/logo-electric-blue-2-2.8.2.png | Bin 0 -> 40494 bytes .../logo/logo-electric-blue-2-2.8.2.xcf | Bin 0 -> 67168 bytes .../resources/logo/logo-electric-blue-2.xcf | Bin 0 -> 66097 bytes 56 files changed, 437 insertions(+), 90 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/394c30f5/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractSocketServer.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractSocketServer.java index a8f1140,0000000..aff7eb1 mode 100644,000000..100644 --- a/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractSocketServer.java +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractSocketServer.java @@@ -1,209 -1,0 +1,222 @@@ +/* + * 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.server; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.URI; +import java.net.URL; ++import java.util.Collections; ++import java.util.List; +import java.util.Objects; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.validators.PositiveInteger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LogEventListener; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.xml.XmlConfiguration; +import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory; +import org.apache.logging.log4j.core.util.BasicCommandLineArguments; +import org.apache.logging.log4j.core.util.InetAddressConverter; +import org.apache.logging.log4j.core.util.Log4jThread; +import org.apache.logging.log4j.util.Strings; + +/** + * Abstract socket server for TCP and UDP implementations. + * + * @param <T> + * The kind of input stream read + * + * TODO Make a LifeCycle + */ +public abstract class AbstractSocketServer<T extends InputStream> extends LogEventListener implements Runnable { + + protected static class CommandLineArguments extends BasicCommandLineArguments { + + @Parameter(names = { "--config", "-c" }, description = "Log4j configuration file location (path or URL).") + private String configLocation; + + @Parameter(names = { "--interactive", + "-i" }, description = "Accepts commands on standard input (\"exit\" is the only command).") + private boolean interactive; + + @Parameter(names = { "--port", + "-p" }, validateWith = PositiveInteger.class, description = "Server socket port.") + private int port; + + @Parameter(names = { "--localbindaddress", + "-a" }, converter = InetAddressConverter.class, description = "Server socket local bind address.") + private InetAddress localBindAddress; + ++ @Parameter(names = {"--classes", "-C"}, description = "Additional classes to allow deserialization") ++ private List<String> allowedClasses; ++ + String getConfigLocation() { + return configLocation; + } + + int getPort() { + return port; + } + + protected boolean isInteractive() { + return interactive; + } + + void setConfigLocation(final String configLocation) { + this.configLocation = configLocation; + } + + void setInteractive(final boolean interactive) { + this.interactive = interactive; + } + + void setPort(final int port) { + this.port = port; + } + + InetAddress getLocalBindAddress() { + return localBindAddress; + } + + void setLocalBindAddress(final InetAddress localBindAddress) { + this.localBindAddress = localBindAddress; + } ++ ++ List<String> getAllowedClasses() { ++ return allowedClasses == null ? Collections.<String>emptyList() : allowedClasses; ++ } ++ ++ void setAllowedClasses(final List<String> allowedClasses) { ++ this.allowedClasses = allowedClasses; ++ } + } + + /** + * Factory that creates a Configuration for the server. + */ + protected static class ServerConfigurationFactory extends XmlConfigurationFactory { + + private final String path; + + public ServerConfigurationFactory(final String path) { + this.path = path; + } + + @Override + public Configuration getConfiguration(final LoggerContext loggerContext, final String name, + final URI configLocation) { + if (Strings.isNotEmpty(path)) { + File file = null; + ConfigurationSource source = null; + try { + file = new File(path); + final FileInputStream is = new FileInputStream(file); + source = new ConfigurationSource(is, file); + } catch (final FileNotFoundException ignored) { + // Ignore this error + } + if (source == null) { + try { + final URL url = new URL(path); + source = new ConfigurationSource(url.openStream(), url); + } catch (final IOException ignored) { + // Ignore this error + } + } + + try { + if (source != null) { + return new XmlConfiguration(loggerContext, source); + } + } catch (final Exception ignored) { + // Ignore this error. + } + System.err.println("Unable to process configuration at " + path + ", using default."); + } + return super.getConfiguration(loggerContext, name, configLocation); + } + } + + protected static final int MAX_PORT = 65534; + + private volatile boolean active = true; + + protected final LogEventBridge<T> logEventInput; + + protected final Logger logger; + + /** + * Creates a new socket server. + * + * @param port + * listen to this port + * @param logEventInput + * Use this input to read log events. + */ + public AbstractSocketServer(final int port, final LogEventBridge<T> logEventInput) { + this.logger = LogManager.getLogger(this.getClass().getName() + '.' + port); + this.logEventInput = Objects.requireNonNull(logEventInput, "LogEventInput"); + } + + protected boolean isActive() { + return this.active; + } + + protected void setActive(final boolean isActive) { + this.active = isActive; + } + + /** + * Start this server in a new thread. + * + * @return the new thread that running this server. + */ + public Thread startNewThread() { + final Thread thread = new Log4jThread(this); + thread.start(); + return thread; + } + + public abstract void shutdown() throws Exception; + + public void awaitTermination(final Thread serverThread) throws Exception { + final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + while (true) { + final String line = reader.readLine(); + if (line == null + || line.equalsIgnoreCase("quit") + || line.equalsIgnoreCase("stop") + || line.equalsIgnoreCase("exit")) { + this.shutdown(); + serverThread.join(); + break; + } + } + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/394c30f5/log4j-server/src/main/java/org/apache/logging/log4j/server/ObjectInputStreamLogEventBridge.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/ObjectInputStreamLogEventBridge.java index 9f22b22,0000000..ddd2e26 mode 100644,000000..100644 --- a/log4j-server/src/main/java/org/apache/logging/log4j/server/ObjectInputStreamLogEventBridge.java +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/ObjectInputStreamLogEventBridge.java @@@ -1,45 -1,0 +1,64 @@@ +/* + * 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.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; ++import java.util.Collections; ++import java.util.List; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LogEventListener; ++import org.apache.logging.log4j.core.util.FilteredObjectInputStream; + +/** + * Reads and logs serialized {@link LogEvent} objects from an {@link ObjectInputStream}. + */ +public class ObjectInputStreamLogEventBridge extends AbstractLogEventBridge<ObjectInputStream> { + ++ private final List<String> allowedClasses; ++ ++ public ObjectInputStreamLogEventBridge() { ++ this(Collections.<String>emptyList()); ++ } ++ ++ /** ++ * Constructs an ObjectInputStreamLogEventBridge with additional allowed classes to deserialize. ++ * ++ * @param allowedClasses class names to also allow for deserialization ++ * @since 2.8.2 ++ */ ++ public ObjectInputStreamLogEventBridge(final List<String> allowedClasses) { ++ this.allowedClasses = allowedClasses; ++ } ++ + @Override + public void logEvents(final ObjectInputStream inputStream, final LogEventListener logEventListener) - throws IOException { ++ throws IOException { + try { + logEventListener.log((LogEvent) inputStream.readObject()); + } catch (final ClassNotFoundException e) { + throw new IOException(e); + } + } + + @Override + public ObjectInputStream wrapStream(final InputStream inputStream) throws IOException { - return new ObjectInputStream(inputStream); ++ return new FilteredObjectInputStream(inputStream, allowedClasses); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/394c30f5/log4j-server/src/main/java/org/apache/logging/log4j/server/TcpSocketServer.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/TcpSocketServer.java index 7856969,0000000..9a5521c mode 100644,000000..100644 --- a/log4j-server/src/main/java/org/apache/logging/log4j/server/TcpSocketServer.java +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/TcpSocketServer.java @@@ -1,314 -1,0 +1,333 @@@ +/* + * 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.server; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.OptionalDataException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; ++import java.util.Collections; ++import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.validators.PositiveInteger; +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.util.BasicCommandLineArguments; +import org.apache.logging.log4j.core.util.Closer; +import org.apache.logging.log4j.core.util.Log4jThread; +import org.apache.logging.log4j.message.EntryMessage; + +/** + * Listens for Log4j events on a TCP server socket and passes them on to Log4j. + * + * @param <T> + * The kind of input stream read + * @see #main(String[]) + */ +public class TcpSocketServer<T extends InputStream> extends AbstractSocketServer<T> { + + protected static class CommandLineArguments extends AbstractSocketServer.CommandLineArguments { + + @Parameter(names = { "--backlog", + "-b" }, validateWith = PositiveInteger.class, description = "Server socket backlog.") + // Same default as ServerSocket + private int backlog = 50; + + int getBacklog() { + return backlog; + } + + void setBacklog(final int backlog) { + this.backlog = backlog; + } + + } + + /** + * Thread that processes the events. + */ + private class SocketHandler extends Log4jThread { + + private final T inputStream; + + private volatile boolean shutdown = false; + + public SocketHandler(final Socket socket) throws IOException { + this.inputStream = logEventInput.wrapStream(socket.getInputStream()); + } + + @Override + public void run() { + final EntryMessage entry = logger.traceEntry(); + boolean closed = false; + try { + try { + while (!shutdown) { + logEventInput.logEvents(inputStream, TcpSocketServer.this); + } + } catch (final EOFException e) { + closed = true; + } catch (final OptionalDataException e) { + logger.error("OptionalDataException eof=" + e.eof + " length=" + e.length, e); + } catch (final IOException e) { + logger.error("IOException encountered while reading from socket", e); + } + if (!closed) { + Closer.closeSilently(inputStream); + } + } finally { + handlers.remove(Long.valueOf(getId())); + } + logger.traceExit(entry); + } + + public void shutdown() { + this.shutdown = true; + interrupt(); + } + } + + /** + * Creates a socket server that reads JSON log events. + * + * @param port + * the port to listen + * @return a new a socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + */ + public static TcpSocketServer<InputStream> createJsonSocketServer(final int port) throws IOException { + LOGGER.entry("createJsonSocketServer", port); + final TcpSocketServer<InputStream> socketServer = new TcpSocketServer<>(port, new JsonInputStreamLogEventBridge()); + return LOGGER.exit(socketServer); + } + + /** + * Creates a socket server that reads serialized log events. + * + * @param port + * the port to listen + * @return a new a socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + */ + public static TcpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port) throws IOException { + LOGGER.entry(port); + final TcpSocketServer<ObjectInputStream> socketServer = new TcpSocketServer<>(port, new ObjectInputStreamLogEventBridge()); + return LOGGER.exit(socketServer); + } + + /** + * Creates a socket server that reads serialized log events. + * + * @param port the port to listen + * @param localBindAddress The server socket's local bin address + * @return a new a socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + * @since 2.7 + */ + public static TcpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port, final int backlog, + final InetAddress localBindAddress) throws IOException { ++ return createSerializedSocketServer(port, backlog, localBindAddress, Collections.<String>emptyList()); ++ } ++ ++ /** ++ * Creates a socket server that reads serialized log events. ++ * ++ * @param port the port to listen ++ * @param localBindAddress The server socket's local bin address ++ * @param allowedClasses additional class names to allow for deserialization ++ * @return a new a socket server ++ * @throws IOException ++ * if an I/O error occurs when opening the socket. ++ * @since 2.8.2 ++ */ ++ public static TcpSocketServer<ObjectInputStream> createSerializedSocketServer( ++ final int port, final int backlog, final InetAddress localBindAddress, final List<String> allowedClasses ++ ) throws IOException { + LOGGER.entry(port); + final TcpSocketServer<ObjectInputStream> socketServer = new TcpSocketServer<>(port, backlog, localBindAddress, - new ObjectInputStreamLogEventBridge()); ++ new ObjectInputStreamLogEventBridge(allowedClasses)); + return LOGGER.exit(socketServer); + } + + /** + * Creates a socket server that reads XML log events. + * + * @param port + * the port to listen + * @return a new a socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + */ + public static TcpSocketServer<InputStream> createXmlSocketServer(final int port) throws IOException { + LOGGER.entry(port); + final TcpSocketServer<InputStream> socketServer = new TcpSocketServer<>(port, new XmlInputStreamLogEventBridge()); + return LOGGER.exit(socketServer); + } + + /** + * Main startup for the server. Run with "--help" for to print command line help on the console. + * + * @param args + * The command line arguments. + * @throws Exception + * if an error occurs. + */ + public static void main(final String[] args) throws Exception { + final CommandLineArguments cla = BasicCommandLineArguments.parseCommandLine(args, TcpSocketServer.class, new CommandLineArguments()); + if (cla.isHelp()) { + return; + } + if (cla.getConfigLocation() != null) { + ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(cla.getConfigLocation())); + } - final TcpSocketServer<ObjectInputStream> socketServer = TcpSocketServer - .createSerializedSocketServer(cla.getPort(), cla.getBacklog(), cla.getLocalBindAddress()); ++ final TcpSocketServer<ObjectInputStream> socketServer = TcpSocketServer.createSerializedSocketServer( ++ cla.getPort(), cla.getBacklog(), cla.getLocalBindAddress(), cla.getAllowedClasses()); + final Thread serverThread = socketServer.startNewThread(); + if (cla.isInteractive()) { + socketServer.awaitTermination(serverThread); + } + } + + private final ConcurrentMap<Long, SocketHandler> handlers = new ConcurrentHashMap<>(); + + private final ServerSocket serverSocket; + + /** + * Constructor. + * + * @param port + * The server socket port. + * @param backlog + * The server socket backlog. + * @param localBindAddress TODO + * @param logEventInput + * the log even input + * @throws IOException + * if an I/O error occurs when opening the socket. + * @since 2.7 + */ + @SuppressWarnings("resource") + public TcpSocketServer(final int port, final int backlog, final InetAddress localBindAddress, final LogEventBridge<T> logEventInput) throws IOException { + this(port, logEventInput, new ServerSocket(port, backlog, localBindAddress)); + } + + /** + * Constructor. + * + * @param port + * to listen. + * @param logEventInput + * the log even input + * @throws IOException + * if an I/O error occurs when opening the socket. + */ + public TcpSocketServer(final int port, final LogEventBridge<T> logEventInput) throws IOException { + this(port, logEventInput, extracted(port)); + } + + private static ServerSocket extracted(final int port) throws IOException { + return new ServerSocket(port); + } + + /** + * Constructor. + * + * @param port + * to listen. + * @param logEventInput + * the log even input + * @param serverSocket + * the socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + */ + public TcpSocketServer(final int port, final LogEventBridge<T> logEventInput, final ServerSocket serverSocket) + throws IOException { + super(port, logEventInput); + this.serverSocket = serverSocket; + } + + /** + * Accept incoming events and processes them. + */ + @Override + public void run() { + final EntryMessage entry = logger.traceEntry(); + while (isActive()) { + if (serverSocket.isClosed()) { + return; + } + try { + // Accept incoming connections. + logger.debug("Listening for a connection {}...", serverSocket); + final Socket clientSocket = serverSocket.accept(); + logger.debug("Acepted connection on {}...", serverSocket); + logger.debug("Socket accepted: {}", clientSocket); + clientSocket.setSoLinger(true, 0); + + // accept() will block until a client connects to the server. + // If execution reaches this point, then it means that a client + // socket has been accepted. + + final SocketHandler handler = new SocketHandler(clientSocket); + handlers.put(Long.valueOf(handler.getId()), handler); + handler.start(); + } catch (final IOException e) { + if (serverSocket.isClosed()) { + // OK we're done. + logger.traceExit(entry); + return; + } + logger.error("Exception encountered on accept. Ignoring. Stack trace :", e); + } + } + for (final Map.Entry<Long, SocketHandler> handlerEntry : handlers.entrySet()) { + final SocketHandler handler = handlerEntry.getValue(); + handler.shutdown(); + try { + handler.join(); + } catch (final InterruptedException ignored) { + // Ignore the exception + } + } + logger.traceExit(entry); + } + + /** + * Shutdown the server. + * + * @throws IOException if the server socket could not be closed + */ + @Override + public void shutdown() throws IOException { + final EntryMessage entry = logger.traceEntry(); + setActive(false); + Thread.currentThread().interrupt(); + serverSocket.close(); + logger.traceExit(entry); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/394c30f5/log4j-server/src/main/java/org/apache/logging/log4j/server/UdpSocketServer.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/UdpSocketServer.java index a2bac17,0000000..12a8450 mode 100644,000000..100644 --- a/log4j-server/src/main/java/org/apache/logging/log4j/server/UdpSocketServer.java +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/UdpSocketServer.java @@@ -1,169 -1,0 +1,185 @@@ +/* + * 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.server; + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.OptionalDataException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; ++import java.util.List; + +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.util.BasicCommandLineArguments; + +/** + * Listens for Log4j events on a datagram socket and passes them on to Log4j. + * + * @param <T> + * The kind of input stream read + * @see #main(String[]) + */ +public class UdpSocketServer<T extends InputStream> extends AbstractSocketServer<T> { + + /** + * Creates a socket server that reads JSON log events. + * + * @param port + * the port to listen + * @return a new a socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + */ + public static UdpSocketServer<InputStream> createJsonSocketServer(final int port) throws IOException { + return new UdpSocketServer<>(port, new JsonInputStreamLogEventBridge()); + } + + /** + * Creates a socket server that reads serialized log events. + * + * @param port + * the port to listen + * @return a new a socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + */ + public static UdpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port) throws IOException { + return new UdpSocketServer<>(port, new ObjectInputStreamLogEventBridge()); + } + + /** ++ * Creates a socket server that reads serialized log events. ++ * ++ * @param port the port to listen ++ * @param allowedClasses additional classes to allow for deserialization ++ * @return a new a socket server ++ * @throws IOException if an I/O error occurs when opening the socket. ++ * @since 2.8.2 ++ */ ++ public static UdpSocketServer<ObjectInputStream> createSerializedSocketServer(final int port, ++ final List<String> allowedClasses) ++ throws IOException { ++ return new UdpSocketServer<>(port, new ObjectInputStreamLogEventBridge(allowedClasses)); ++ } ++ ++ /** + * Creates a socket server that reads XML log events. + * + * @param port + * the port to listen + * @return a new a socket server + * @throws IOException + * if an I/O error occurs when opening the socket. + */ + public static UdpSocketServer<InputStream> createXmlSocketServer(final int port) throws IOException { + return new UdpSocketServer<>(port, new XmlInputStreamLogEventBridge()); + } + + /** + * Main startup for the server. Run with "--help" for to print command line help on the console. + * + * @param args + * The command line arguments. + * @throws Exception + * if an error occurs. + */ + public static void main(final String[] args) throws Exception { + final CommandLineArguments cla = BasicCommandLineArguments.parseCommandLine(args, UdpSocketServer.class, new CommandLineArguments()); + if (cla.isHelp()) { + return; + } + if (cla.getConfigLocation() != null) { + ConfigurationFactory.setConfigurationFactory(new ServerConfigurationFactory(cla.getConfigLocation())); + } + final UdpSocketServer<ObjectInputStream> socketServer = UdpSocketServer - .createSerializedSocketServer(cla.getPort()); ++ .createSerializedSocketServer(cla.getPort(), cla.getAllowedClasses()); + final Thread serverThread = socketServer.startNewThread(); + if (cla.isInteractive()) { + socketServer.awaitTermination(serverThread); + } + } + + private final DatagramSocket datagramSocket; + + // max size so we only have to deal with one packet + private final int maxBufferSize = 1024 * 65 + 1024; + + /** + * Constructor. + * + * @param port + * to listen on. + * @param logEventInput + * @throws IOException + * If an error occurs. + */ + public UdpSocketServer(final int port, final LogEventBridge<T> logEventInput) throws IOException { + super(port, logEventInput); + this.datagramSocket = new DatagramSocket(port); + } + + /** + * Accept incoming events and processes them. + */ + @Override + public void run() { + while (isActive()) { + if (datagramSocket.isClosed()) { + // OK we're done. + return; + } + try { + final byte[] buf = new byte[maxBufferSize]; + final DatagramPacket packet = new DatagramPacket(buf, buf.length); + datagramSocket.receive(packet); + final ByteArrayInputStream bais = new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength()); + logEventInput.logEvents(logEventInput.wrapStream(bais), this); + } catch (final OptionalDataException e) { + if (datagramSocket.isClosed()) { + // OK we're done. + return; + } + logger.error("OptionalDataException eof=" + e.eof + " length=" + e.length, e); + } catch (final EOFException e) { + if (datagramSocket.isClosed()) { + // OK we're done. + return; + } + logger.info("EOF encountered"); + } catch (final IOException e) { + if (datagramSocket.isClosed()) { + // OK we're done. + return; + } + logger.error("Exception encountered on accept. Ignoring. Stack Trace :", e); + } + } + } + + /** + * Shutdown the server. + */ + @Override + public void shutdown() { + this.setActive(false); + Thread.currentThread().interrupt(); + datagramSocket.close(); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/394c30f5/pom.xml ----------------------------------------------------------------------
