Repository: logging-log4j2
Updated Branches:
  refs/heads/Lucene5 [created] 929cc9ba9


New branch for Lucene 5 Appender based on
https://github.com/apache/logging-log4j2/pull/82

Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/929cc9ba
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/929cc9ba
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/929cc9ba

Branch: refs/heads/Lucene5
Commit: 929cc9ba9edcfa811d4beba4418ffbe7e0097922
Parents: 864b7a8
Author: Gary Gregory <[email protected]>
Authored: Mon Jun 5 18:22:46 2017 -0700
Committer: Gary Gregory <[email protected]>
Committed: Mon Jun 5 18:22:46 2017 -0700

----------------------------------------------------------------------
 log4j-bom/pom.xml                               |   6 +
 log4j-distribution/pom.xml                      |  17 +
 log4j-lucene5/pom.xml                           | 262 +++++++++++++++
 .../log4j/lucene5/appender/LuceneAnalyzer.java  |  31 ++
 .../log4j/lucene5/appender/LuceneAppender.java  | 318 +++++++++++++++++++
 .../lucene5/appender/LuceneIndexField.java      | 114 +++++++
 .../log4j/lucene5/appender/LuceneTokenizer.java |  35 ++
 .../log4j/lucene5/appender/package-info.java    |  23 ++
 .../lucene5/appender/LuceneAppenderTest.java    | 136 ++++++++
 .../src/test/resources/log4j2-lucene.xml        |  30 ++
 pom.xml                                         |   1 +
 11 files changed, 973 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-bom/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-bom/pom.xml b/log4j-bom/pom.xml
index 920f6a0..a43cf54 100644
--- a/log4j-bom/pom.xml
+++ b/log4j-bom/pom.xml
@@ -108,6 +108,12 @@
         <artifactId>log4j-liquibase</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <!-- Apache Lucene 5 Appender -->
+      <dependency>
+        <groupId>org.apache.logging.log4j</groupId>
+        <artifactId>log4j-lucene5</artifactId>
+        <version>${project.version}</version>
+      </dependency>
       <!-- Scala 2.10 API -->
       <dependency>
         <groupId>org.apache.logging.log4j</groupId>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-distribution/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-distribution/pom.xml b/log4j-distribution/pom.xml
index e77e883..af63ce0 100644
--- a/log4j-distribution/pom.xml
+++ b/log4j-distribution/pom.xml
@@ -294,6 +294,23 @@
       <version>${project.version}</version>
       <classifier>javadoc</classifier>
     </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-lucene5</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-lucene5</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-lucene5</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+    </dependency>
   </dependencies>
   <build>
     <plugins>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-lucene5/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-lucene5/pom.xml b/log4j-lucene5/pom.xml
new file mode 100644
index 0000000..f578b6b
--- /dev/null
+++ b/log4j-lucene5/pom.xml
@@ -0,0 +1,262 @@
+<?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";>
+  <parent>
+    <groupId>org.apache.logging.log4j</groupId>
+    <artifactId>log4j</artifactId>
+    <version>2.8.3-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>log4j-lucene5</artifactId>
+  <name>Apache Log4j Lucene 5</name>
+  <description>
+    Apache Log4j Lucene 5 appender.
+  </description>
+  <properties>
+    <log4jParentDir>${basedir}/..</log4jParentDir>
+    <docLabel>Lucene 5 Documentation</docLabel>
+    <projectDir>/log4j-lucene5</projectDir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-analyzers-common</artifactId>
+      <version>5.5.4</version>
+    </dependency>
+    <!-- Pull in useful test classes from API -->
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <!-- Apache Commons Compress -->
+    <dependency>
+      <groupId>org.tukaani</groupId>
+      <artifactId>xz</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- Zeroconf advertiser tests -->
+    <dependency>
+      <groupId>org.jmdns</groupId>
+      <artifactId>jmdns</artifactId>
+      <version>3.5.1</version>
+      <scope>test</scope>
+    </dependency>
+    <!-- Log4j 1.2 tests -->
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-1.2-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- SLF4J tests -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-ext</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- JUnit, naturally -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- Mocking framework for use with JUnit -->
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- Embedded JDBC drivers for database appender tests -->
+    <dependency>
+      <groupId>org.hsqldb</groupId>
+      <artifactId>hsqldb</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- JPA Tests -->
+    <dependency>
+      <groupId>org.eclipse.persistence</groupId>
+      <artifactId>org.eclipse.persistence.jpa</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- Useful mock classes and utilities -->
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- JPA, JNDI and JMS tests -->
+    <dependency>
+      <groupId>org.apache.activemq</groupId>
+      <artifactId>activemq-broker</artifactId>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.geronimo.specs</groupId>
+          <artifactId>geronimo-jms_1.1_spec</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- Logback performance tests -->
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- OSGi tests -->
+    <dependency>
+      <groupId>org.eclipse.tycho</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.framework</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-utils</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- GELF -->
+    <dependency>
+      <groupId>net.javacrumbs.json-unit</groupId>
+      <artifactId>json-unit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.xmlunit</groupId>
+      <artifactId>xmlunit-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.xmlunit</groupId>
+      <artifactId>xmlunit-matchers</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- Other -->
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache-extras.beanshell</groupId>
+      <artifactId>bsh</artifactId>
+      <version>2.0b5</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.groovy</groupId>
+      <artifactId>groovy-all</artifactId>
+      <version>2.4.7</version>
+      <scope>test</scope>
+    </dependency>
+    <!-- GC-free -->
+    <dependency>
+      <groupId>com.google.code.java-allocation-instrumenter</groupId>
+      <artifactId>java-allocation-instrumenter</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hdrhistogram</groupId>
+      <artifactId>HdrHistogram</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <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>*</Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneAnalyzer.java
----------------------------------------------------------------------
diff --git 
a/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneAnalyzer.java
 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneAnalyzer.java
