This is an automated email from the ASF dual-hosted git repository.
rmani pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 44b1aa5 RANGER-1837:Enhance Ranger Audit to HDFS to support ORC file
format
44b1aa5 is described below
commit 44b1aa5bf72b88f04c5ced06d5b9fea3b908fdbf
Author: Ramesh Mani <[email protected]>
AuthorDate: Wed Mar 31 10:51:56 2021 -0700
RANGER-1837:Enhance Ranger Audit to HDFS to support ORC file format
---
agents-audit/pom.xml | 11 +
.../audit/destination/HDFSAuditDestination.java | 324 +++------------
.../apache/ranger/audit/provider/AuditHandler.java | 4 +-
.../audit/provider/AuditProviderFactory.java | 29 +-
.../ranger/audit/provider/AuditWriterFactory.java | 117 ++++++
.../ranger/audit/provider/BaseAuditHandler.java | 7 +
.../ranger/audit/provider/DummyAuditProvider.java | 9 +
.../org/apache/ranger/audit/provider/MiscUtil.java | 13 +
.../audit/provider/MultiDestAuditProvider.java | 14 +
.../audit/queue/AuditFileCacheProviderSpool.java | 9 +-
.../apache/ranger/audit/queue/AuditFileQueue.java | 126 ++++++
...ProviderSpool.java => AuditFileQueueSpool.java} | 316 +++++++++-----
.../audit/utils/AbstractRangerAuditWriter.java | 353 ++++++++++++++++
.../org/apache/ranger/audit/utils/ORCFileUtil.java | 454 +++++++++++++++++++++
.../ranger/audit/utils/RangerAuditWriter.java | 39 ++
.../ranger/audit/utils/RangerJSONAuditWriter.java | 175 ++++++++
.../ranger/audit/utils/RangerORCAuditWriter.java | 189 +++++++++
17 files changed, 1809 insertions(+), 380 deletions(-)
diff --git a/agents-audit/pom.xml b/agents-audit/pom.xml
index b9f6af2..06ed81f 100644
--- a/agents-audit/pom.xml
+++ b/agents-audit/pom.xml
@@ -308,5 +308,16 @@
<artifactId>httpcore</artifactId>
<version>${httpcomponents.httpcore.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.hive</groupId>
+ <artifactId>hive-exec</artifactId>
+ <version>${hive.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
</dependencies>
</project>
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java
b/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java
index 5e6f402..ec9dc4b 100644
---
a/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java
@@ -19,25 +19,20 @@
package org.apache.ranger.audit.destination;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.URI;
+import java.io.File;
import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
-import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.FSDataOutputStream;
-import org.apache.hadoop.fs.FileSystem;
-import org.apache.hadoop.fs.Path;
-import org.apache.hadoop.fs.StreamCapabilities;
import org.apache.ranger.audit.model.AuditEventBase;
+import org.apache.ranger.audit.provider.AuditWriterFactory;
import org.apache.ranger.audit.provider.MiscUtil;
-import org.apache.ranger.audit.utils.RollingTimeUtil;
+import org.apache.ranger.audit.utils.RangerAuditWriter;
/**
* This class write the logs to local file
@@ -46,90 +41,24 @@ public class HDFSAuditDestination extends AuditDestination {
private static final Log logger = LogFactory
.getLog(HDFSAuditDestination.class);
- public static final String PROP_HDFS_DIR = "dir";
- public static final String PROP_HDFS_SUBDIR = "subdir";
- public static final String PROP_HDFS_FILE_NAME_FORMAT =
"filename.format";
- public static final String PROP_HDFS_ROLLOVER = "file.rollover.sec";
- public static final String PROP_HDFS_ROLLOVER_PERIOD =
"file.rollover.period";
-
- int fileRolloverSec = 24 * 60 * 60; // In seconds
-
- private String logFileNameFormat;
-
- private String rolloverPeriod;
-
- boolean initDone = false;
-
- private String logFolder;
-
- private PrintWriter logWriter = null;
- volatile FSDataOutputStream ostream = null; // output stream wrapped in
logWriter
-
- private String currentFileName;
-
- private boolean isStopped = false;
-
- private RollingTimeUtil rollingTimeUtil = null;
-
- private Date nextRollOverTime = null;
-
- private boolean rollOverByDuration = false;
-
- private boolean isHFlushCapableStream = false;
+ private Map<String, String> auditConfigs = null;
+ private String auditProviderName = null;
+ private RangerAuditWriter auditWriter = null;
+ private boolean initDone = false;
+ private boolean isStopped = false;
@Override
public void init(Properties prop, String propPrefix) {
super.init(prop, propPrefix);
-
- // Initialize properties for this class
- // Initial folder and file properties
- String logFolderProp = MiscUtil.getStringProperty(props,
propPrefix
- + "." + PROP_HDFS_DIR);
- if (logFolderProp == null || logFolderProp.isEmpty()) {
- logger.fatal("File destination folder is not
configured. Please set "
- + propPrefix + "." + PROP_HDFS_DIR + ".
name=" + getName());
- return;
- }
-
- String logSubFolder = MiscUtil.getStringProperty(props,
propPrefix
- + "." + PROP_HDFS_SUBDIR);
- if (logSubFolder == null || logSubFolder.isEmpty()) {
- logSubFolder = "%app-type%/%time:yyyyMMdd%";
- }
-
- logFileNameFormat = MiscUtil.getStringProperty(props,
propPrefix + "."
- + PROP_HDFS_FILE_NAME_FORMAT);
- fileRolloverSec = MiscUtil.getIntProperty(props, propPrefix +
"."
- + PROP_HDFS_ROLLOVER, fileRolloverSec);
-
- if (logFileNameFormat == null || logFileNameFormat.isEmpty()) {
- logFileNameFormat =
"%app-type%_ranger_audit_%hostname%" + ".log";
- }
-
- logFolder = logFolderProp + "/" + logSubFolder;
- logger.info("logFolder=" + logFolder + ", destName=" +
getName());
- logger.info("logFileNameFormat=" + logFileNameFormat + ",
destName="
- + getName());
- logger.info("config=" + configProps.toString());
-
- rolloverPeriod = MiscUtil.getStringProperty(props, propPrefix
+ "." + PROP_HDFS_ROLLOVER_PERIOD);
- rollingTimeUtil = RollingTimeUtil.getInstance();
-
- //file.rollover.period is used for rolling over. If it could
compute the next roll over time using file.rollover.period
- //it fall back to use file.rollover.sec for find next rollover
time. If still couldn't find default will be 1day window
- //for rollover.
- if(StringUtils.isEmpty(rolloverPeriod) ) {
- rolloverPeriod =
rollingTimeUtil.convertRolloverSecondsToRolloverPeriod(fileRolloverSec);
- }
+ this.auditProviderName = getName();
+ this.auditConfigs = configProps;
try {
- nextRollOverTime =
rollingTimeUtil.computeNextRollingTime(rolloverPeriod);
- } catch ( Exception e) {
- logger.warn("Rollover by file.rollover.period
failed...will be using the file.rollover.sec for hdfs audit file
rollover...",e);
- rollOverByDuration = true;
- nextRollOverTime = rollOverByDuration();
+ this.auditWriter = getWriter();
+ this.initDone = true;
+ } catch (Exception e) {
+ logger.error("Error while getting Audit writer", e);
}
- initDone = true;
}
@Override
@@ -146,33 +75,10 @@ public class HDFSAuditDestination extends AuditDestination
{
logError("log() called after stop was requested. name="
+ getName());
return false;
}
-
- PrintWriter out = null;
try {
- if (logger.isDebugEnabled()) {
- logger.debug("UGI=" + MiscUtil.getUGILoginUser()
- + ". Will write to HDFS file="
+ currentFileName);
- }
-
- out = MiscUtil.executePrivilegedAction(new
PrivilegedExceptionAction<PrintWriter>() {
- @Override
- public PrintWriter run() throws Exception {
- PrintWriter out = getLogFileStream();
- for (String event : events) {
- out.println(event);
- }
- return out;
- };
- });
-
- // flush and check the stream for errors
- if (out.checkError()) {
- // In theory, this count may NOT be accurate as
part of the messages may have been successfully written.
- // However, in practice, since client does
buffering, either all of none would succeed.
+ boolean ret = auditWriter.log(events);
+ if (!ret) {
addDeferredCount(events.size());
- out.close();
- logWriter = null;
- ostream = null;
return false;
}
} catch (Throwable t) {
@@ -181,7 +87,7 @@ public class HDFSAuditDestination extends AuditDestination {
return false;
} finally {
logger.info("Flushing HDFS audit. Event Size:" +
events.size());
- if (out != null) {
+ if (auditWriter != null) {
flush();
}
}
@@ -190,12 +96,40 @@ public class HDFSAuditDestination extends AuditDestination
{
}
@Override
+ synchronized public boolean logFile(final File file) {
+ logStatusIfRequired();
+ if (!initDone) {
+ return false;
+ }
+ if (isStopped) {
+ logError("log() called after stop was requested. name="
+ getName());
+ return false;
+ }
+
+ try {
+ boolean ret = auditWriter.logFile(file);
+ if (!ret) {
+ return false;
+ }
+ } catch (Throwable t) {
+ logError("Error writing to log file.", t);
+ return false;
+ } finally {
+ logger.info("Flushing HDFS audit. File:" +
file.getAbsolutePath() + file.getName());
+ if (auditWriter != null) {
+ flush();
+ }
+ }
+ return true;
+ }
+
+ @Override
public void flush() {
logger.info("Flush called. name=" + getName());
MiscUtil.executePrivilegedAction(new PrivilegedAction<Void>() {
@Override
public Void run() {
- hflush();
+ auditWriter.flush();
return null;
}
});
@@ -244,158 +178,14 @@ public class HDFSAuditDestination extends
AuditDestination {
@Override
synchronized public void stop() {
- isStopped = true;
- if (logWriter != null) {
- try {
- logWriter.flush();
- logWriter.close();
- } catch (Throwable t) {
- logger.error("Error on closing log writter.
Exception will be ignored. name="
- + getName() + ", fileName=" +
currentFileName);
- }
- logWriter = null;
- ostream = null;
- }
+ auditWriter.stop();
logStatus();
+ isStopped = true;
}
- // Helper methods in this class
- synchronized private PrintWriter getLogFileStream() throws Exception {
- closeFileIfNeeded();
-
- // Either there are no open log file or the previous one has
been rolled
- // over
- if (logWriter == null) {
- Date currentTime = new Date();
- // Create a new file
- String fileName =
MiscUtil.replaceTokens(logFileNameFormat,
- currentTime.getTime());
- String parentFolder = MiscUtil.replaceTokens(logFolder,
- currentTime.getTime());
- Configuration conf = createConfiguration();
-
- String fullPath = parentFolder + Path.SEPARATOR +
fileName;
- String defaultPath = fullPath;
- URI uri = URI.create(fullPath);
- FileSystem fileSystem = FileSystem.get(uri, conf);
-
- Path hdfPath = new Path(fullPath);
- logger.info("Checking whether log file exists.
hdfPath=" + fullPath + ", UGI=" + MiscUtil.getUGILoginUser());
- int i = 0;
- while (fileSystem.exists(hdfPath)) {
- i++;
- int lastDot = defaultPath.lastIndexOf('.');
- String baseName = defaultPath.substring(0,
lastDot);
- String extension =
defaultPath.substring(lastDot);
- fullPath = baseName + "." + i + extension;
- hdfPath = new Path(fullPath);
- logger.info("Checking whether log file exists.
hdfPath="
- + fullPath);
- }
- logger.info("Log file doesn't exists. Will create and
use it. hdfPath="
- + fullPath);
- // Create parent folders
- createParents(hdfPath, fileSystem);
-
- // Create the file to write
- logger.info("Creating new log file. hdfPath=" +
fullPath);
- ostream = fileSystem.create(hdfPath);
- logWriter = new PrintWriter(ostream);
- currentFileName = fullPath;
- isHFlushCapableStream =
ostream.hasCapability(StreamCapabilities.HFLUSH);
- }
- return logWriter;
- }
-
- Configuration createConfiguration() {
- Configuration conf = new Configuration();
- for (Map.Entry<String, String> entry : configProps.entrySet()) {
- String key = entry.getKey();
- String value = entry.getValue();
- // for ease of install config file may contain
properties with empty value, skip those
- if (StringUtils.isNotEmpty(value)) {
- conf.set(key, value);
- }
- logger.info("Adding property to HDFS config: " + key +
" => " + value);
- }
-
- logger.info("Returning HDFS Filesystem Config: " +
conf.toString());
- return conf;
- }
-
- private void createParents(Path pathLogfile, FileSystem fileSystem)
- throws Exception {
- logger.info("Creating parent folder for " + pathLogfile);
- Path parentPath = pathLogfile != null ? pathLogfile.getParent()
: null;
-
- if (parentPath != null && fileSystem != null
- && !fileSystem.exists(parentPath)) {
- fileSystem.mkdirs(parentPath);
- }
- }
-
- private void closeFileIfNeeded() throws FileNotFoundException,
IOException {
- if (logWriter == null) {
- return;
- }
-
- if ( System.currentTimeMillis() > nextRollOverTime.getTime() ) {
- logger.info("Closing file. Rolling over. name=" +
getName()
- + ", fileName=" + currentFileName);
- try {
- logWriter.flush();
- logWriter.close();
- } catch (Throwable t) {
- logger.error("Error on closing log writter.
Exception will be ignored. name="
- + getName() + ", fileName=" +
currentFileName);
- }
-
- logWriter = null;
- ostream = null;
- currentFileName = null;
-
- if (!rollOverByDuration) {
- try {
- if(StringUtils.isEmpty(rolloverPeriod)
) {
- rolloverPeriod =
rollingTimeUtil.convertRolloverSecondsToRolloverPeriod(fileRolloverSec);
- }
- nextRollOverTime =
rollingTimeUtil.computeNextRollingTime(rolloverPeriod);
- } catch ( Exception e) {
- logger.warn("Rollover by
file.rollover.period failed...will be using the file.rollover.sec for hdfs
audit file rollover...",e);
- nextRollOverTime = rollOverByDuration();
- }
- } else {
- nextRollOverTime = rollOverByDuration();
- }
- }
- }
-
- private void hflush() {
- if (ostream != null) {
- try {
- synchronized (this) {
- if (ostream != null)
- // 1) PrinterWriter does not
have bufferring of its own so
- // we need to flush its
underlying stream
- // 2) HDFS flush() does not
really flush all the way to disk.
- if (isHFlushCapableStream) {
- //Checking HFLUSH
capability of the stream because of HADOOP-13327.
- //For S3 filesystem,
hflush throws UnsupportedOperationException and hence we call flush.
- ostream.hflush();
- } else {
- ostream.flush();
- }
- logger.info("Flush HDFS audit logs
completed.....");
- }
- } catch (IOException e) {
- logger.error("Error on flushing log writer: " +
e.getMessage() +
- "\nException will be ignored.
name=" + getName() + ", fileName=" + currentFileName);
- }
- }
- }
-
- private Date rollOverByDuration() {
- long rollOverTime =
rollingTimeUtil.computeNextRollingTime(fileRolloverSec,nextRollOverTime);
- return new Date(rollOverTime);
+ public RangerAuditWriter getWriter() throws Exception {
+ AuditWriterFactory auditWriterFactory =
AuditWriterFactory.getInstance();
+ auditWriterFactory.init(props, propPrefix, auditProviderName,
auditConfigs);
+ return auditWriterFactory.getAuditWriter();
}
}
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditHandler.java
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditHandler.java
index 4ce31dd..dd02255 100644
---
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditHandler.java
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditHandler.java
@@ -18,6 +18,7 @@
package org.apache.ranger.audit.provider;
+import java.io.File;
import java.util.Collection;
import java.util.Properties;
@@ -28,7 +29,8 @@ public interface AuditHandler {
boolean log(Collection<AuditEventBase> events);
boolean logJSON(String event);
- boolean logJSON(Collection<String> events);
+ boolean logJSON(Collection<String> events);
+ boolean logFile(File file);
void init(Properties prop);
void init(Properties prop, String basePropertyName);
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditProviderFactory.java
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditProviderFactory.java
index 6b7f4b0..7a3c7f6 100644
---
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditProviderFactory.java
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditProviderFactory.java
@@ -35,6 +35,7 @@ import
org.apache.ranger.audit.provider.kafka.KafkaAuditProvider;
import org.apache.ranger.audit.provider.solr.SolrAuditProvider;
import org.apache.ranger.audit.queue.AuditAsyncQueue;
import org.apache.ranger.audit.queue.AuditBatchQueue;
+import org.apache.ranger.audit.queue.AuditFileQueue;
import org.apache.ranger.audit.queue.AuditQueue;
import org.apache.ranger.audit.queue.AuditSummaryQueue;
@@ -58,6 +59,8 @@ public class AuditProviderFactory {
public static final String AUDIT_DEST_BASE =
"xasecure.audit.destination";
public static final String AUDIT_SHUTDOWN_HOOK_MAX_WAIT_SEC =
"xasecure.audit.shutdown.hook.max.wait.seconds";
public static final String AUDIT_IS_FILE_CACHE_PROVIDER_ENABLE_PROP =
"xasecure.audit.provider.filecache.is.enabled";
+ public static final String FILE_QUEUE_TYPE = "filequeue";
+ public static final String DEFAULT_QUEUE_TYPE = "memoryqueue";
public static final int AUDIT_SHUTDOWN_HOOK_MAX_WAIT_SEC_DEFAULT = 30;
public static final int AUDIT_ASYNC_MAX_QUEUE_SIZE_DEFAULT = 10 * 1024;
@@ -423,7 +426,7 @@ public class AuditProviderFactory {
} else if (providerName.equalsIgnoreCase("log4j")) {
provider = new Log4JAuditDestination();
} else if (providerName.equalsIgnoreCase("batch")) {
- provider = new AuditBatchQueue(consumer);
+ provider = getAuditProvider(props, propPrefix,
consumer);
} else if (providerName.equalsIgnoreCase("async")) {
provider = new AuditAsyncQueue(consumer);
} else {
@@ -441,6 +444,30 @@ public class AuditProviderFactory {
return provider;
}
+ private AuditHandler getAuditProvider(Properties props, String
propPrefix, AuditHandler consumer) {
+ AuditHandler ret = null;
+ String queueType = MiscUtil.getStringProperty(props,
propPrefix + "." + "queuetype", DEFAULT_QUEUE_TYPE);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("==> AuditProviderFactory.getAuditProvider()
propPerfix= " + propPrefix + ", " + " queueType= " + queueType);
+ }
+
+ if (FILE_QUEUE_TYPE.equalsIgnoreCase(queueType)) {
+ AuditFileQueue auditFileQueue = new
AuditFileQueue(consumer);
+ String propPrefixFileQueue = propPrefix + "." +
FILE_QUEUE_TYPE;
+ auditFileQueue.init(props, propPrefixFileQueue);
+ ret = new AuditBatchQueue(auditFileQueue);
+ } else {
+ ret = new AuditBatchQueue(consumer);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("<==
AuditProviderFactory.getAuditProvider()");
+ }
+
+ return ret;
+ }
+
private AuditHandler getDefaultProvider() {
return new DummyAuditProvider();
}
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditWriterFactory.java
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditWriterFactory.java
new file mode 100644
index 0000000..6ef2255
--- /dev/null
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditWriterFactory.java
@@ -0,0 +1,117 @@
+package org.apache.ranger.audit.provider;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.audit.utils.RangerAuditWriter;
+
+import java.util.Map;
+import java.util.Properties;
+
+public class AuditWriterFactory {
+ private static final Log logger =
LogFactory.getLog(AuditWriterFactory.class);
+ public static final String AUDIT_FILETYPE_DEFAULT = "json";
+ public static final String AUDIT_JSON_FILEWRITER_IMPL =
"org.apache.ranger.audit.utils.RangerJSONAuditWriter";
+ public static final String AUDIT_ORC_FILEWRITER_IMPL =
"org.apache.ranger.audit.utils.RangerORCAuditWriter";
+
+ public Map<String,String> auditConfigs = null;
+ public Properties props = null;
+ public String propPrefix = null;
+ public String auditProviderName = null;
+ public RangerAuditWriter auditWriter = null;
+ private static volatile AuditWriterFactory me = null;
+
+ public static AuditWriterFactory getInstance() {
+ AuditWriterFactory auditWriter = me;
+ if (auditWriter == null) {
+ synchronized (AuditWriterFactory.class) {
+ auditWriter = me;
+ if (auditWriter == null) {
+ me = auditWriter = new AuditWriterFactory();
+ }
+ }
+ }
+ return auditWriter;
+ }
+
+ public void init(Properties props, String propPrefix, String
auditProviderName, Map<String,String> auditConfigs) throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AuditWriterFactory.init()");
+ }
+ this.props = props;
+ this.propPrefix = propPrefix;
+ this.auditProviderName = auditProviderName;
+ this.auditConfigs = auditConfigs;
+ String auditFileType = MiscUtil.getStringProperty(props, propPrefix
+ ".filetype", AUDIT_FILETYPE_DEFAULT);
+ String writerClass = MiscUtil.getStringProperty(props, propPrefix
+ ".filewriter.impl");
+
+ auditWriter = StringUtils.isEmpty(writerClass) ?
createWriter(getDefaultWriter(auditFileType)) : createWriter(writerClass);
+
+ if (auditWriter != null) {
+ auditWriter.init(props, propPrefix, auditProviderName,
auditConfigs);
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AuditWriterFactory.init() :" +
auditWriter.getClass().getName());
+ }
+ }
+
+ public RangerAuditWriter createWriter(String writerClass) throws
Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AuditWriterFactory.createWriter()");
+ }
+ RangerAuditWriter ret = null;
+ try {
+ Class<RangerAuditWriter> cls = (Class<RangerAuditWriter>)
Class.forName(writerClass);
+ ret = cls.newInstance();
+ } catch (Exception e) {
+ throw e;
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AuditWriterFactory.createWriter()");
+ }
+ return ret;
+ }
+
+ public String getDefaultWriter(String auditFileType) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AuditWriterFactory.getDefaultWriter()");
+ }
+ String ret = null;
+ switch (auditFileType) {
+ case "orc":
+ ret = AUDIT_ORC_FILEWRITER_IMPL;
+ break;
+ case "json":
+ ret = AUDIT_JSON_FILEWRITER_IMPL;
+ break;
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AuditWriterFactory.getDefaultWriter() :" + ret);
+ }
+ return ret;
+ }
+
+ public RangerAuditWriter getAuditWriter(){
+ return this.auditWriter;
+ }
+}
\ No newline at end of file
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/BaseAuditHandler.java
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/BaseAuditHandler.java
index 54f3764..1987856 100644
---
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/BaseAuditHandler.java
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/BaseAuditHandler.java
@@ -25,6 +25,8 @@ import org.apache.ranger.audit.model.AuthzAuditEvent;
import com.google.gson.GsonBuilder;
+//import java.io.File;
+import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
@@ -191,6 +193,11 @@ public abstract class BaseAuditHandler implements
AuditHandler {
return log(eventList);
}
+ @Override
+ public boolean logFile(File file) {
+ return logFile(file);
+ }
+
public String getParentPath() {
return parentPath;
}
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/DummyAuditProvider.java
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/DummyAuditProvider.java
index 05f882f..cbd25ab 100644
---
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/DummyAuditProvider.java
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/DummyAuditProvider.java
@@ -17,6 +17,7 @@
*/
package org.apache.ranger.audit.provider;
+import java.io.File;
import java.util.Collection;
import java.util.Properties;
@@ -103,4 +104,12 @@ public class DummyAuditProvider implements AuditHandler {
return this.getClass().getName();
}
+ /* (non-Javadoc)
+ * @see org.apache.ranger.audit.provider.AuditProvider#getAuditFileType()
+ */
+ @Override
+ public boolean logFile(File file) {
+ return logFile(file);
+ }
+
}
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java
index e2b7448..f58b813 100644
--- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java
+++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java
@@ -339,6 +339,19 @@ public class MiscUtil {
return ret;
}
+ public static String getStringProperty(Properties props, String
propName, String defValue) {
+ String ret = defValue;
+
+ if (props != null && propName != null) {
+ String val = props.getProperty(propName);
+ if (val != null) {
+ ret = val;
+ }
+ }
+
+ return ret;
+ }
+
public static boolean getBooleanProperty(Properties props, String
propName,
boolean defValue) {
boolean ret = defValue;
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MultiDestAuditProvider.java
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MultiDestAuditProvider.java
index 282f5ab..b829ac5 100644
---
a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MultiDestAuditProvider.java
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MultiDestAuditProvider.java
@@ -17,6 +17,7 @@
*/
package org.apache.ranger.audit.provider;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -155,6 +156,19 @@ public class MultiDestAuditProvider extends
BaseAuditHandler {
return true;
}
+
+ @Override
+ public boolean logFile(File file) {
+ for (AuditHandler provider : mProviders) {
+ try {
+ provider.logFile(file);
+ } catch (Throwable excp) {
+ logFailedEventJSON(file.getAbsolutePath(), excp);
+ }
+ }
+ return true;
+ }
+
@Override
public void start() {
for (AuditHandler provider : mProviders) {
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java
b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java
index 41513ba..f6951c6 100644
---
a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java
@@ -56,10 +56,10 @@ public class AuditFileCacheProviderSpool implements
Runnable {
public static final String PROP_FILE_SPOOL_FILE_ROLLOVER
= "filespool.file.rollover.sec";
public static final String PROP_FILE_SPOOL_INDEX_FILE
= "filespool.index.filename";
public static final String PROP_FILE_SPOOL_DEST_RETRY_MS
= "filespool.destination.retry.ms";
+ public static final String PROP_FILE_SPOOL_BATCH_SIZE =
"filespool.buffer.size";
public static final String AUDIT_IS_FILE_CACHE_PROVIDER_ENABLE_PROP =
"xasecure.audit.provider.filecache.is.enabled";
public static final String FILE_CACHE_PROVIDER_NAME
= "AuditFileCacheProviderSpool";
- public static final int AUDIT_BATCH_SIZE_DEFAULT
= 1000;
AuditHandler consumerProvider = null;
@@ -79,6 +79,7 @@ public class AuditFileCacheProviderSpool implements Runnable {
int fileRolloverSec = 24 * 60 * 60; // In seconds
int maxArchiveFiles = 100;
int errorLogIntervalMS = 30 * 1000; // Every 30 seconds
+ int auditBatchSize = 1000;
long lastErrorLogMS = 0;
boolean isAuditFileCacheProviderEnabled = false;
boolean closeFile = false;
@@ -275,6 +276,10 @@ public class AuditFileCacheProviderSpool implements
Runnable {
+ FILE_CACHE_PROVIDER_NAME, t);
return false;
}
+
+ auditBatchSize = MiscUtil.getIntProperty(props, propPrefix
+ + "." + PROP_FILE_SPOOL_BATCH_SIZE, auditBatchSize);
+
initDone = true;
logger.debug("<== AuditFileCacheProviderSpool.init()");
@@ -824,7 +829,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
AuditEventBase event = MiscUtil.fromJson(line,
AuthzAuditEvent.class);
events.add(event);
- if (events.size() == AUDIT_BATCH_SIZE_DEFAULT) {
+ if (events.size() == auditBatchSize) {
boolean ret = sendEvent(events,
currentConsumerIndexRecord, currLine);
if (!ret) {
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueue.java
b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueue.java
new file mode 100644
index 0000000..0b245c0
--- /dev/null
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueue.java
@@ -0,0 +1,126 @@
+/*
+ * 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.ranger.audit.queue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.audit.model.AuditEventBase;
+import org.apache.ranger.audit.provider.AuditHandler;
+import org.apache.ranger.audit.provider.BaseAuditHandler;
+
+import java.util.Collection;
+import java.util.Properties;
+
+/*
+ AuditFileQueue class does the work of stashing the audit logs into Local
Filesystem before sending it to the AuditBatchQueue Consumer
+*/
+
+public class AuditFileQueue extends BaseAuditHandler {
+ private static final Log logger =
LogFactory.getLog(AuditFileQueue.class);
+
+ AuditFileQueueSpool fileSpooler = null;
+ AuditHandler consumer = null;
+
+ static final String DEFAULT_NAME = "batch";
+
+ public AuditFileQueue(AuditHandler consumer) {
+ this.consumer = consumer;
+ }
+
+ public void init(Properties prop, String basePropertyName) {
+ String propPrefix = "xasecure.audit.batch";
+ if (basePropertyName != null) {
+ propPrefix = basePropertyName;
+ }
+ super.init(prop, propPrefix);
+
+ //init AuditFileQueueSpooler thread to send Local logs to destination
+ fileSpooler = new AuditFileQueueSpool(consumer);
+ fileSpooler.init(prop,propPrefix);
+ }
+
+ @Override
+ public boolean log(AuditEventBase event) {
+ boolean ret = false;
+ if ( event != null) {
+ fileSpooler.stashLogs(event);
+ if (fileSpooler.isSpoolingSuccessful()) {
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean log(Collection<AuditEventBase> events) {
+ boolean ret = true;
+ if ( events != null) {
+ for (AuditEventBase event : events) {
+ ret = log(event);
+ }
+ }
+ return ret;
+ }
+
+
+ @Override
+ public void start() {
+ // Start the consumer thread
+ if (consumer != null) {
+ consumer.start();
+ }
+ if (fileSpooler != null) {
+ // start AuditFileSpool thread
+ fileSpooler.start();
+ }
+ }
+
+ @Override
+ public void stop() {
+ logger.info("Stop called. name=" + getName());
+ if (consumer != null) {
+ consumer.stop();
+ }
+ }
+
+ @Override
+ public void waitToComplete() {
+ logger.info("waitToComplete called. name=" + getName());
+ if ( consumer != null) {
+ consumer.waitToComplete();
+ }
+ }
+
+ @Override
+ public void waitToComplete(long timeout) {
+ logger.info("waitToComplete called. name=" + getName());
+ if ( consumer != null) {
+ consumer.waitToComplete(timeout);
+ }
+ }
+
+ @Override
+ public void flush() {
+ logger.info("waitToComplete. name=" + getName());
+ if ( consumer != null) {
+ consumer.flush();
+ }
+ }
+
+}
\ No newline at end of file
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java
b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueueSpool.java
similarity index 76%
copy from
agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java
copy to
agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueueSpool.java
index 41513ba..3284783 100644
---
a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueueSpool.java
@@ -1,21 +1,21 @@
/*
- * 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.
- */
+* 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.ranger.audit.queue;
@@ -25,12 +25,28 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.MDC;
import org.apache.ranger.audit.model.AuditEventBase;
-import org.apache.ranger.audit.provider.AuditHandler;
import org.apache.ranger.audit.model.AuthzAuditEvent;
+import org.apache.ranger.audit.provider.AuditHandler;
import org.apache.ranger.audit.provider.MiscUtil;
-import java.io.*;
-import java.util.*;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -38,33 +54,31 @@ import java.util.concurrent.TimeUnit;
/**
* This class temporarily stores logs in Local file system before it
despatches each logs in file to the AuditBatchQueue Consumer.
* This gets instantiated only when AuditFileCacheProvider is enabled
(xasecure.audit.provider.filecache.is.enabled).
- * When AuditFileCacheProvider is all the logs are stored in local file system
before sent to destination.
+ * When AuditFileCacheProvider is enabled all the logs are stored in local
file system before sent to destination.
*/
-public class AuditFileCacheProviderSpool implements Runnable {
- private static final Log logger =
LogFactory.getLog(AuditFileCacheProviderSpool.class);
+public class AuditFileQueueSpool implements Runnable {
+ private static final Log logger =
LogFactory.getLog(AuditFileQueueSpool.class);
public enum SPOOL_FILE_STATUS {
pending, write_inprogress, read_inprogress, done
}
- public static final String PROP_FILE_SPOOL_LOCAL_DIR
= "filespool.dir";
- public static final String PROP_FILE_SPOOL_LOCAL_FILE_NAME
= "filespool.filename.format";
- public static final String PROP_FILE_SPOOL_ARCHIVE_DIR
= "filespool.archive.dir";
- public static final String PROP_FILE_SPOOL_ARCHIVE_MAX_FILES_COUNT =
"filespool.archive.max.files";
- public static final String PROP_FILE_SPOOL_FILENAME_PREFIX
= "filespool.file.prefix";
- public static final String PROP_FILE_SPOOL_FILE_ROLLOVER
= "filespool.file.rollover.sec";
- public static final String PROP_FILE_SPOOL_INDEX_FILE
= "filespool.index.filename";
- public static final String PROP_FILE_SPOOL_DEST_RETRY_MS
= "filespool.destination.retry.ms";
-
- public static final String AUDIT_IS_FILE_CACHE_PROVIDER_ENABLE_PROP =
"xasecure.audit.provider.filecache.is.enabled";
- public static final String FILE_CACHE_PROVIDER_NAME
= "AuditFileCacheProviderSpool";
- public static final int AUDIT_BATCH_SIZE_DEFAULT
= 1000;
-
- AuditHandler consumerProvider = null;
-
- BlockingQueue<AuditIndexRecord> indexQueue = new
LinkedBlockingQueue<AuditIndexRecord>();
- List<AuditIndexRecord> indexRecords = new
ArrayList<AuditIndexRecord>();
+ public static final String PROP_FILE_SPOOL_LOCAL_DIR
= "filespool.dir";
+ public static final String PROP_FILE_SPOOL_LOCAL_FILE_NAME
= "filespool.filename.format";
+ public static final String PROP_FILE_SPOOL_ARCHIVE_DIR
= "filespool.archive.dir";
+ public static final String PROP_FILE_SPOOL_ARCHIVE_MAX_FILES_COUNT =
"filespool.archive.max.files";
+ public static final String PROP_FILE_SPOOL_FILENAME_PREFIX
= "filespool.file.prefix";
+ public static final String PROP_FILE_SPOOL_FILE_ROLLOVER =
"filespool.file.rollover.sec";
+ public static final String PROP_FILE_SPOOL_INDEX_FILE
= "filespool.index.filename";
+ public static final String PROP_FILE_SPOOL_DEST_RETRY_MS =
"filespool.destination.retry.ms";
+ public static final String PROP_FILE_SPOOL_BATCH_SIZE =
"filespool.buffer.size";
+ public static final String FILE_QUEUE_PROVIDER_NAME
= "AuditFileQueueSpool";
+ public static final String DEFAULT_AUDIT_FILE_TYPE =
"json";
+
+ AuditHandler consumerProvider = null;
+ BlockingQueue<AuditIndexRecord> indexQueue = new
LinkedBlockingQueue<AuditIndexRecord>();
+ List<AuditIndexRecord> indexRecords = new
ArrayList<AuditIndexRecord>();
// Folder and File attributes
File logFolder = null;
@@ -74,6 +88,7 @@ public class AuditFileCacheProviderSpool implements Runnable {
String indexFileName = null;
File indexFile = null;
String indexDoneFileName = null;
+ String auditFileType = null;
File indexDoneFile = null;
int retryDestinationMS = 30 * 1000; // Default 30 seconds
int fileRolloverSec = 24 * 60 * 60; // In seconds
@@ -84,6 +99,7 @@ public class AuditFileCacheProviderSpool implements Runnable {
boolean closeFile = false;
boolean isPending = false;
long lastAttemptTime = 0;
+ long bufferSize = 1000;
boolean initDone = false;
PrintWriter logWriter = null;
@@ -100,7 +116,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
private Gson gson = null;
- public AuditFileCacheProviderSpool(AuditHandler consumerProvider) {
+ public AuditFileQueueSpool(AuditHandler consumerProvider) {
this.consumerProvider = consumerProvider;
}
@@ -109,7 +125,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
}
public boolean init(Properties props, String basePropertyName) {
- logger.debug("==> AuditFileCacheProviderSpool.init()");
+ logger.debug("==> AuditFileQueueSpool.init()");
if (initDone) {
logger.error("init() called more than once. queueProvider="
@@ -142,20 +158,19 @@ public class AuditFileCacheProviderSpool implements
Runnable {
+ PROP_FILE_SPOOL_FILE_ROLLOVER, fileRolloverSec);
maxArchiveFiles = MiscUtil.getIntProperty(props, propPrefix + "."
+ PROP_FILE_SPOOL_ARCHIVE_MAX_FILES_COUNT,
maxArchiveFiles);
- isAuditFileCacheProviderEnabled =
MiscUtil.getBooleanProperty(props, AUDIT_IS_FILE_CACHE_PROVIDER_ENABLE_PROP,
false);
logger.info("retryDestinationMS=" + retryDestinationMS
- + ", queueName=" + FILE_CACHE_PROVIDER_NAME);
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME);
logger.info("fileRolloverSec=" + fileRolloverSec + ", queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
logger.info("maxArchiveFiles=" + maxArchiveFiles + ", queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
if (logFolderProp == null || logFolderProp.isEmpty()) {
logger.fatal("Audit spool folder is not configured. Please set
"
+ propPrefix
+ "."
+ PROP_FILE_SPOOL_LOCAL_DIR
- + ". queueName=" + FILE_CACHE_PROVIDER_NAME);
+ + ". queueName=" + FILE_QUEUE_PROVIDER_NAME);
return false;
}
logFolder = new File(logFolderProp);
@@ -165,19 +180,19 @@ public class AuditFileCacheProviderSpool implements
Runnable {
logger.fatal("File Spool folder not found and can't be
created. folder="
+ logFolder.getAbsolutePath()
+ ", queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
return false;
}
}
logger.info("logFolder=" + logFolder + ", queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
if (logFileNameFormat == null || logFileNameFormat.isEmpty()) {
logFileNameFormat = "spool_" + "%app-type%" + "_"
+ "%time:yyyyMMdd-HHmm.ss%.log";
}
logger.info("logFileNameFormat=" + logFileNameFormat
- + ", queueName=" + FILE_CACHE_PROVIDER_NAME);
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME);
if (archiveFolderProp == null || archiveFolderProp.isEmpty()) {
archiveFolder = new File(logFolder, "archive");
@@ -190,16 +205,16 @@ public class AuditFileCacheProviderSpool implements
Runnable {
logger.error("File Spool archive folder not found and
can't be created. folder="
+ archiveFolder.getAbsolutePath()
+ ", queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
return false;
}
}
logger.info("archiveFolder=" + archiveFolder + ", queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
if (indexFileName == null || indexFileName.isEmpty()) {
if (fileNamePrefix == null || fileNamePrefix.isEmpty()) {
- fileNamePrefix = FILE_CACHE_PROVIDER_NAME + "_"
+ fileNamePrefix = FILE_QUEUE_PROVIDER_NAME + "_"
+ consumerProvider.getName();
}
indexFileName = "index_" + fileNamePrefix + "_" + "%app-type%"
@@ -218,7 +233,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
}
}
logger.info("indexFile=" + indexFile + ", queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
int lastDot = indexFileName.lastIndexOf('.');
if (lastDot < 0) {
@@ -236,7 +251,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
}
}
logger.info("indexDoneFile=" + indexDoneFile + ", queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
// Load index file
loadIndexFile();
@@ -249,7 +264,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
currentWriterIndexRecord = auditIndexRecord;
logger.info("currentWriterIndexRecord="
+ currentWriterIndexRecord.filePath
- + ", queueName=" + FILE_CACHE_PROVIDER_NAME);
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME);
}
if (auditIndexRecord.status
.equals(SPOOL_FILE_STATUS.read_inprogress)) {
@@ -270,14 +285,23 @@ public class AuditFileCacheProviderSpool implements
Runnable {
}
}
+ auditFileType = MiscUtil.getStringProperty(props, propPrefix +
".filetype", DEFAULT_AUDIT_FILE_TYPE);
+ if (auditFileType == null) {
+ auditFileType = DEFAULT_AUDIT_FILE_TYPE;
+ }
+
} catch (Throwable t) {
logger.fatal("Error initializing File Spooler. queue="
- + FILE_CACHE_PROVIDER_NAME, t);
+ + FILE_QUEUE_PROVIDER_NAME, t);
return false;
}
+
+ bufferSize = MiscUtil.getLongProperty(props, propPrefix
+ + "." + PROP_FILE_SPOOL_BATCH_SIZE, bufferSize);
+
initDone = true;
- logger.debug("<== AuditFileCacheProviderSpool.init()");
+ logger.debug("<== AuditFileQueueSpool.init()");
return true;
}
@@ -287,16 +311,16 @@ public class AuditFileCacheProviderSpool implements
Runnable {
public void start() {
if (!initDone) {
logger.error("Cannot start Audit File Spooler. Initilization not
done yet. queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
return;
}
logger.info("Starting writerThread, queueName="
- + FILE_CACHE_PROVIDER_NAME + ", consumer="
+ + FILE_QUEUE_PROVIDER_NAME + ", consumer="
+ consumerProvider.getName());
// Let's start the thread to read
- destinationThread = new Thread(this, FILE_CACHE_PROVIDER_NAME + "_"
+ destinationThread = new Thread(this, FILE_QUEUE_PROVIDER_NAME + "_"
+ consumerProvider.getName() + "_destWriter");
destinationThread.setDaemon(true);
destinationThread.start();
@@ -305,10 +329,10 @@ public class AuditFileCacheProviderSpool implements
Runnable {
public void stop() {
if (!initDone) {
logger.error("Cannot stop Audit File Spooler. Initilization not
done. queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
return;
}
- logger.info("Stop called, queueName=" + FILE_CACHE_PROVIDER_NAME
+ logger.info("Stop called, queueName=" + FILE_QUEUE_PROVIDER_NAME
+ ", consumer=" + consumerProvider.getName());
isDrain = true;
@@ -329,7 +353,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
}
try {
logger.info("Closing open file, queueName="
- + FILE_CACHE_PROVIDER_NAME + ", consumer="
+ + FILE_QUEUE_PROVIDER_NAME + ", consumer="
+ consumerProvider.getName());
out.flush();
@@ -353,7 +377,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
public void flush() {
if (!initDone) {
logger.error("Cannot flush Audit File Spooler. Initilization not
done. queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
return;
}
PrintWriter out = getOpenLogFileStream();
@@ -371,7 +395,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
public boolean isPending() {
if (!initDone) {
logError("isPending(): File Spooler not initialized. queueName="
- + FILE_CACHE_PROVIDER_NAME);
+ + FILE_QUEUE_PROVIDER_NAME);
return false;
}
@@ -494,7 +518,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
}
fileName = newFileName;
logger.info("Creating new file. queueName="
- + FILE_CACHE_PROVIDER_NAME + ", fileName=" + fileName);
+ + FILE_QUEUE_PROVIDER_NAME + ", fileName=" + fileName);
// Open the file
logWriter = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(new FileOutputStream(
outLogFile),"UTF-8")));
@@ -515,7 +539,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
// This means the process just started. We need to open the
file
// in append mode.
logger.info("Opening existing file for append. queueName="
- + FILE_CACHE_PROVIDER_NAME + ", fileName="
+ + FILE_QUEUE_PROVIDER_NAME + ", fileName="
+ currentWriterIndexRecord.filePath);
logWriter = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(new FileOutputStream(
currentWriterIndexRecord.filePath, true),"UTF-8")));
@@ -544,7 +568,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
currentWriterIndexRecord.writeCompleteTime = new Date();
saveIndexFile();
logger.info("Adding file to queue. queueName="
- + FILE_CACHE_PROVIDER_NAME + ", fileName="
+ + FILE_QUEUE_PROVIDER_NAME + ", fileName="
+ currentWriterIndexRecord.filePath);
indexQueue.add(currentWriterIndexRecord);
currentWriterIndexRecord = null;
@@ -557,7 +581,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
- currentWriterIndexRecord.fileCreateTime.getTime() >
fileRolloverSec * 1000) {
closeFile = true;
logger.info("Closing file. Rolling over. queueName="
- + FILE_CACHE_PROVIDER_NAME + ", fileName="
+ + FILE_QUEUE_PROVIDER_NAME + ", fileName="
+ currentWriterIndexRecord.filePath);
}
}
@@ -571,7 +595,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
logger.info("Loading index file. fileName=" + indexFile.getPath());
BufferedReader br = null;
try {
- br = new BufferedReader(new InputStreamReader(new
FileInputStream(indexFile), "UTF-8"));
+ br = new BufferedReader(new InputStreamReader(new
FileInputStream(indexFile), "UTF-8"));
indexRecords.clear();
String line;
while ((line = br.readLine()) != null) {
@@ -606,7 +630,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
AuditIndexRecord record = iter.next();
if (record.id.equals(indexRecord.id)) {
logger.info("Removing file from index. file=" + record.filePath
- + ", queueName=" + FILE_CACHE_PROVIDER_NAME
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME
+ ", consumer=" + consumerProvider.getName());
iter.remove();
@@ -634,7 +658,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
void appendToDoneFile(AuditIndexRecord indexRecord)
throws FileNotFoundException, IOException {
logger.info("Moving to done file. " + indexRecord.filePath
- + ", queueName=" + FILE_CACHE_PROVIDER_NAME + ", consumer="
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME + ", consumer="
+ consumerProvider.getName());
String line = gson.toJson(indexRecord);
PrintWriter out = new PrintWriter(new BufferedWriter(new
OutputStreamWriter(new FileOutputStream(
@@ -778,7 +802,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
logger.info("Destination is down. sleeping for "
+ retryDestinationMS
+ " milli seconds. indexQueue=" + indexQueue.size()
- + ", queueName=" + FILE_CACHE_PROVIDER_NAME
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME
+ ", consumer=" + consumerProvider.getName());
Thread.sleep(retryDestinationMS);
}
@@ -812,38 +836,19 @@ public class AuditFileCacheProviderSpool implements
Runnable {
BufferedReader br = new BufferedReader(new
InputStreamReader(new FileInputStream(
currentConsumerIndexRecord.filePath),"UTF-8"));
try {
- int startLine =
currentConsumerIndexRecord.linePosition;
- String line;
- int currLine = 0;
- List<AuditEventBase> events = new ArrayList<>();
- while ((line = br.readLine()) != null) {
- currLine++;
- if (currLine < startLine) {
- continue;
- }
- AuditEventBase event = MiscUtil.fromJson(line,
AuthzAuditEvent.class);
- events.add(event);
-
- if (events.size() == AUDIT_BATCH_SIZE_DEFAULT) {
- boolean ret = sendEvent(events,
- currentConsumerIndexRecord, currLine);
- if (!ret) {
- throw new Exception("Destination down");
- }
- events.clear();
- }
- }
- if (events.size() > 0) {
- boolean ret = sendEvent(events,
- currentConsumerIndexRecord, currLine);
- if (!ret) {
- throw new Exception("Destination down");
- }
- events.clear();
+ if
(auditFileType.equalsIgnoreCase(DEFAULT_AUDIT_FILE_TYPE)) {
+ // if Audit File format is JSON each audit file in
the Local Spool Location will be copied
+ // to HDFS location as JSON
+ File srcFile = new
File(currentConsumerIndexRecord.filePath);
+ logFile(srcFile);
+ } else {
+ // If Audit File format is ORC, each records in
audit files in the Local Spool Location will be
+ // read and converted into ORC format and pushed
into an ORC file.
+ logEvent(br);
}
logger.info("Done reading file. file="
+ currentConsumerIndexRecord.filePath
- + ", queueName=" + FILE_CACHE_PROVIDER_NAME
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME
+ ", consumer=" + consumerProvider.getName());
// The entire file is read
currentConsumerIndexRecord.status =
SPOOL_FILE_STATUS.done;
@@ -854,7 +859,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
} catch (Exception ex) {
isDestDown = true;
logError("Destination down. queueName="
- + FILE_CACHE_PROVIDER_NAME + ", consumer="
+ + FILE_QUEUE_PROVIDER_NAME + ", consumer="
+ consumerProvider.getName());
lastAttemptTime = System.currentTimeMillis();
// Update the index file
@@ -878,10 +883,42 @@ public class AuditFileCacheProviderSpool implements
Runnable {
logger.error("Exception in destination writing thread.", t);
}
}
- logger.info("Exiting file spooler. provider=" +
FILE_CACHE_PROVIDER_NAME
+ logger.info("Exiting file spooler. provider=" +
FILE_QUEUE_PROVIDER_NAME
+ ", consumer=" + consumerProvider.getName());
}
+ private void logEvent(BufferedReader br) throws Exception {
+ String line;
+ int currLine = 0;
+ int startLine = currentConsumerIndexRecord.linePosition;
+ List<AuditEventBase> events = new ArrayList<>();
+ while ((line = br.readLine()) != null) {
+ currLine++;
+ if (currLine < startLine) {
+ continue;
+ }
+ AuditEventBase event = MiscUtil.fromJson(line,
AuthzAuditEvent.class);
+ events.add(event);
+
+ if (events.size() == bufferSize) {
+ boolean ret = sendEvent(events,
+ currentConsumerIndexRecord, currLine);
+ if (!ret) {
+ throw new Exception("Destination down");
+ }
+ events.clear();
+ }
+ }
+ if (events.size() > 0) {
+ boolean ret = sendEvent(events,
+ currentConsumerIndexRecord, currLine);
+ if (!ret) {
+ throw new Exception("Destination down");
+ }
+ events.clear();
+ }
+ }
+
private boolean sendEvent(List<AuditEventBase> events, AuditIndexRecord
indexRecord,
int currLine) {
boolean ret = true;
@@ -890,7 +927,7 @@ public class AuditFileCacheProviderSpool implements
Runnable {
if (!ret) {
// Need to log error after fixed interval
logError("Error sending logs to consumer. provider="
- + FILE_CACHE_PROVIDER_NAME + ", consumer="
+ + FILE_QUEUE_PROVIDER_NAME + ", consumer="
+ consumerProvider.getName());
} else {
// Update index and save
@@ -903,17 +940,78 @@ public class AuditFileCacheProviderSpool implements
Runnable {
if (isDestDown) {
isDestDown = false;
logger.info("Destination up now. " + indexRecord.filePath
- + ", queueName=" + FILE_CACHE_PROVIDER_NAME
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME
+ ", consumer=" + consumerProvider.getName());
}
}
} catch (Throwable t) {
logger.error("Error while sending logs to consumer. provider="
- + FILE_CACHE_PROVIDER_NAME + ", consumer="
+ + FILE_QUEUE_PROVIDER_NAME + ", consumer="
+ consumerProvider.getName() + ", log=" + events, t);
}
return ret;
}
-}
+ private void logFile(File file) throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AuditFileQueueSpool.logFile()");
+ }
+ int currLine = 0;
+ int startLine = currentConsumerIndexRecord.linePosition;
+
+ if (currLine < startLine) {
+ currLine++;
+ }
+
+ boolean ret = sendFile(file,currentConsumerIndexRecord, currLine);
+ if (!ret) {
+ throw new Exception("Destination down");
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AuditFileQueueSpool.logFile()");
+ }
+ }
+
+ private boolean sendFile(File file, AuditIndexRecord indexRecord,
+ int currLine) {
+ boolean ret = true;
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AuditFileQueueSpool.sendFile()");
+ }
+
+ try {
+ ret = consumerProvider.logFile(file);
+ if (!ret) {
+ // Need to log error after fixed interval
+ logError("Error sending log file to consumer. provider="
+ + FILE_QUEUE_PROVIDER_NAME + ", consumer="
+ + consumerProvider.getName()+ ", logFile=" +
file.getName());
+ } else {
+ // Update index and save
+ indexRecord.linePosition = currLine;
+ indexRecord.status = SPOOL_FILE_STATUS.read_inprogress;
+ indexRecord.lastSuccessTime = new Date();
+ indexRecord.lastAttempt = true;
+ saveIndexFile();
+
+ if (isDestDown) {
+ isDestDown = false;
+ logger.info("Destination up now. " + indexRecord.filePath
+ + ", queueName=" + FILE_QUEUE_PROVIDER_NAME
+ + ", consumer=" + consumerProvider.getName());
+ }
+ }
+ } catch (Throwable t) {
+ logger.error("Error sending log file to consumer. provider="
+ + FILE_QUEUE_PROVIDER_NAME + ", consumer="
+ + consumerProvider.getName() + ", logFile=" +
file.getName(), t);
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AuditFileQueueSpool.sendFile() " + ret );
+ }
+ return ret;
+ }
+}
\ No newline at end of file
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractRangerAuditWriter.java
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractRangerAuditWriter.java
new file mode 100644
index 0000000..b5967e4
--- /dev/null
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractRangerAuditWriter.java
@@ -0,0 +1,353 @@
+package org.apache.ranger.audit.utils;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.*;
+import org.apache.ranger.audit.provider.MiscUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.util.Date;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * This is Abstract class to have common properties of Ranger Audit HDFS
Destination Writer.
+ */
+public abstract class AbstractRangerAuditWriter implements RangerAuditWriter {
+ private static final Log logger =
LogFactory.getLog(AbstractRangerAuditWriter.class);
+
+ public static final String PROP_FILESYSTEM_DIR = "dir";
+ public static final String PROP_FILESYSTEM_SUBDIR = "subdir";
+ public static final String PROP_FILESYSTEM_FILE_NAME_FORMAT =
"filename.format";
+ public static final String PROP_FILESYSTEM_FILE_ROLLOVER =
"file.rollover.sec";
+ public static final String PROP_FILESYSTEM_ROLLOVER_PERIOD =
"file.rollover.period";
+ public static final String PROP_FILESYSTEM_FILE_EXTENSION = ".log";
+ public Configuration conf
= null;
+ public FileSystem fileSystem
= null;
+ public Map<String, String> auditConfigs
= null;
+ public Path auditPath
= null;
+ public PrintWriter logWriter = null;
+ public RollingTimeUtil rollingTimeUtil = null;
+ public String auditProviderName
= null;
+ public String fullPath
= null;
+ public String parentFolder
= null;
+ public String currentFileName
= null;
+ public String logFileNameFormat
= null;
+ public String logFolder =
null;
+ public String fileExtension
= null;
+ public String rolloverPeriod = null;
+ public String fileSystemScheme
= null;
+ public Date nextRollOverTime = null;
+ public int fileRolloverSec
= 24 * 60 * 60; // In seconds
+ public boolean rollOverByDuration = false;
+ public volatile FSDataOutputStream ostream = null;
// output stream wrapped in logWriter
+ private boolean isHFlushCapableStream = false;
+
+ @Override
+ public void init(Properties props, String propPrefix, String
auditProviderName, Map<String,String> auditConfigs) {
+ // Initialize properties for this class
+ // Initial folder and file properties
+ logger.info("==> AbstractRangerAuditWriter.init()");
+ this.auditProviderName = auditProviderName;
+ this.auditConfigs = auditConfigs;
+
+ init(props,propPrefix);
+
+ logger.info("<== AbstractRangerAuditWriter.init()");
+ }
+
+ public void createFileSystemFolders() throws Exception {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("==>
AbstractRangerAuditWriter.createFileSystemFolders()");
+ }
+ // Create a new file
+ Date currentTime = new Date();
+ String fileName = MiscUtil.replaceTokens(logFileNameFormat,
currentTime.getTime());
+ parentFolder = MiscUtil.replaceTokens(logFolder,
currentTime.getTime());
+ fullPath = parentFolder + Path.SEPARATOR + fileName;
+ String defaultPath = fullPath;
+ conf = createConfiguration();
+ URI uri = URI.create(fullPath);
+ fileSystem = FileSystem.get(uri, conf);
+ auditPath = new Path(fullPath);
+ fileSystemScheme = getFileSystemScheme();
+ logger.info("Checking whether log file exists. "+ fileSystemScheme +
"Path= " + fullPath + ", UGI=" + MiscUtil.getUGILoginUser());
+ int i = 0;
+ while (fileSystem.exists(auditPath)) {
+ i++;
+ int lastDot = defaultPath.lastIndexOf('.');
+ String baseName = defaultPath.substring(0, lastDot);
+ String extension = defaultPath.substring(lastDot);
+ fullPath = baseName + "." + i + extension;
+ auditPath = new Path(fullPath);
+ logger.info("Checking whether log file exists. "+ fileSystemScheme
+ "Path= " + fullPath);
+ }
+ logger.info("Log file doesn't exists. Will create and use it. "+
fileSystemScheme + "Path= " + fullPath);
+
+ // Create parent folders
+ createParents(auditPath, fileSystem);
+
+ currentFileName = fullPath;
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<==
AbstractRangerAuditWriter.createFileSystemFolders()");
+ }
+ }
+
+ public Configuration createConfiguration() {
+ Configuration conf = new Configuration();
+ for (Map.Entry<String, String> entry : auditConfigs.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ // for ease of install config file may contain properties with
empty value, skip those
+ if (StringUtils.isNotEmpty(value)) {
+ conf.set(key, value);
+ }
+ logger.info("Adding property to "+ fileSystemScheme + " + config:
" + key + " => " + value);
+ }
+
+ logger.info("Returning " + fileSystemScheme + "Filesystem Config: " +
conf.toString());
+ return conf;
+ }
+
+ public void createParents(Path pathLogfile, FileSystem fileSystem)
+ throws Exception {
+ logger.info("Creating parent folder for " + pathLogfile);
+ Path parentPath = pathLogfile != null ? pathLogfile.getParent() : null;
+
+ if (parentPath != null && fileSystem != null
+ && !fileSystem.exists(parentPath)) {
+ fileSystem.mkdirs(parentPath);
+ }
+ }
+
+ public void init(Properties props, String propPrefix) {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AbstractRangerAuditWriter.init()");
+ }
+
+ String logFolderProp = MiscUtil.getStringProperty(props, propPrefix
+ "." + PROP_FILESYSTEM_DIR);
+ if (StringUtils.isEmpty(logFolderProp)) {
+ logger.fatal("File destination folder is not configured. Please
set "
+ + propPrefix + "."
+ + PROP_FILESYSTEM_DIR + ". name="
+ + auditProviderName);
+ return;
+ }
+
+ String logSubFolder = MiscUtil.getStringProperty(props, propPrefix +
"." + PROP_FILESYSTEM_SUBDIR);
+ if (StringUtils.isEmpty(logSubFolder)) {
+ logSubFolder = "%app-type%/%time:yyyyMMdd%";
+ }
+
+ logFileNameFormat = MiscUtil.getStringProperty(props, propPrefix + "."
+ PROP_FILESYSTEM_FILE_NAME_FORMAT);
+ fileRolloverSec = MiscUtil.getIntProperty(props, propPrefix + "." +
PROP_FILESYSTEM_FILE_ROLLOVER, fileRolloverSec);
+
+ if (StringUtils.isEmpty(fileExtension)) {
+ setFileExtension(PROP_FILESYSTEM_FILE_EXTENSION);
+ }
+
+ if (logFileNameFormat == null || logFileNameFormat.isEmpty()) {
+ logFileNameFormat = "%app-type%_ranger_audit_%hostname%" +
fileExtension;
+ }
+
+ logFolder = logFolderProp + "/" + logSubFolder;
+
+ logger.info("logFolder=" + logFolder + ", destName=" +
auditProviderName);
+ logger.info("logFileNameFormat=" + logFileNameFormat + ", destName="+
auditProviderName);
+ logger.info("config=" + auditConfigs.toString());
+
+ rolloverPeriod = MiscUtil.getStringProperty(props, propPrefix + "." +
PROP_FILESYSTEM_ROLLOVER_PERIOD);
+ rollingTimeUtil = RollingTimeUtil.getInstance();
+
+ //file.rollover.period is used for rolling over. If it could compute
the next roll over time using file.rollover.period
+ //it fall back to use file.rollover.sec for find next rollover time.
If still couldn't find default will be 1day window
+ //for rollover.
+ if(StringUtils.isEmpty(rolloverPeriod) ) {
+ rolloverPeriod =
rollingTimeUtil.convertRolloverSecondsToRolloverPeriod(fileRolloverSec);
+ }
+
+ try {
+ nextRollOverTime =
rollingTimeUtil.computeNextRollingTime(rolloverPeriod);
+ } catch ( Exception e) {
+ logger.warn("Rollover by file.rollover.period failed...will be
using the file.rollover.sec for "+ fileSystemScheme + " audit file
rollover...", e);
+ rollOverByDuration = true;
+ nextRollOverTime = rollOverByDuration();
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AbstractRangerAuditWriter.init()");
+ }
+
+ }
+
+ public void closeFileIfNeeded() throws IOException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AbstractRangerAuditWriter.closeFileIfNeeded()");
+ }
+
+ if (logWriter == null) {
+ return;
+ }
+
+ if ( System.currentTimeMillis() > nextRollOverTime.getTime() ) {
+ logger.info("Closing file. Rolling over. name=" + auditProviderName
+ + ", fileName=" + currentFileName);
+ try {
+ logWriter.flush();
+ logWriter.close();
+ } catch (Throwable t) {
+ logger.error("Error on closing log writter. Exception will be
ignored. name="
+ + auditProviderName + ", fileName=" + currentFileName);
+ }
+
+ logWriter = null;
+ ostream = null;
+ currentFileName = null;
+
+ if (!rollOverByDuration) {
+ try {
+ if(StringUtils.isEmpty(rolloverPeriod) ) {
+ rolloverPeriod =
rollingTimeUtil.convertRolloverSecondsToRolloverPeriod(fileRolloverSec);
+ }
+ nextRollOverTime =
rollingTimeUtil.computeNextRollingTime(rolloverPeriod);
+ } catch ( Exception e) {
+ logger.warn("Rollover by file.rollover.period
failed...will be using the file.rollover.sec for " + fileSystemScheme + " audit
file rollover...", e);
+ nextRollOverTime = rollOverByDuration();
+ }
+ } else {
+ nextRollOverTime = rollOverByDuration();
+ }
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AbstractRangerAuditWriter.closeFileIfNeeded()");
+ }
+ }
+
+ public Date rollOverByDuration() {
+ long rollOverTime =
rollingTimeUtil.computeNextRollingTime(fileRolloverSec,nextRollOverTime);
+ return new Date(rollOverTime);
+ }
+
+ public PrintWriter createWriter() throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AbstractRangerAuditWriter.createWriter()");
+ }
+
+ if (logWriter == null) {
+ // Create the file to write
+ logger.info("Creating new log file. auditPath=" + fullPath);
+ createFileSystemFolders();
+ ostream = fileSystem.create(auditPath);
+ logWriter = new PrintWriter(ostream);
+ isHFlushCapableStream =
ostream.hasCapability(StreamCapabilities.HFLUSH);
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AbstractRangerAuditWriter.createWriter()");
+ }
+
+ return logWriter;
+ }
+
+ public void closeWriter() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AbstractRangerAuditWriter.closeWriter()");
+ }
+
+ logWriter = null;
+ ostream = null;
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AbstractRangerAuditWriter.closeWriter()");
+ }
+ }
+
+ @Override
+ public void flush() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AbstractRangerAuditWriter.flush()");
+ }
+ if (ostream != null) {
+ try {
+ synchronized (this) {
+ if (ostream != null)
+ // 1) PrinterWriter does not have bufferring of its
own so
+ // we need to flush its underlying stream
+ // 2) HDFS flush() does not really flush all the way
to disk.
+ if (isHFlushCapableStream) {
+ //Checking HFLUSH capability of the stream because
of HADOOP-13327.
+ //For S3 filesysttem, hflush throws
UnsupportedOperationException and hence we call flush.
+ ostream.hflush();
+ } else {
+ ostream.flush();
+ } logger.info("Flush " +
fileSystemScheme + " audit logs completed.....");
+ }
+ } catch (IOException e) {
+ logger.error("Error on flushing log writer: " + e.getMessage()
+
+ "\nException will be ignored. name=" +
auditProviderName + ", fileName=" + currentFileName);
+ }
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AbstractRangerAuditWriter.flush()");
+ }
+ }
+
+ public boolean logFileToHDFS(File file) throws Exception {
+ boolean ret = false;
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> AbstractRangerAuditWriter.logFileToHDFS()");
+ }
+
+ if (logWriter == null) {
+ // Create the file to write
+ createFileSystemFolders();
+ logger.info("Copying the Audit File" + file.getName() + " to HDFS
Path" + fullPath);
+ Path destPath = new Path(fullPath);
+ ret = FileUtil.copy(file,fileSystem,destPath,false,conf);
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== AbstractRangerAuditWriter.logFileToHDFS()");
+ }
+ return ret;
+ }
+
+ public String getFileSystemScheme() {
+ String ret = null;
+ ret = logFolder.substring(0, (logFolder.indexOf(":")));
+ ret = ret.toUpperCase();
+ return ret;
+ }
+
+ public void setFileExtension(String fileExtension) {
+ this.fileExtension = fileExtension;
+ }
+}
\ No newline at end of file
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/utils/ORCFileUtil.java
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/ORCFileUtil.java
new file mode 100644
index 0000000..2e4378d
--- /dev/null
+++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/ORCFileUtil.java
@@ -0,0 +1,454 @@
+package org.apache.ranger.audit.utils;
+
+/*
+ * 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.
+ */
+
+import com.google.gson.annotations.SerializedName;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hive.ql.exec.vector.*;
+import org.apache.orc.CompressionKind;
+import org.apache.orc.OrcFile;
+import org.apache.orc.OrcFile.WriterOptions;
+import org.apache.orc.TypeDescription;
+import org.apache.orc.Writer;
+import org.apache.ranger.audit.model.AuthzAuditEvent;
+import org.apache.ranger.audit.model.EnumRepositoryType;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+import java.util.HashMap;
+import java.text.Format;
+import java.text.SimpleDateFormat;
+
+public class ORCFileUtil {
+
+ private static final Log logger = LogFactory.getLog(ORCFileUtil.class);
+
+ private static volatile ORCFileUtil me = null;
+ protected CompressionKind defaultCompression = CompressionKind.SNAPPY;
+ protected CompressionKind compressionKind = CompressionKind.NONE;
+ protected TypeDescription schema = null;
+ protected VectorizedRowBatch batch = null;
+ protected String auditSchema = null;
+ protected String dateFormat = "yyyy-MM-dd HH:mm:ss";
+
+ protected ArrayList<String> schemaFields = new
ArrayList<>();
+ protected Map<String,ColumnVector> vectorizedRowBatchMap = new
HashMap<>();
+ protected int orcBufferSize;
+ protected long orcStripeSize;
+
+ public static ORCFileUtil getInstance() {
+ ORCFileUtil orcFileUtil = me;
+ if (orcFileUtil == null) {
+ synchronized (ORCFileUtil.class) {
+ orcFileUtil = me;
+ if (orcFileUtil == null) {
+ me = orcFileUtil = new ORCFileUtil();
+ }
+ }
+ }
+ return orcFileUtil;
+ }
+
+ public void init(int orcBufferSize, long orcStripeSize, String
compression) throws Exception{
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> ORCFileUtil.init()");
+ }
+ this.orcBufferSize = orcBufferSize;
+ this.orcStripeSize = orcStripeSize;
+ this.compressionKind = getORCCompression(compression);
+ initORCAuditSchema();
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== ORCFileUtil.init() : orcBufferSize: " +
orcBufferSize + " stripeSize: " + orcStripeSize +
+ " compression: " + compression);
+ }
+ }
+
+ public Writer createWriter(Configuration conf, FileSystem fs, String path)
throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> ORCFileUtil.createWriter()");
+ }
+ Writer ret = null;
+ WriterOptions writeOptions = OrcFile.writerOptions(conf)
+ .fileSystem(fs)
+ .setSchema(schema)
+ .bufferSize(orcBufferSize)
+ .stripeSize(orcStripeSize)
+ .compress(compressionKind);
+
+ ret = OrcFile.createWriter(new Path(path), writeOptions);
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== ORCFileUtil.createWriter()");
+ }
+ return ret;
+ }
+
+ public void close(Writer writer) throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> ORCFileUtil.close()");
+ }
+
+ writer.close();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== ORCFileUtil.close()");
+ }
+ }
+
+ public void log(Writer writer, Collection<AuthzAuditEvent> events) throws
Exception {
+ int eventBatchSize = events.size();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> ORCFileUtil.log() : EventSize: " +
eventBatchSize + "ORC bufferSize:" + orcBufferSize );
+ }
+
+ //increase the batch size according to event size, so it can
accomodate all the events.
+ if (eventBatchSize > orcBufferSize) {
+ batch = schema.createRowBatch(orcBufferSize);
+ }
+
+ try {
+ for(AuthzAuditEvent event : events) {
+ int row = batch.size++;
+ for (int j=0;j<schemaFields.size();j++) {
+ String fieldName = schemaFields.get(j);
+ SchemaInfo schemaInfo = getFieldValue(event,
fieldName);
+ ColumnVector columnVector =
vectorizedRowBatchMap.get(fieldName);
+ if (columnVector instanceof LongColumnVector) {
+ ((LongColumnVector) columnVector).vector[row] =
castLongObject(schemaInfo.getValue());
+ } else if (columnVector instanceof BytesColumnVector) {
+ ((BytesColumnVector) columnVector).setVal(row,
getBytesValues(castStringObject(schemaInfo.getValue())));
+ }
+ }
+ if (batch.size == orcBufferSize) {
+ writer.addRowBatch(batch);
+ batch.reset();
+ }
+ }
+ if (batch.size != 0) {
+ writer.addRowBatch(batch);
+ batch.reset();
+ }
+ } catch (Exception e) {
+ batch.reset();
+ logger.error("Error while writing into ORC File:", e);
+ throw e;
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== ORCFileUtil.log(): EventSize = " +
eventBatchSize );
+ }
+ }
+
+ protected byte[] getBytesValues(String val) {
+ byte[] ret = "".getBytes();
+ if(val != null) {
+ ret = val.getBytes();
+ }
+ return ret;
+ }
+
+ protected String getDateString(Date date) {
+ String ret = null;
+ Format formatter = new SimpleDateFormat(dateFormat);
+ ret = formatter.format(date);
+ return ret;
+ }
+
+ protected void initORCAuditSchema() throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> ORCWriter.initORCAuditSchema()");
+ }
+ auditSchema = getAuditSchema();
+ Map<String,String> schemaFieldTypeMap = getSchemaFieldTypeMap();
+ schema = TypeDescription.fromString(auditSchema);
+ batch = schema.createRowBatch(orcBufferSize);
+ buildVectorRowBatch(schemaFieldTypeMap);
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== ORCWriter.initORCAuditSchema()");
+ }
+ }
+
+ protected Map<String,String> getSchemaFieldTypeMap() {
+ Map<String,String> ret = new HashMap<>();
+
+ int index1 = auditSchema.indexOf("<");
+ int index2 = auditSchema.indexOf(">");
+ String subAuditSchema = auditSchema.substring(index1+1,index2);
+ String[] fields = subAuditSchema.split(",");
+ schemaFields = new ArrayList<>();
+
+ for (String field: fields) {
+ String[] flds = field.split(":");
+ schemaFields.add(flds[0]);
+ ret.put(flds[0],flds[1]);
+ }
+ return ret;
+ }
+
+ protected void buildVectorRowBatch(Map<String,String> schemaFieldTypeMap)
throws Exception {
+ int i = 0;
+ for (i=0;i<schemaFields.size();i++) {
+ String fld = schemaFields.get(i);
+ String fieldType = schemaFieldTypeMap.get(fld);
+ ColumnVector columnVector = getColumnVectorType(fieldType);
+ if (columnVector instanceof LongColumnVector) {
+ vectorizedRowBatchMap.put(fld, (LongColumnVector)
batch.cols[i]);
+ } else if (columnVector instanceof BytesColumnVector) {
+ vectorizedRowBatchMap.put(fld, (BytesColumnVector)
batch.cols[i]);
+ } else if (columnVector instanceof DecimalColumnVector) {
+ vectorizedRowBatchMap.put(fld, (DecimalColumnVector)
batch.cols[i]);
+ }
+ }
+ }
+
+ protected SchemaInfo getFieldValue(AuthzAuditEvent event, String fieldName
) {
+ SchemaInfo ret = new SchemaInfo();
+ try {
+ Class aClass = AuthzAuditEvent.class;
+ Field fld = aClass.getDeclaredField(fieldName);
+ fld.setAccessible(true);
+
+ Class<?> cls = fld.getType();
+ Object value = fld.get(event);
+
+ ret.setField(fieldName);
+ ret.setType(cls.getName());
+ ret.setValue(value);
+ } catch (Exception e){
+ logger.error("Error while writing into ORC File:", e);
+ }
+ return ret;
+ }
+
+ protected ColumnVector getColumnVectorType(String fieldType) throws
Exception {
+ ColumnVector ret = null;
+ fieldType = fieldType.toLowerCase();
+ switch(fieldType) {
+ case "int" :
+ case "bigint":
+ case "date":
+ case "boolean":
+ ret = new LongColumnVector();
+ break;
+ case "string":
+ case "varchar":
+ case "char":
+ case "binary":
+ ret = new BytesColumnVector();
+ break;
+ case "decimal":
+ ret = new DecimalColumnVector(10,5);
+ break;
+ case "double":
+ case "float":
+ ret = new DoubleColumnVector();
+ break;
+ case "array":
+ case "map":
+ case "uniontype":
+ case "struct":
+ throw new Exception("Unsuppoted field Type");
+ }
+ return ret;
+ }
+
+ protected Long castLongObject(Object object) {
+ Long ret = 0l;
+ try {
+ if (object instanceof Long)
+ ret = ((Long) object).longValue();
+ else if (object instanceof Integer) {
+ ret = ((Integer) object).longValue();
+ } else if (object instanceof String) {
+ ret = Long.valueOf((String) object);
+ }
+ } catch (Exception e) {
+ logger.error("Error while writing into ORC File:", e);
+ }
+ return ret;
+ }
+
+ protected String castStringObject(Object object) {
+ String ret = null;
+ try {
+ if (object instanceof String)
+ ret = (String) object;
+ else if (object instanceof Date) {
+ ret = (getDateString((Date) object));
+ }
+ } catch (Exception e) {
+ logger.error("Error while writing into ORC File:", e);
+ }
+ return ret;
+ }
+
+ protected String getAuditSchema() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> ORCWriter.getAuditSchema()");
+ }
+ String ret = null;
+ String fieldStr = "struct<";
+ StringBuilder sb = new StringBuilder(fieldStr);
+
+ Class auditEventClass = AuthzAuditEvent.class;
+ for(Field fld: auditEventClass.getDeclaredFields()) {
+ if (fld.isAnnotationPresent(SerializedName.class)) {
+ String field = fld.getName();
+ String fieldType = getShortFieldType(fld.getType().getName());
+ if (fieldType == null) {
+ continue;
+ }
+ fieldStr = field + ":" + fieldType + ",";
+ sb.append(fieldStr);
+ }
+ }
+ fieldStr = sb.toString();
+ if (fieldStr.endsWith(",")) {
+ fieldStr = fieldStr.substring(0, fieldStr.length() - 1);
+ }
+ ret = fieldStr + ">";
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== ORCWriter.getAuditSchema() AuditSchema: " +
ret);
+ }
+ return ret;
+ }
+
+ protected String getShortFieldType(String type){
+ String ret = null;
+ switch(type) {
+ case "java.lang.String":
+ ret = "string";
+ break;
+ case "int":
+ ret = "int";
+ break;
+ case "short":
+ ret = "string";
+ break;
+ case "java.util.Date":
+ ret = "string";
+ break;
+ case "long":
+ ret = "bigint";
+ break;
+ default:
+ ret = null;
+ }
+ return ret;
+ }
+
+ class SchemaInfo {
+ String field = null;
+ String type = null;
+ Object value = null;
+
+ public String getField() {
+ return field;
+ }
+
+ public void setField(String field) {
+ this.field = field;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+ }
+
+ protected CompressionKind getORCCompression(String compression) {
+ CompressionKind ret;
+ if (compression == null) {
+ compression = defaultCompression.name().toLowerCase();
+ }
+ switch(compression) {
+ case "snappy":
+ ret = CompressionKind.SNAPPY;
+ break;
+ case "lzo":
+ ret = CompressionKind.LZO;
+ break;
+ case "zlip":
+ ret = CompressionKind.ZLIB;
+ break;
+ case "none":
+ ret = CompressionKind.NONE;
+ break;
+ default:
+ ret = defaultCompression;
+ break;
+ }
+ return ret;
+ }
+
+ public static void main(String[] args) throws Exception {
+ ORCFileUtil auditOrcFileUtil = new ORCFileUtil();
+ auditOrcFileUtil.init(10000,100000L,"snappy");
+ try {
+ Configuration conf = new Configuration();
+ FileSystem fs = FileSystem.get(conf);
+ Writer write = auditOrcFileUtil.createWriter(conf, fs,
"/tmp/test.orc");
+ Collection<AuthzAuditEvent> events = getTestEvent();
+ auditOrcFileUtil.log(write, events);
+ write.close();
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ protected static Collection<AuthzAuditEvent> getTestEvent() {
+ Collection<AuthzAuditEvent> events = new ArrayList<>();
+ for (int idx=0;idx<20;idx++) {
+ AuthzAuditEvent event = new AuthzAuditEvent();
+ event.setEventId(Integer.toString(idx));
+ event.setClientIP("127.0.0.1");
+ event.setAccessResult((short) 1);
+ event.setAclEnforcer("ranger-acl");
+ event.setRepositoryName("hdfsdev");
+ event.setRepositoryType(EnumRepositoryType.HDFS);
+ event.setResourcePath("/tmp/test-audit.log" +idx+idx+1);
+ event.setResourceType("file");
+ event.setAccessType("read");
+ event.setEventTime(new Date());
+ event.setResultReason(Integer.toString(1));
+ events.add(event);
+ }
+ return events;
+ }
+}
\ No newline at end of file
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerAuditWriter.java
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerAuditWriter.java
new file mode 100644
index 0000000..fbe9301
--- /dev/null
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerAuditWriter.java
@@ -0,0 +1,39 @@
+package org.apache.ranger.audit.utils;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Properties;
+
+public interface RangerAuditWriter {
+ void init(Properties prop, String propPrefix, String auditProviderName,
Map<String,String> auditConfigs);
+
+ boolean log(Collection<String> events) throws Exception;
+
+ boolean logFile(File file) throws Exception;
+
+ void start();
+
+ void flush();
+
+ void stop();
+}
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerJSONAuditWriter.java
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerJSONAuditWriter.java
new file mode 100644
index 0000000..f46e0a5
--- /dev/null
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerJSONAuditWriter.java
@@ -0,0 +1,175 @@
+package org.apache.ranger.audit.utils;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.audit.provider.MiscUtil;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Writes the Ranger audit to HDFS as JSON text
+ */
+public class RangerJSONAuditWriter extends AbstractRangerAuditWriter {
+
+ private static final Log logger =
LogFactory.getLog(RangerJSONAuditWriter.class);
+
+ protected String JSON_FILE_EXTENSION = ".log";
+
+ public void init(Properties props, String propPrefix, String
auditProviderName, Map<String,String> auditConfigs) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> RangerJSONAuditWriter.init()");
+ }
+ init();
+ super.init(props,propPrefix,auditProviderName,auditConfigs);
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== RangerJSONAuditWriter.init()");
+ }
+ }
+
+ public void init() {
+ setFileExtension(JSON_FILE_EXTENSION);
+ }
+
+ synchronized public boolean logJSON(final Collection<String> events)
throws Exception {
+ boolean ret = false;
+ PrintWriter out = null;
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("UGI=" + MiscUtil.getUGILoginUser()
+ + ". Will write to HDFS file=" + currentFileName);
+ }
+ out = MiscUtil.executePrivilegedAction(new
PrivilegedExceptionAction<PrintWriter>() {
+ @Override
+ public PrintWriter run() throws Exception {
+ PrintWriter out = getLogFileStream();
+ for (String event : events) {
+ out.println(event);
+ }
+ return out;
+ };
+ });
+ // flush and check the stream for errors
+ if (out.checkError()) {
+ // In theory, this count may NOT be accurate as part of the
messages may have been successfully written.
+ // However, in practice, since client does buffering, either
all of none would succeed.
+ out.close();
+ closeWriter();
+ return ret;
+ }
+ } catch (Exception e) {
+ if (out != null) {
+ out.close();
+ }
+ closeWriter();
+ return ret;
+ } finally {
+ ret = true;
+ if (logger.isDebugEnabled()) {
+ logger.debug("Flushing HDFS audit. Event Size:" +
events.size());
+ }
+ if (out != null) {
+ out.flush();
+ }
+ }
+
+ return ret;
+ }
+
+ @Override
+ public boolean log(Collection<String> events) throws Exception {
+ return logJSON(events);
+ }
+
+ synchronized public boolean logAsFile(final File file) throws Exception {
+ boolean ret = false;
+ if (logger.isDebugEnabled()) {
+ logger.debug("UGI=" + MiscUtil.getUGILoginUser()
+ + ". Will write to HDFS file=" + currentFileName);
+ }
+ Boolean retVal = MiscUtil.executePrivilegedAction(new
PrivilegedExceptionAction<Boolean>() {
+ @Override
+ public Boolean run() throws Exception {
+ boolean ret = logFileToHDFS(file);
+ return Boolean.valueOf(ret);
+ };
+ });
+ ret = retVal.booleanValue();
+ logger.info("Flushing HDFS audit File :" + file.getAbsolutePath() +
file.getName());
+ return ret;
+ }
+
+ @Override
+ public boolean logFile(File file) throws Exception {
+ return logAsFile(file);
+ }
+
+ synchronized public PrintWriter getLogFileStream() throws Exception {
+ closeFileIfNeeded();
+ // Either there are no open log file or the previous one has been
rolled
+ // over
+ PrintWriter logWriter = createWriter();
+ return logWriter;
+ }
+
+
+ public void flush() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> JSONWriter.flush()");
+ }
+ logger.info("Flush called. name=" + auditProviderName);
+ super.flush();
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== JSONWriter.flush()");
+ }
+ }
+
+ @Override
+ public void start() {
+ // nothing to start
+ }
+
+ @Override
+ synchronized public void stop() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> JSONWriter.stop()");
+ }
+ if (logWriter != null) {
+ try {
+ logWriter.flush();
+ logWriter.close();
+ } catch (Throwable t) {
+ logger.error("Error on closing log writter. Exception will be
ignored. name="
+ + auditProviderName + ", fileName=" + currentFileName);
+ }
+ logWriter = null;
+ ostream = null;
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== JSONWriter.stop()");
+ }
+ }
+}
\ No newline at end of file
diff --git
a/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerORCAuditWriter.java
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerORCAuditWriter.java
new file mode 100644
index 0000000..a3cd140
--- /dev/null
+++
b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerORCAuditWriter.java
@@ -0,0 +1,189 @@
+/*
+ * 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.ranger.audit.utils;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.orc.Writer;
+import org.apache.ranger.audit.model.AuthzAuditEvent;
+import org.apache.ranger.audit.provider.MiscUtil;
+
+import java.io.File;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * This class writes the Ranger audits to HDFS as ORC files
+ * Refer README.TXT for enabling ORCWriter.
+ */
+public class RangerORCAuditWriter extends AbstractRangerAuditWriter {
+ private static final Log logger =
LogFactory.getLog(RangerORCAuditWriter.class);
+
+ protected static final String ORC_FILE_EXTENSION = ".orc";
+ protected volatile ORCFileUtil orcFileUtil = null;
+ protected Writer orcLogWriter = null;
+ protected String fileType = "orc";
+ protected String compression = null;
+ protected int orcBufferSize = 0;
+ protected int defaultbufferSize = 100000;
+ protected long orcStripeSize = 0;
+ protected long defaultStripeSize = 100000L;
+
+ @Override
+ public void init(Properties props, String propPrefix, String
auditProviderName, Map<String,String> auditConfigs) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> RangerORCAuditWriter.init()");
+ }
+ init(props,propPrefix);
+ super.init(props, propPrefix, auditProviderName, auditConfigs);
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== RangerORCAuditWriter.init()");
+ }
+ }
+
+ synchronized public boolean logAuditAsORC(final
Collection<AuthzAuditEvent> events) throws Exception {
+ boolean ret = false;
+ Writer out = null;
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("UGI=" + MiscUtil.getUGILoginUser()
+ + ". Will write to HDFS file=" + currentFileName);
+ }
+
+ out = MiscUtil.executePrivilegedAction(new
PrivilegedExceptionAction<Writer>() {
+ @Override
+ public Writer run() throws Exception {
+ Writer out = getORCFileWrite();
+ orcFileUtil.log(out,events);
+ return out;
+ };
+ });
+ } catch (Exception e) {
+ orcLogWriter = null;
+ logger.error("Error while writing into ORC FileWriter", e);
+ throw e;
+ } finally {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Flushing HDFS audit in ORC Format. Event Size:"
+ events.size());
+ }
+ if (out != null) {
+ try {
+ //flush and close the ORC batch file
+ orcFileUtil.close(out);
+ ret = true;
+ } catch (Exception e) {
+ logger.error("Error while closing the ORC FileWriter", e);
+ throw e;
+ }
+ orcLogWriter = null;
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public void flush() {
+ //For HDFSAuditDestionation with ORC format each file is flushed
immediately after writing the ORC batch.
+ //So nothing to flush.
+ }
+
+ @Override
+ public boolean log(Collection<String> events) throws Exception {
+ return logAsORC(events);
+ }
+
+ @Override
+ public void start() {
+ // Nothing to do here. We will open the file when the first log
request comes
+ }
+
+ @Override
+ synchronized public void stop() {
+ if (orcLogWriter != null) {
+ try {
+ orcFileUtil.close(orcLogWriter);
+ } catch (Throwable t) {
+ logger.error("Error on closing log ORC Writer. Exception will
be ignored. name="
+ + auditProviderName + ", fileName=" + currentFileName);
+ }
+ orcLogWriter = null;
+ }
+ }
+
+ @Override
+ public boolean logFile(File file) throws Exception {
+ return false;
+ }
+
+ // Creates ORC Write file
+ synchronized protected Writer getORCFileWrite() throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug("==> RangerORCAuditWriter.getORCFileWrite()");
+ }
+ if (orcLogWriter == null) {
+ // Create the file to write
+ createFileSystemFolders();
+ logger.info("Creating new log file. hdfPath=" + fullPath);
+ orcLogWriter = orcFileUtil.createWriter(conf, fileSystem,
fullPath);
+ currentFileName = fullPath;
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("<== RangerORCAuditWriter.getORCFileWrite()");
+ }
+ return orcLogWriter;
+ }
+
+ public boolean logAsORC(Collection<String> events) throws Exception {
+ boolean ret = false;
+ Collection<AuthzAuditEvent> authzAuditEvents =
getAuthzAuditEvents(events);
+ ret = logAuditAsORC(authzAuditEvents);
+ return ret;
+ }
+
+ public Collection<AuthzAuditEvent> getAuthzAuditEvents(Collection<String>
events) throws Exception {
+ Collection<AuthzAuditEvent> ret = new ArrayList<>();
+ for (String event : events) {
+ try {
+ AuthzAuditEvent authzAuditEvent = MiscUtil.fromJson(event,
AuthzAuditEvent.class);
+ ret.add(authzAuditEvent);
+ } catch (Exception e) {
+ logger.error("Error converting to From JSON to
AuthzAuditEvent=" + event);
+ throw e;
+ }
+ }
+ return ret;
+ }
+
+ public void init(Properties props, String propPrefix) {
+ compression = MiscUtil.getStringProperty(props, propPrefix + "." +
fileType +".compression");
+ orcBufferSize = MiscUtil.getIntProperty(props, propPrefix + "." +
fileType +".buffersize",defaultbufferSize);
+ orcStripeSize = MiscUtil.getLongProperty(props, propPrefix + "." +
fileType +".stripesize",defaultStripeSize);
+ setFileExtension(ORC_FILE_EXTENSION);
+ try {
+ orcFileUtil = ORCFileUtil.getInstance();
+ orcFileUtil.init(orcBufferSize, orcStripeSize, compression);
+ } catch ( Exception e) {
+ logger.error("Error while doing ORCWriter.init() ", e);
+ }
+ }
+}
\ No newline at end of file