You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2017/06/06 01:22:55 UTC
logging-log4j2 git commit: New branch for Lucene 5 Appender based on
https://github.com/apache/logging-log4j2/pull/82
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 <gg...@apache.org>
Authored: Mon Jun 5 18:22:46 2017 -0700
Committer: Gary Gregory <gg...@apache.org>
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>
+ * <Lucene5 name="lucene" ignoreExceptions="true" target="/target/lucene/index">
+ * <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36} %L %M
+ * - %msg%xEx%n"/>
+ *
+ * <IndexField name="time" pattern="%d{UNIX_MILLIS}" type="LongField"/>
+ * <IndexField name="level" pattern="%-5level" />
+ * <IndexField name="content" pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L
+ * %M - %msg%xEx%n"/>
+ * </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>