new file mode 100644
index 0000000..bfff51e
--- /dev/null
+++ 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneAnalyzer.java
@@ -0,0 +1,31 @@
+/*
+ * 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.lucene5.appender;
+
+import org.apache.lucene.analysis.Analyzer;
+
+/**
+ * Tokenizes the entire stream as a single token, but case insensitive.
+ */
+public class LuceneAnalyzer extends Analyzer {
+
+       @SuppressWarnings("resource")
+       @Override
+       protected TokenStreamComponents createComponents(final String 
fieldName) {
+               return new TokenStreamComponents(new LuceneTokenizer());
+       }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneAppender.java
----------------------------------------------------------------------
diff --git 
a/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneAppender.java
 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneAppender.java
new file mode 100644
index 0000000..8964fb1
--- /dev/null
+++ 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneAppender.java
@@ -0,0 +1,318 @@
+/*
+ * 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.lucene5.appender;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.file.Paths;
+import java.util.Calendar;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.Scheduled;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import 
org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.core.util.CronExpression;
+import org.apache.logging.log4j.util.Strings;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.LongField;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.store.FSDirectory;
+
+/**
+ * This Appender writes logging events to a Lucene index library. It takes a
+ * list of {@link IndexField} with which determines which fields are written to
+ * the index library.
+ * 
+ * <pre>
+ * &lt;Lucene5 name="lucene" ignoreExceptions="true" 
target="/target/lucene/index">
+ *   &lt;PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level 
%class{36} %L %M
+ *     - %msg%xEx%n"/>
+ * 
+ *   &lt;IndexField name="time" pattern="%d{UNIX_MILLIS}" type="LongField"/>
+ *   &lt;IndexField name="level" pattern="%-5level" />
+ *   &lt;IndexField name="content" pattern="%d{HH:mm:ss.SSS} %-5level 
%class{36} %L
+ *     %M - %msg%xEx%n"/>
+ * &lt;/Lucene5>
+ * </pre>
+ */
+@Plugin(name = "Lucene5", category = Node.CATEGORY, elementType = 
Appender.ELEMENT_TYPE, printObject = true)
+@Scheduled
+public class LuceneAppender extends AbstractAppender {
+       public static class Builder<B extends Builder<B>> extends 
AbstractAppender.Builder<B>
+                       implements 
org.apache.logging.log4j.core.util.Builder<LuceneAppender> {
+
+               @PluginConfiguration
+               private Configuration configuration;
+
+               @PluginBuilderAttribute
+               private Integer expirySeconds;
+
+               @PluginElement("IndexField")
+               @Required(message = "No IndexField provided")
+               private LuceneIndexField[] indexField;
+
+               @PluginBuilderAttribute
+               @Required(message = "No target provided")
+               private String target;
+
+               @Override
+               public LuceneAppender build() {
+                       return new LuceneAppender(getName(), 
isIgnoreExceptions(), getFilter(), this.getLayout(), this.target,
+                                       this.expirySeconds, this.indexField, 
this.configuration);
+               }
+
+               @Override
+               public B setConfiguration(final Configuration config) {
+                       this.configuration = config;
+                       return asBuilder();
+               }
+
+               public B setExpirySeconds(final Integer expirySeconds) {
+                       this.expirySeconds = expirySeconds;
+                       return this.asBuilder();
+               }
+
+               public B setIndexField(final LuceneIndexField... indexField) {
+                       this.indexField = indexField;
+                       return this.asBuilder();
+               }
+
+               public B setTarget(final String target) {
+                       this.target = target;
+                       return this.asBuilder();
+               }
+       }
+
+       /**
+        * IndexWriter corresponding to each index directory.
+        */
+       private static final ConcurrentHashMap<String, IndexWriter> writerMap = 
new ConcurrentHashMap<>();
+
+       @PluginBuilderFactory
+       public static <B extends Builder<B>> B newBuilder() {
+               return new Builder<B>().asBuilder();
+       }
+
+       private final Configuration configuration;
+
+       /**
+        * Index expiration time (seconds)
+        */
+       private final Integer expirySeconds;
+
+       /**
+        * IndexField array.
+        */
+       private final LuceneIndexField[] indexFields;
+
+       /**
+        * Index directory
+        */
+       private final String target;
+
+       protected LuceneAppender(String name, boolean ignoreExceptions, Filter 
filter,
+                       Layout<? extends Serializable> layout, String target, 
Integer expiryTime, LuceneIndexField[] indexFields,
+                       final Configuration configuration) {
+               super(name, filter, layout, ignoreExceptions);
+               this.target = target;
+               this.expirySeconds = expiryTime;
+               this.indexFields = indexFields;
+               this.configuration = configuration;
+               getIndexWriter();
+               initialize();
+       }
+
+       /**
+        * create lucene index.
+        * 
+        * @param event
+        */
+       @Override
+       public void append(LogEvent event) {
+               if (null != indexFields && indexFields.length > 0) {
+                       IndexWriter indexWriter = getIndexWriter();
+                       if (null != indexWriter) {
+                               Document doc = new Document();
+                               doc.add(new LongField("timestamp", 
event.getTimeMillis(), Field.Store.YES));
+                               try {
+                                       for (LuceneIndexField field : 
indexFields) {
+                                               String value = 
field.getLayout().toSerializable(event);
+                                               if (Strings.isEmpty(value) || 
value.matches("[$]\\{.+\\}")) {
+                                                       return;
+                                               }
+                                               value = value.trim();
+                                               String type = field.getType();
+                                               if (Strings.isNotEmpty(type)) {
+                                                       Class<?> clazz = 
Class.forName("org.apache.lucene.document." + type);
+                                                       if (clazz == 
LongField.class) {
+                                                               doc.add(new 
LongField(field.getName(), Long.valueOf(value), Field.Store.YES));
+                                                       } else if (clazz == 
StringField.class) {
+                                                               doc.add(new 
StringField(field.getName(), value, Field.Store.YES));
+                                                       } else if (clazz == 
TextField.class) {
+                                                               doc.add(new 
TextField(field.getName(), value, Field.Store.YES));
+                                                       } else {
+                                                               // TODO Should 
we throw an AppenderLoggingException here?
+                                                               throw new 
UnsupportedOperationException(type + " type currently not supported.");
+                                                       }
+                                               } else {
+                                                       doc.add(new 
TextField(field.getName(), value, Field.Store.YES));
+                                               }
+                                       }
+                                       indexWriter.addDocument(doc);
+                               } catch (Exception e) {
+                                       LOGGER.error(e.getMessage(), e);
+                                       if (!ignoreExceptions()) {
+                                               throw new 
AppenderLoggingException(e);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       @Override
+       public void initialize() {
+               try {
+                       registerCommitTimer();
+                       if (this.expirySeconds != null) {
+                               registerClearTimer();
+                       }
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
+               super.initialize();
+       }
+
+       /**
+        * IndexWriter initialization.
+        */
+       private IndexWriter getIndexWriter() {
+               if (null == writerMap.get(target)) {
+                       try {
+                               // TODO Who closes this FSDirectory?
+                               FSDirectory fsDir = 
FSDirectory.open(Paths.get(this.target));
+                               // TODO Who closes this LuceneAnalyzer?
+                               IndexWriterConfig writerConfig = new 
IndexWriterConfig(new LuceneAnalyzer());
+                               writerMap.putIfAbsent(target, new 
IndexWriter(fsDir, writerConfig));
+                       } catch (IOException e) {
+                               LOGGER.error("IndexWriter initialization 
failed: {}", e.getMessage(), e);
+                       }
+               }
+               return writerMap.get(target);
+       }
+
+       /**
+        * Register IndexWriter clean timertask. Delete the index before
+        * {@link LuceneAppender#expirySeconds} second every day at 0.
+        * 
+        * @see LuceneAppender#expirySeconds
+        */
+       private void registerClearTimer() throws Exception {
+               Calendar calendar = Calendar.getInstance();
+               long curMillis = calendar.getTimeInMillis();
+               calendar.add(Calendar.DAY_OF_MONTH, 1);
+               calendar.set(Calendar.HOUR_OF_DAY, 0);
+               calendar.set(Calendar.MINUTE, 0);
+               calendar.set(Calendar.SECOND, 0);
+               calendar.set(Calendar.MILLISECOND, 0);
+               long difMinutes = (calendar.getTimeInMillis() - curMillis) / 
(1000 * 60);
+               configuration.getScheduler().scheduleAtFixedRate(new Runnable() 
{
+                       @Override
+                       public void run() {
+                               LOGGER.info("Deleting index {} {}...", target, 
expirySeconds);
+                               IndexWriter indexWriter = getIndexWriter();
+                               if (null != indexWriter) {
+                                       Long start = 0L;
+                                       Long end = System.currentTimeMillis() - 
expirySeconds * 1000;
+                                       NumericRangeQuery<Long> rangeQuery = 
NumericRangeQuery.newLongRange("timestamp", start, end, true,
+                                                       true);
+                                       try {
+                                               
indexWriter.deleteDocuments(rangeQuery);
+                                               indexWriter.commit();
+                                               LOGGER.info("Deleted index end 
{} {}", target, expirySeconds);
+                                       } catch (IOException e) {
+                                               LOGGER.error("Failed to delete 
index: {}", e.getMessage(), e);
+                                       }
+                               }
+                       }
+               }, difMinutes, 1440, TimeUnit.MINUTES);
+       }
+
+       /**
+        * Register IndexWriter commit timertask.
+        */
+       private void registerCommitTimer() throws Exception {
+               configuration.getScheduler().scheduleWithCron(new 
CronExpression("0 1/1 * * * ? "), new Runnable() {
+                       @Override
+                       public void run() {
+                               IndexWriter indexWriter = getIndexWriter();
+                               if (null != indexWriter && 
indexWriter.numRamDocs() > 0) {
+                                       try {
+                                               indexWriter.commit();
+                                       } catch (IOException e) {
+                                               LOGGER.error("IndexWriter 
commit failed: {}", e.getMessage(), e);
+                                       }
+                               }
+                       }
+               });
+       }
+
+       @Override
+       public final void start() {
+               if (null == writerMap.get(target)) {
+                       LOGGER.error("No IndexWriter set for appender [{}].", 
this.getName());
+               }
+               super.start();
+       }
+
+       @Override
+       public boolean stop(final long timeout, final TimeUnit timeUnit) {
+               setStopping();
+               boolean stopped = super.stop(timeout, timeUnit, false);
+               IndexWriter indexWriter = writerMap.get(target);
+               if (null != indexWriter) {
+                       try {
+                               indexWriter.commit();
+                               if (indexWriter.isOpen()) {
+                                       indexWriter.close();
+                               }
+                               writerMap.remove(target);
+                       } catch (IOException e) {
+                               LOGGER.error(e.getMessage(), e);
+                       }
+               }
+               setStopped();
+               return stopped;
+       }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneIndexField.java
----------------------------------------------------------------------
diff --git 
a/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneIndexField.java
 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneIndexField.java
new file mode 100644
index 0000000..39f2aea
--- /dev/null
+++ 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneIndexField.java
@@ -0,0 +1,114 @@
+/*
+ * 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.lucene5.appender;
+
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import 
org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
+import org.apache.logging.log4j.core.filter.AbstractFilterable;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+
+/**
+ * {@link LuceneAppender}'s configuration element that logs the log event
+ * attributes to the Field in the Lucene document.
+ */
+@Plugin(name = "IndexField", category = Node.CATEGORY, printObject = true)
+public final class LuceneIndexField {
+
+       public static class Builder<B extends Builder<B>> extends 
AbstractFilterable.Builder<B>
+                       implements 
org.apache.logging.log4j.core.util.Builder<LuceneIndexField> {
+
+               @PluginConfiguration
+               private Configuration configuration;
+
+               @PluginBuilderAttribute
+               @Required(message = "No name provided")
+               private String name;
+
+               @PluginBuilderAttribute
+               @Required(message = "No pattern provided")
+               private String pattern;
+
+               @PluginBuilderAttribute
+               private String type;
+
+               @Override
+               public LuceneIndexField build() {
+                       // @formatter:off
+                       final PatternLayout layout = PatternLayout.newBuilder()
+                                       .withPattern(pattern)
+                                       .withConfiguration(configuration)
+                                       .withAlwaysWriteExceptions(false)
+                                       .build();
+                       // @formatter:on
+                       return new LuceneIndexField(name, layout, type);
+               }
+
+               public B withName(final String name) {
+                       this.name = name;
+                       return this.asBuilder();
+               }
+
+               public B withPattern(final String pattern) {
+                       this.pattern = pattern;
+                       return this.asBuilder();
+               }
+
+               public B withType(final String type) {
+                       this.type = type;
+                       return this.asBuilder();
+               }
+       }
+
+       @PluginBuilderFactory
+       public static <B extends Builder<B>> B newBuilder() {
+               return new Builder<B>().asBuilder();
+       }
+
+       private final PatternLayout layout;
+
+       private final String name;
+
+       private final String type;
+
+       private LuceneIndexField(final String name, final PatternLayout layout, 
final String type) {
+               this.name = name;
+               this.layout = layout;
+               this.type = type;
+       }
+
+       public PatternLayout getLayout() {
+               return this.layout;
+       }
+
+       public String getName() {
+               return this.name;
+       }
+
+       public String getType() {
+               return type;
+       }
+
+       @Override
+       public String toString() {
+               return "{name=" + this.name + ", layout=" + this.layout + " }";
+       }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneTokenizer.java
----------------------------------------------------------------------
diff --git 
a/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneTokenizer.java
 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneTokenizer.java
new file mode 100644
index 0000000..c588a33
--- /dev/null
+++ 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/LuceneTokenizer.java
@@ -0,0 +1,35 @@
+/*
+ * 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.lucene5.appender;
+
+import org.apache.lucene.analysis.util.CharTokenizer;
+
+/**
+ * Emits the entire input as a single token.
+ */
+public class LuceneTokenizer extends CharTokenizer {
+
+       @Override
+       protected boolean isTokenChar(int c) {
+               return true;
+       }
+
+       @Override
+       protected int normalize(int c) {
+               return Character.toLowerCase(c);
+       }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/package-info.java
----------------------------------------------------------------------
diff --git 
a/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/package-info.java
 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/package-info.java
new file mode 100644
index 0000000..bc0da3c
--- /dev/null
+++ 
b/log4j-lucene5/src/main/java/org/apache/logging/log4j/lucene5/appender/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+/**
+ * The classes in this package provide appenders for Lucene 5 and related
+ * configuration scheme.
+ */
+package org.apache.logging.log4j.lucene5.appender;
+
+;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-lucene5/src/test/java/org/apache/logging/log4j/lucene5/appender/LuceneAppenderTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-lucene5/src/test/java/org/apache/logging/log4j/lucene5/appender/LuceneAppenderTest.java
 
b/log4j-lucene5/src/test/java/org/apache/logging/log4j/lucene5/appender/LuceneAppenderTest.java
new file mode 100644
index 0000000..1286d78
--- /dev/null
+++ 
b/log4j-lucene5/src/test/java/org/apache/logging/log4j/lucene5/appender/LuceneAppenderTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.lucene5.appender;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.junit.CleanFolders;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.lucene5.appender.LuceneAppender;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.store.FSDirectory;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class LuceneAppenderTest {
+       
+       private class LuceneAppenderRunner implements Runnable {
+               @Override
+               public void run() {
+                       try {
+                               write();
+                       } catch (Exception e) {
+                               throw new RuntimeException(e);
+                       }
+               }
+       }
+
+       private static final String CONFIGURATION_FILE = "log4j2-lucene.xml";
+       private static final String FIELD_1 = "field1";
+       private static final String FIELD_2 = "field2";
+       private static final String LOG_MESSAGE = "Hello world!";
+       private static final String LOGGER_NAME = "TestLogger";
+       private static final String TARGET_FOLDER = "target/lucene";
+       private static final String EXEPECTED_REGEX = "^\\d{4}-\\d{2}-\\d{2} 
\\d{2}:\\d{2}:\\d{2},\\d{3} \\[[^\\]]*\\] INFO "
+                       + LOGGER_NAME + " - " + LOG_MESSAGE;
+       private static final Path PATH = Paths.get(TARGET_FOLDER);
+
+       private static final int THREAD_COUNT = 50;
+
+       @Rule
+       public LoggerContextRule ctx = new 
LoggerContextRule(CONFIGURATION_FILE);
+
+       @Rule
+       public CleanFolders folders = new CleanFolders(PATH);
+
+       @Test
+       public void testMultipleThreads() throws Exception {
+               final ExecutorService threadPool = 
Executors.newFixedThreadPool(THREAD_COUNT);
+               final LuceneAppenderRunner runner = new LuceneAppenderRunner();
+               for (int i = 0; i < THREAD_COUNT; ++i) {
+                       threadPool.execute(runner);
+               }
+
+               // Waiting for lucene records to complete and submit
+               Thread.sleep(3000);
+
+               verify(THREAD_COUNT);
+       }
+
+       @Test
+       public void testSimple() throws Exception {
+               write();
+               verify(1);
+       }
+
+       private final synchronized void verify(final int exepectedTotalHits) 
throws Exception {
+               try (final FSDirectory fsDir = FSDirectory.open(PATH); final 
IndexReader reader = DirectoryReader.open(fsDir)) {
+                       final IndexSearcher searcher = new 
IndexSearcher(reader);
+                       final TopDocs all = searcher.search(new 
MatchAllDocsQuery(), Integer.MAX_VALUE);
+                       Assert.assertEquals(all.totalHits, exepectedTotalHits);
+                       for (ScoreDoc scoreDoc : all.scoreDocs) {
+                               final Document doc = searcher.doc(scoreDoc.doc);
+                               Assert.assertEquals(doc.getFields().size(), 3);
+                               final String field1 = doc.get(FIELD_1);
+                               Assert.assertTrue("Unexpected field1: " + 
field1, Level.INFO.toString().equals(field1));
+                               final String field2 = doc.get(FIELD_2);
+                               final Pattern pattern = 
Pattern.compile(EXEPECTED_REGEX);
+                               final Matcher matcher = pattern.matcher(field2);
+                               Assert.assertTrue("Unexpected field2: " + 
field2, matcher.matches());
+                       }
+               }
+       }
+
+       private final void write() throws Exception {
+               final LuceneAppender appender = (LuceneAppender) 
ctx.getRequiredAppender("Lucene5Appender");
+               try {
+                       appender.start();
+                       assertTrue("Appender did not start", 
appender.isStarted());
+                       // @formatter:off
+                       final Log4jLogEvent event = Log4jLogEvent.newBuilder()
+                                       .setLoggerName(LOGGER_NAME)
+                                       
.setLoggerFqcn(LuceneAppenderTest.class.getName())
+                                       .setLevel(Level.INFO)
+                                       .setMessage(new 
SimpleMessage(LOG_MESSAGE))
+                                       .build();
+                       // @formatter:on
+                       appender.append(event);
+               } finally {
+                       appender.stop();
+               }
+               assertFalse("Appender did not stop", appender.isStarted());
+       }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/log4j-lucene5/src/test/resources/log4j2-lucene.xml
----------------------------------------------------------------------
diff --git a/log4j-lucene5/src/test/resources/log4j2-lucene.xml 
b/log4j-lucene5/src/test/resources/log4j2-lucene.xml
new file mode 100644
index 0000000..f8ce397
--- /dev/null
+++ b/log4j-lucene5/src/test/resources/log4j2-lucene.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<Configuration name="Lucene5AppenderTest" packages="com.wb.testCase">
+  <Appenders>
+    <Lucene5 name="Lucene5Appender" target="target/lucene">
+      <IndexField name="field1" pattern="%-5level" type="TextField" />
+      <IndexField name="field2" pattern="%d [%t] %p %c - %m%n" />
+    </Lucene5>
+  </Appenders>
+  <Loggers>
+    <Root level="info">
+      <AppenderRef ref="Lucene5Appender" />
+    </Root>
+  </Loggers>
+</Configuration>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/929cc9ba/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index e395d7e..b40f778 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1341,6 +1341,7 @@
     <module>log4j-iostreams</module>
     <module>log4j-jul</module>
     <module>log4j-liquibase</module>
+    <module>log4j-lucene5</module>
     <module>log4j-api-scala_2.10</module>
     <module>log4j-api-scala_2.11</module>
   </modules>

Reply via email to