Merge branch 'master' into log4j-server
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/b77b7d03 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/b77b7d03 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/b77b7d03 Branch: refs/heads/java9NoMultiRelease Commit: b77b7d039653bc1675e7f9a1376cedf9e4fd16c5 Parents: f515fa3 c822338 Author: Mikael StÃ¥ldal <[email protected]> Authored: Fri Mar 17 14:59:48 2017 +0100 Committer: Mikael StÃ¥ldal <[email protected]> Committed: Fri Mar 17 16:03:24 2017 +0100 ---------------------------------------------------------------------- .../core/util/datetime/FixedDateFormat.java | 32 +- .../core/util/datetime/FixedDateFormatTest.java | 177 ++++++++++- log4j-server/pom.xml | 25 +- .../core/net/mom/jms/AbstractJmsReceiver.java | 48 --- .../core/net/mom/jms/JmsQueueReceiver.java | 46 --- .../core/net/mom/jms/JmsTopicReceiver.java | 46 --- .../log4j/core/net/mom/jms/package-info.java | 26 -- .../core/net/server/AbstractLogEventBridge.java | 44 --- .../core/net/server/AbstractSocketServer.java | 209 ------------ .../net/server/InputStreamLogEventBridge.java | 103 ------ .../log4j/core/net/server/JmsServer.java | 148 --------- .../server/JsonInputStreamLogEventBridge.java | 90 ------ .../log4j/core/net/server/LogEventBridge.java | 57 ---- .../server/ObjectInputStreamLogEventBridge.java | 45 --- .../core/net/server/SecureTcpSocketServer.java | 37 --- .../log4j/core/net/server/TcpSocketServer.java | 314 ------------------- .../log4j/core/net/server/UdpSocketServer.java | 169 ---------- .../server/XmlInputStreamLogEventBridge.java | 54 ---- .../log4j/core/net/server/package-info.java | 24 -- .../log4j/server/AbstractLogEventBridge.java | 44 +++ .../log4j/server/AbstractSocketServer.java | 209 ++++++++++++ .../log4j/server/InputStreamLogEventBridge.java | 103 ++++++ .../apache/logging/log4j/server/JmsServer.java | 148 +++++++++ .../server/JsonInputStreamLogEventBridge.java | 90 ++++++ .../logging/log4j/server/LogEventBridge.java | 57 ++++ .../server/ObjectInputStreamLogEventBridge.java | 45 +++ .../log4j/server/SecureTcpSocketServer.java | 37 +++ .../logging/log4j/server/TcpSocketServer.java | 314 +++++++++++++++++++ .../logging/log4j/server/UdpSocketServer.java | 169 ++++++++++ .../server/XmlInputStreamLogEventBridge.java | 54 ++++ .../server/mom/jms/AbstractJmsReceiver.java | 48 +++ .../log4j/server/mom/jms/JmsQueueReceiver.java | 46 +++ .../log4j/server/mom/jms/JmsTopicReceiver.java | 46 +++ .../log4j/server/mom/jms/package-info.java | 26 ++ .../logging/log4j/server/package-info.java | 24 ++ log4j-server/src/site/markdown/index.md | 7 +- .../net/server/AbstractSocketServerTest.java | 237 -------------- .../core/net/server/SslXmlSocketServerTest.java | 104 ------ .../net/server/TcpJsonSocketServerTest.java | 62 ---- .../server/TcpSerializedSocketServerTest.java | 63 ---- .../core/net/server/TcpXmlSocketServerTest.java | 65 ---- .../log4j/core/net/server/ThreadIdFilter.java | 40 --- .../log4j/core/net/server/ThreadNameFilter.java | 39 --- .../core/net/server/ThreadPriorityFilter.java | 40 --- .../net/server/UdpJsonSocketServerTest.java | 58 ---- .../server/UdpSerializedSocketServerTest.java | 60 ---- .../core/net/server/UdpXmlSocketServerTest.java | 61 ---- .../log4j/server/AbstractSocketServerTest.java | 237 ++++++++++++++ .../log4j/server/SslXmlSocketServerTest.java | 104 ++++++ .../log4j/server/TcpJsonSocketServerTest.java | 62 ++++ .../server/TcpSerializedSocketServerTest.java | 63 ++++ .../log4j/server/TcpXmlSocketServerTest.java | 65 ++++ .../logging/log4j/server/ThreadIdFilter.java | 40 +++ .../logging/log4j/server/ThreadNameFilter.java | 39 +++ .../log4j/server/ThreadPriorityFilter.java | 40 +++ .../log4j/server/UdpJsonSocketServerTest.java | 58 ++++ .../server/UdpSerializedSocketServerTest.java | 60 ++++ .../log4j/server/UdpXmlSocketServerTest.java | 61 ++++ .../core/net/ssl/client.log4j2-keystore.jks | Bin 0 -> 6829 bytes .../logging/log4j/core/net/ssl/truststore.jks | Bin 0 -> 1487 bytes src/changes/changes.xml | 3 + 61 files changed, 2516 insertions(+), 2306 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/pom.xml ---------------------------------------------------------------------- diff --cc log4j-server/pom.xml index 2833871,0000000..3a370b4 mode 100644,000000..100644 --- a/log4j-server/pom.xml +++ b/log4j-server/pom.xml @@@ -1,197 -1,0 +1,220 @@@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j</artifactId> + <version>2.8.2-SNAPSHOT</version> + <relativePath>../</relativePath> + </parent> + <artifactId>log4j-server</artifactId> + <packaging>jar</packaging> + <name>Apache Log4j Server components</name> + <description>The Apache Log4j server components</description> + <properties> + <log4jParentDir>${basedir}/..</log4jParentDir> + <projectDir>/log4j-server</projectDir> + </properties> + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + </dependency> - <!-- Command line for TCP and UDP servers --> ++ <!-- Command line parsing --> + <dependency> + <groupId>com.beust</groupId> + <artifactId>jcommander</artifactId> + </dependency> ++ <!-- Used for JMS server (needs an implementation of course) --> ++ <dependency> ++ <groupId>org.jboss.spec.javax.jms</groupId> ++ <artifactId>jboss-jms-api_1.1_spec</artifactId> ++ <scope>provided</scope> ++ <optional>true</optional> ++ </dependency> ++ <!-- Required for JSON support --> ++ <dependency> ++ <groupId>com.fasterxml.jackson.core</groupId> ++ <artifactId>jackson-databind</artifactId> ++ </dependency> ++ <!-- Required for XML support --> ++ <dependency> ++ <groupId>com.fasterxml.jackson.dataformat</groupId> ++ <artifactId>jackson-dataformat-xml</artifactId> ++ </dependency> ++ <!-- POM for jackson-dataformat-xml 2.8.3 depends on woodstox-core 5.0.2 --> ++ <dependency> ++ <groupId>com.fasterxml.woodstox</groupId> ++ <artifactId>woodstox-core</artifactId> ++ <version>5.0.2</version> ++ </dependency> + + <!-- TEST DEPENDENCIES --> + + <!-- Pull in useful test classes from API --> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-all</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <!-- Include the standard NOTICE and LICENSE --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-remote-resources-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>process</goal> + </goals> + <configuration> + <skip>false</skip> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Export-Package>org.apache.logging.log4j.core.net.*</Export-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-changes-plugin</artifactId> + <version>${changes.plugin.version}</version> + <reportSets> + <reportSet> + <reports> + <report>changes-report</report> + </reports> + </reportSet> + </reportSets> + <configuration> + <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate> + <useJql>true</useJql> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + <version>${checkstyle.plugin.version}</version> + <configuration> + <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> --> + <configLocation>${log4jParentDir}/checkstyle.xml</configLocation> + <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation> + <enableRulesSummary>false</enableRulesSummary> + <propertyExpansion>basedir=${basedir}</propertyExpansion> + <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>${javadoc.plugin.version}</version> + <configuration> + <bottom><![CDATA[<p align="center">Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br /> + Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo, + and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom> + <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating + project --> + <detectOfflineLinks>false</detectOfflineLinks> + <linksource>true</linksource> + </configuration> + <reportSets> + <reportSet> + <id>non-aggregate</id> + <reports> + <report>javadoc</report> + </reports> + </reportSet> + </reportSets> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.plugin.version}</version> + <configuration> + <fork>true</fork> + <jvmArgs>-Duser.language=en</jvmArgs> + <threshold>Normal</threshold> + <effort>Default</effort> + <excludeFilterFile>${log4jParentDir}/findbugs-exclude-filter.xml</excludeFilterFile> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jxr-plugin</artifactId> + <version>${jxr.plugin.version}</version> + <reportSets> + <reportSet> + <id>non-aggregate</id> + <reports> + <report>jxr</report> + </reports> + </reportSet> + <reportSet> + <id>aggregate</id> + <reports> + <report>aggregate</report> + </reports> + </reportSet> + </reportSets> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + <version>${pmd.plugin.version}</version> + <configuration> + <targetJdk>${maven.compiler.target}</targetJdk> + </configuration> + </plugin> + </plugins> + </reporting> +</project> + http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractLogEventBridge.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractLogEventBridge.java index 0000000,0000000..5e368cb new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractLogEventBridge.java @@@ -1,0 -1,0 +1,44 @@@ ++/* ++ * 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 org.apache.logging.log4j.Logger; ++import org.apache.logging.log4j.status.StatusLogger; ++ ++/** ++ * Abstract class for implementations of {@link LogEventBridge}. ++ * ++ * @param <T> ++ * The kind of input stream read ++ */ ++public abstract class AbstractLogEventBridge<T extends InputStream> implements LogEventBridge<T> { ++ ++ protected static final int END = -1; ++ ++ protected static final Logger logger = StatusLogger.getLogger(); ++ ++ // The default is to return the same object as given. ++ @SuppressWarnings("unchecked") ++ @Override ++ public T wrapStream(final InputStream inputStream) throws IOException { ++ return (T) inputStream; ++ } ++ ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/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 0000000,0000000..a8f1140 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/AbstractSocketServer.java @@@ -1,0 -1,0 +1,209 @@@ ++/* ++ * 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.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; ++ ++ 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; ++ } ++ } ++ ++ /** ++ * 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/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/InputStreamLogEventBridge.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/InputStreamLogEventBridge.java index 0000000,0000000..68ec791 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/InputStreamLogEventBridge.java @@@ -1,0 -1,0 +1,103 @@@ ++/* ++ * 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.nio.charset.Charset; ++ ++import org.apache.logging.log4j.core.LogEvent; ++import org.apache.logging.log4j.core.LogEventListener; ++import org.apache.logging.log4j.core.impl.Log4jLogEvent; ++import org.apache.logging.log4j.util.Strings; ++ ++import com.fasterxml.jackson.databind.ObjectMapper; ++import com.fasterxml.jackson.databind.ObjectReader; ++ ++/** ++ * Reads and logs {@link LogEvent}s from an {@link InputStream}. ++ */ ++public abstract class InputStreamLogEventBridge extends AbstractLogEventBridge<InputStream> { ++ ++ private final int bufferSize; ++ ++ private final Charset charset; ++ ++ private final String eventEndMarker; ++ ++ private final ObjectReader objectReader; ++ ++ public InputStreamLogEventBridge(final ObjectMapper mapper, final int bufferSize, final Charset charset, final String eventEndMarker) { ++ this.bufferSize = bufferSize; ++ this.charset = charset; ++ this.eventEndMarker = eventEndMarker; ++ this.objectReader = mapper.readerFor(Log4jLogEvent.class); ++ } ++ ++ abstract protected int[] getEventIndices(final String text, int beginIndex); ++ ++ @Override ++ public void logEvents(final InputStream inputStream, final LogEventListener logEventListener) throws IOException { ++ String workingText = Strings.EMPTY; ++ try { ++ // Allocate buffer once ++ final byte[] buffer = new byte[bufferSize]; ++ String textRemains = workingText = Strings.EMPTY; ++ while (true) { ++ // Process until the stream is EOF. ++ final int streamReadLength = inputStream.read(buffer); ++ if (streamReadLength == END) { ++ // The input stream is EOF ++ break; ++ } ++ final String text = workingText = textRemains + new String(buffer, 0, streamReadLength, charset); ++ int beginIndex = 0; ++ while (true) { ++ // Extract and log all XML events in the buffer ++ final int[] pair = getEventIndices(text, beginIndex); ++ final int eventStartMarkerIndex = pair[0]; ++ if (eventStartMarkerIndex < 0) { ++ // No more events or partial XML only in the buffer. ++ // Save the unprocessed string part ++ textRemains = text.substring(beginIndex); ++ break; ++ } ++ final int eventEndMarkerIndex = pair[1]; ++ if (eventEndMarkerIndex > 0) { ++ final int eventEndXmlIndex = eventEndMarkerIndex + eventEndMarker.length(); ++ final String textEvent = workingText = text.substring(eventStartMarkerIndex, eventEndXmlIndex); ++ final LogEvent logEvent = unmarshal(textEvent); ++ logEventListener.log(logEvent); ++ beginIndex = eventEndXmlIndex; ++ } else { ++ // No more events or partial XML only in the buffer. ++ // Save the unprocessed string part ++ textRemains = text.substring(beginIndex); ++ break; ++ } ++ } ++ } ++ } catch (final IOException ex) { ++ logger.error(workingText, ex); ++ } ++ } ++ ++ protected Log4jLogEvent unmarshal(final String jsonEvent) throws IOException { ++ return this.objectReader.readValue(jsonEvent); ++ } ++ ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/JmsServer.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/JmsServer.java index 0000000,0000000..b673c06 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/JmsServer.java @@@ -1,0 -1,0 +1,148 @@@ ++/* ++ * 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.IOException; ++import java.io.InputStreamReader; ++import java.nio.charset.Charset; ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.atomic.AtomicReference; ++import javax.jms.JMSException; ++import javax.jms.Message; ++import javax.jms.MessageConsumer; ++import javax.jms.MessageListener; ++import javax.jms.ObjectMessage; ++ ++import org.apache.logging.log4j.LoggingException; ++import org.apache.logging.log4j.core.AbstractLifeCycle; ++import org.apache.logging.log4j.core.LifeCycle2; ++import org.apache.logging.log4j.core.LogEvent; ++import org.apache.logging.log4j.core.LogEventListener; ++import org.apache.logging.log4j.core.appender.mom.JmsManager; ++import org.apache.logging.log4j.core.net.JndiManager; ++ ++/** ++ * LogEventListener server that receives LogEvents over a JMS {@link javax.jms.Destination}. ++ * ++ * @since 2.1 ++ */ ++public class JmsServer extends LogEventListener implements MessageListener, LifeCycle2 { ++ ++ private final AtomicReference<State> state = new AtomicReference<>(State.INITIALIZED); ++ private final JmsManager jmsManager; ++ private MessageConsumer messageConsumer; ++ ++ public JmsServer(final String connectionFactoryBindingName, ++ final String destinationBindingName, ++ final String username, ++ final String password) { ++ final String managerName = JmsServer.class.getName() + '@' + JmsServer.class.hashCode(); ++ final JndiManager jndiManager = JndiManager.getDefaultManager(managerName); ++ jmsManager = JmsManager.getJmsManager(managerName, jndiManager, connectionFactoryBindingName, ++ destinationBindingName, username, password); ++ } ++ ++ @Override ++ public State getState() { ++ return state.get(); ++ } ++ ++ @Override ++ public void onMessage(final Message message) { ++ try { ++ if (message instanceof ObjectMessage) { ++ final Object body = ((ObjectMessage) message).getObject(); ++ if (body instanceof LogEvent) { ++ log((LogEvent) body); ++ } else { ++ LOGGER.warn("Expected ObjectMessage to contain LogEvent. Got type {} instead.", body.getClass()); ++ } ++ } else { ++ LOGGER.warn("Received message of type {} and JMSType {} which cannot be handled.", message.getClass(), ++ message.getJMSType()); ++ } ++ } catch (final JMSException e) { ++ LOGGER.catching(e); ++ } ++ } ++ ++ @Override ++ public void initialize() { ++ } ++ ++ @Override ++ public void start() { ++ if (state.compareAndSet(State.INITIALIZED, State.STARTING)) { ++ try { ++ messageConsumer = jmsManager.createMessageConsumer(); ++ messageConsumer.setMessageListener(this); ++ } catch (final JMSException e) { ++ throw new LoggingException(e); ++ } ++ } ++ } ++ ++ @Override ++ public void stop() { ++ stop(AbstractLifeCycle.DEFAULT_STOP_TIMEOUT, AbstractLifeCycle.DEFAULT_STOP_TIMEUNIT); ++ } ++ ++ @Override ++ public boolean stop(final long timeout, final TimeUnit timeUnit) { ++ boolean stopped = true; ++ try { ++ messageConsumer.close(); ++ } catch (final JMSException e) { ++ LOGGER.debug("Exception closing {}", messageConsumer, e); ++ stopped = false; ++ } ++ return stopped && jmsManager.stop(timeout, timeUnit); ++ } ++ ++ @Override ++ public boolean isStarted() { ++ return state.get() == State.STARTED; ++ } ++ ++ @Override ++ public boolean isStopped() { ++ return state.get() == State.STOPPED; ++ } ++ ++ /** ++ * Starts and runs this server until the user types "exit" into standard input. ++ * ++ * @throws IOException ++ * @since 2.6 ++ */ ++ public void run() throws IOException { ++ this.start(); ++ System.out.println("Type \"exit\" to quit."); ++ final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset())); ++ while (true) { ++ final String line = stdin.readLine(); ++ if (line == null || line.equalsIgnoreCase("exit")) { ++ System.out.println("Exiting. Kill the application if it does not exit due to daemon threads."); ++ this.stop(); ++ return; ++ } ++ } ++ } ++ ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/JsonInputStreamLogEventBridge.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/JsonInputStreamLogEventBridge.java index 0000000,0000000..6a06ae4 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/JsonInputStreamLogEventBridge.java @@@ -1,0 -1,0 +1,90 @@@ ++/* ++ * 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.InputStream; ++import java.nio.charset.Charset; ++ ++import org.apache.logging.log4j.core.LogEvent; ++import org.apache.logging.log4j.core.jackson.Log4jJsonObjectMapper; ++import org.apache.logging.log4j.util.Chars; ++ ++/** ++ * Reads and logs JSON {@link LogEvent}s from an {@link InputStream}.. ++ */ ++public class JsonInputStreamLogEventBridge extends InputStreamLogEventBridge { ++ ++ private static final int[] END_PAIR = new int[] { END, END }; ++ private static final char EVENT_END_MARKER = '}'; ++ private static final char EVENT_START_MARKER = '{'; ++ private static final char JSON_ESC = '\\'; ++ private static final char JSON_STR_DELIM = Chars.DQUOTE; ++ private static final boolean THREAD_CONTEXT_MAP_AS_LIST = false; ++ ++ public JsonInputStreamLogEventBridge() { ++ this(1024, Charset.defaultCharset()); ++ } ++ ++ public JsonInputStreamLogEventBridge(final int bufferSize, final Charset charset) { ++ super(new Log4jJsonObjectMapper(THREAD_CONTEXT_MAP_AS_LIST, true), bufferSize, charset, ++ String.valueOf(EVENT_END_MARKER)); ++ } ++ ++ @Override ++ protected int[] getEventIndices(final String text, final int beginIndex) { ++ // Scan the text for the end of the next JSON object. ++ final int start = text.indexOf(EVENT_START_MARKER, beginIndex); ++ if (start == END) { ++ return END_PAIR; ++ } ++ final char[] charArray = text.toCharArray(); ++ int stack = 0; ++ boolean inStr = false; ++ boolean inEsc = false; ++ for (int i = start; i < charArray.length; i++) { ++ final char c = charArray[i]; ++ if (inEsc) { ++ // Skip this char and continue ++ inEsc = false; ++ } else { ++ switch (c) { ++ case EVENT_START_MARKER: ++ if (!inStr) { ++ stack++; ++ } ++ break; ++ case EVENT_END_MARKER: ++ if (!inStr) { ++ stack--; ++ } ++ break; ++ case JSON_STR_DELIM: ++ inStr = !inStr; ++ break; ++ case JSON_ESC: ++ inEsc = true; ++ break; ++ } ++ if (stack == 0) { ++ return new int[] { start, i }; ++ } ++ } ++ } ++ return END_PAIR; ++ } ++ ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/LogEventBridge.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/LogEventBridge.java index 0000000,0000000..785c365 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/LogEventBridge.java @@@ -1,0 -1,0 +1,57 @@@ ++/* ++ * 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 org.apache.logging.log4j.core.LogEvent; ++import org.apache.logging.log4j.core.LogEventListener; ++ ++/** ++ * Reads {@link LogEvent}s from the given input stream and logs them as they are discovered on the given logger. ++ * ++ * <p> ++ * Should be stateless. ++ * </p> ++ * ++ * @param <T> ++ * The kind of {@link InputStream} to wrap and read. ++ */ ++public interface LogEventBridge<T extends InputStream> { ++ ++ /** ++ * Reads {@link LogEvent}s from the given input stream and logs them as they are discovered on the given logger. ++ * ++ * @param inputStream ++ * the input stream to read ++ * @param logEventListener ++ * TODO ++ * @throws IOException ++ */ ++ void logEvents(T inputStream, LogEventListener logEventListener) throws IOException; ++ ++ /** ++ * Wraps the given stream if needed. ++ * ++ * @param inputStream ++ * the stream to wrap ++ * @return the wrapped stream or the given stream. ++ * @throws IOException ++ */ ++ T wrapStream(InputStream inputStream) throws IOException; ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/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 0000000,0000000..9f22b22 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/ObjectInputStreamLogEventBridge.java @@@ -1,0 -1,0 +1,45 @@@ ++/* ++ * 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 org.apache.logging.log4j.core.LogEvent; ++import org.apache.logging.log4j.core.LogEventListener; ++ ++/** ++ * Reads and logs serialized {@link LogEvent} objects from an {@link ObjectInputStream}. ++ */ ++public class ObjectInputStreamLogEventBridge extends AbstractLogEventBridge<ObjectInputStream> { ++ ++ @Override ++ public void logEvents(final ObjectInputStream inputStream, final LogEventListener logEventListener) ++ 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); ++ } ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/SecureTcpSocketServer.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/SecureTcpSocketServer.java index 0000000,0000000..81c9d64 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/SecureTcpSocketServer.java @@@ -1,0 -1,0 +1,37 @@@ ++/* ++ * 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 org.apache.logging.log4j.core.net.ssl.SslConfiguration; ++ ++/** ++ * Listens for events over a secure socket connection (SSL/TLS). ++ * ++ * @param <T> ++ * The kind of input stream read ++ */ ++public class SecureTcpSocketServer<T extends InputStream> extends TcpSocketServer<T> { ++ ++ public SecureTcpSocketServer(final int port, final LogEventBridge<T> logEventInput, ++ final SslConfiguration sslConfig) throws IOException { ++ super(port, logEventInput, sslConfig.getSslServerSocketFactory().createServerSocket(port)); ++ } ++ ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/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 0000000,0000000..7856969 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/TcpSocketServer.java @@@ -1,0 -1,0 +1,314 @@@ ++/* ++ * 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.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 { ++ LOGGER.entry(port); ++ final TcpSocketServer<ObjectInputStream> socketServer = new TcpSocketServer<>(port, backlog, localBindAddress, ++ new ObjectInputStreamLogEventBridge()); ++ 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 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/b77b7d03/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 0000000,0000000..a2bac17 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/UdpSocketServer.java @@@ -1,0 -1,0 +1,169 @@@ ++/* ++ * 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 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 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()); ++ 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/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/XmlInputStreamLogEventBridge.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/XmlInputStreamLogEventBridge.java index 0000000,0000000..683ae07 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/XmlInputStreamLogEventBridge.java @@@ -1,0 -1,0 +1,54 @@@ ++/* ++ * 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.InputStream; ++import java.nio.charset.Charset; ++ ++import org.apache.logging.log4j.core.LogEvent; ++import org.apache.logging.log4j.core.jackson.Log4jXmlObjectMapper; ++ ++/** ++ * Reads and logs {@link LogEvent}s from an {@link InputStream}. ++ */ ++public class XmlInputStreamLogEventBridge extends InputStreamLogEventBridge { ++ ++ private static final String EVENT_END = "</Event>"; ++ private static final String EVENT_START_NS_N = "<Event>"; ++ private static final String EVENT_START_NS_Y = "<Event "; ++ ++ public XmlInputStreamLogEventBridge() { ++ this(1024, Charset.defaultCharset()); ++ } ++ ++ public XmlInputStreamLogEventBridge(final int bufferSize, final Charset charset) { ++ super(new Log4jXmlObjectMapper(), bufferSize, charset, EVENT_END); ++ } ++ ++ @Override ++ protected int[] getEventIndices(final String text, final int beginIndex) { ++ int start = text.indexOf(EVENT_START_NS_Y, beginIndex); ++ int startLen = EVENT_START_NS_Y.length(); ++ if (start < 0) { ++ start = text.indexOf(EVENT_START_NS_N, beginIndex); ++ startLen = EVENT_START_NS_N.length(); ++ } ++ final int end = start < 0 ? -1 : text.indexOf(EVENT_END, start + startLen); ++ return new int[] { start, end }; ++ } ++ ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/AbstractJmsReceiver.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/AbstractJmsReceiver.java index 0000000,0000000..373d31c new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/AbstractJmsReceiver.java @@@ -1,0 -1,0 +1,48 @@@ ++/* ++ * 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.mom.jms; ++ ++import org.apache.logging.log4j.server.JmsServer; ++ ++/** ++ * Common JMS server functionality. ++ * ++ * @since 2.6 ++ */ ++public abstract class AbstractJmsReceiver { ++ ++ /** ++ * Prints out usage information to {@linkplain System#err standard error}. ++ */ ++ protected abstract void usage(); ++ ++ /** ++ * Executes a JmsServer with the given command line arguments. ++ * ++ * @param args command line arguments ++ * @throws Exception ++ */ ++ protected void doMain(final String... args) throws Exception { ++ if (args.length != 4) { ++ usage(); ++ System.exit(1); ++ } ++ final JmsServer server = new JmsServer(args[0], args[1], args[2], args[3]); ++ server.run(); ++ } ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsQueueReceiver.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsQueueReceiver.java index 0000000,0000000..a7f75ec new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsQueueReceiver.java @@@ -1,0 -1,0 +1,46 @@@ ++/* ++ * 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.mom.jms; ++ ++/** ++ * Receives Log Events over a JMS Queue. This implementation expects that all messages will ++ * contain a serialized LogEvent. ++ */ ++public class JmsQueueReceiver extends AbstractJmsReceiver { ++ ++ private JmsQueueReceiver() { ++ } ++ ++ /** ++ * Main startup for the receiver. ++ * ++ * @param args The command line arguments. ++ * @throws Exception if an error occurs. ++ */ ++ public static void main(final String[] args) throws Exception { ++ final JmsQueueReceiver receiver = new JmsQueueReceiver(); ++ receiver.doMain(args); ++ } ++ ++ @Override ++ protected void usage() { ++ System.err.println("Wrong number of arguments."); ++ System.err.println("Usage: java " + JmsQueueReceiver.class.getName() ++ + " QueueConnectionFactoryBindingName QueueBindingName username password"); ++ } ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsTopicReceiver.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsTopicReceiver.java index 0000000,0000000..21828d7 new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/JmsTopicReceiver.java @@@ -1,0 -1,0 +1,46 @@@ ++/* ++ * 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.mom.jms; ++ ++/** ++ * Receives Topic messages that contain LogEvents. This implementation expects that all messages ++ * are serialized log events. ++ */ ++public class JmsTopicReceiver extends AbstractJmsReceiver { ++ ++ private JmsTopicReceiver() { ++ } ++ ++ /** ++ * Main startup for the receiver. ++ * ++ * @param args The command line arguments. ++ * @throws Exception if an error occurs. ++ */ ++ public static void main(final String[] args) throws Exception { ++ final JmsTopicReceiver receiver = new JmsTopicReceiver(); ++ receiver.doMain(args); ++ } ++ ++ @Override ++ protected void usage() { ++ System.err.println("Wrong number of arguments."); ++ System.err.println("Usage: java " + JmsTopicReceiver.class.getName() ++ + " TopicConnectionFactoryBindingName TopicBindingName username password"); ++ } ++} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/package-info.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/package-info.java index 0000000,0000000..dd9c90e new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/mom/jms/package-info.java @@@ -1,0 -1,0 +1,26 @@@ ++/* ++ * 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. ++ */ ++ ++/** ++ * Supporting network code for JMS appenders. ++ * ++ * <p>Note that you can use JmsQueueReceiver or JmsTopicReceiver as executable main classes to receive log events over ++ * JMS (sent via the appropriate JMS appender) that can be subsequently logged according to the configuration given to ++ * the running process. Of course, use of these classes as standalone executables are entirely optional and can ++ * be used directly in your application (e.g., through your Spring {@code beans.xml} configuration).</p> ++ */ ++package org.apache.logging.log4j.server.mom.jms; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/main/java/org/apache/logging/log4j/server/package-info.java ---------------------------------------------------------------------- diff --cc log4j-server/src/main/java/org/apache/logging/log4j/server/package-info.java index 0000000,0000000..c446a9d new file mode 100644 --- /dev/null +++ b/log4j-server/src/main/java/org/apache/logging/log4j/server/package-info.java @@@ -1,0 -1,0 +1,24 @@@ ++/* ++ * 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. ++ */ ++ ++/** ++ * Standalone server classes for consuming log events over a network. Each of the various servers should be used with ++ * another Log4j configuration to handle incoming {@link org.apache.logging.log4j.core.LogEvent}s. It is recommended ++ * to consider using the <a href="../../../../../../../../../manual/appenders.html#FlumeAppender">Flume Appender</a> ++ * for highly reliable networked logging. ++ */ ++package org.apache.logging.log4j.server; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/b77b7d03/log4j-server/src/site/markdown/index.md ---------------------------------------------------------------------- diff --cc log4j-server/src/site/markdown/index.md index b66e935,0000000..b3d96ba mode 100644,000000..100644 --- a/log4j-server/src/site/markdown/index.md +++ b/log4j-server/src/site/markdown/index.md @@@ -1,29 -1,0 +1,30 @@@ +<!-- vim: set syn=markdown : --> +<!-- + 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. +--> + +# Log4j Server components + +## Log4j Server components + ++Standalone server classes for consuming log events over a network. Each of the various servers should be used with ++another Log4j configuration to handle incoming log events. It is recommended to consider using the ++[Flume Appender](../manual/appenders.html#FlumeAppender) for highly reliable networked logging. + +## Requirements + +The Log4j Server components requires the Log4j 2 API and core. This component was introduced in Log4j 2.8.2, - before it was part of log4j-core. For more information, see [Runtime Dependencies](../runtime-dependencies.html). - - ## Usage ++before it was part of log4j-core. For more information, see [Runtime Dependencies](../manual/runtime-dependencies.html).
