RANGER-2699 : JVM metrics for Ranger usersync and Ranger tagsync

Signed-off-by: Pradeep <pradeep@apache.org>
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerMetricsUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerMetricsUtil.java
new file mode 100644
index 0000000..f6eef2f
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerMetricsUtil.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.plugin.util;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.ranger.plugin.model.RangerMetrics;
+
+import com.google.gson.Gson;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.RuntimeMXBean;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.MemoryType;
+import java.lang.management.MemoryUsage;
+
+/**
+ * Connect Worker system and runtime information.
+ */
+public class RangerMetricsUtil {
+
+    private static final Logger LOG = Logger.getLogger(RangerMetricsUtil.class);
+    private static final OperatingSystemMXBean OS;
+    private static final MemoryMXBean MEM_BEAN;
+    public static final String NL = System.getProperty("line.separator");
+
+    private static final RuntimeMXBean RUNTIME = ManagementFactory.getRuntimeMXBean();
+    private static final String JVM_MACHINE_ACTUAL_NAME = RUNTIME.getVmName();
+    private static final String VERSION = RUNTIME.getVmVersion();
+    private static final String JVM_MACHINE_REPRESENTATION_NAME = RUNTIME.getName();
+    private static final long UP_TIME_OF_JVM = RUNTIME.getUptime();
+    private static final String JVM_VENDOR_NAME =  RUNTIME.getVmVendor();
+
+
+    static {
+        OS = ManagementFactory.getOperatingSystemMXBean();
+        MEM_BEAN = ManagementFactory.getMemoryMXBean();
+    }
+
+    public Map<String, Object> getValues() {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerMetricsUtil.getValues()");
+        }
+
+        Map<String, Object> values = new LinkedHashMap<>();
+        values.put("os.spec", StringUtils.join(Arrays.asList(addSystemInfo()), ", "));
+        values.put("os.vcpus", String.valueOf(OS.getAvailableProcessors()));
+        values.put("memory", addMemoryDetails());
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RangerMetricsUtil.getValues()" + values);
+        }
+
+        return values;
+    }
+
+    /**
+     * collect the pool division of java
+     */
+    protected Map<String, Object> getPoolDivision() {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerMetricsUtil.getPoolDivision()");
+        }
+
+        Map<String, Object> poolDivisionValues = new LinkedHashMap<>();
+        for (MemoryPoolMXBean mpBean : ManagementFactory.getMemoryPoolMXBeans()) {
+            if (mpBean.getType() == MemoryType.HEAP) {
+                poolDivisionValues.put(mpBean.getName(), mpBean.getUsage());
+            }
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RangerMetricsUtil.getPoolDivision()" + poolDivisionValues);
+        }
+
+        return poolDivisionValues;
+    }
+
+    /**
+     * Add memory details
+     */
+    protected Map<String, Object> addMemoryDetails() {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerMetricsUtil.addMemoryDetails()");
+        }
+
+        Map<String, Object> memory  = new LinkedHashMap<>();
+        MemoryUsage memHeapUsage = MEM_BEAN.getHeapMemoryUsage();
+        MemoryUsage nonHeapUsage = MEM_BEAN.getNonHeapMemoryUsage();
+        memory.put("heapInit", String.valueOf(memHeapUsage.getInit()));
+        memory.put("heapMax", String.valueOf(memHeapUsage.getMax()));
+        memory.put("heapCommitted", String.valueOf(memHeapUsage.getCommitted()));
+        memory.put("heapUsed", String.valueOf(memHeapUsage.getUsed()));
+        memory.put("nonHeapInit", String.valueOf(nonHeapUsage.getInit()));
+        memory.put("nonHeapMax", String.valueOf(nonHeapUsage.getMax()));
+        memory.put("nonHeapCommitted", String.valueOf(nonHeapUsage.getCommitted()));
+        memory.put("nonHeapUsed", String.valueOf(nonHeapUsage.getUsed()));
+        memory.put("memory_pool_usages", getPoolDivision());
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RangerMetricsUtil.addMemoryDetails()" + memory);
+        }
+
+        return memory;
+    }
+
+    /**
+     * Collect system information.
+     */
+    protected String[] addSystemInfo() {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerMetricsUtil.addSystemInfo()");
+        }
+
+        String[] osInfo = { OS.getName(), OS.getArch(), OS.getVersion() };
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RangerMetricsUtil.addSystemInfo()" + osInfo);
+        }
+
+        return osInfo;
+    }
+
+    public RangerMetrics getVMStatus() {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerMetricsUtil.getVMStatus()");
+		}
+
+		Map<String, Object> jvm = new LinkedHashMap<>();
+		Map<String, Object> vmDetails = new LinkedHashMap<>();
+		vmDetails.put("JVM Machine Actual Name", JVM_MACHINE_ACTUAL_NAME);
+		vmDetails.put("version", VERSION);
+		vmDetails.put("JVM Machine Representation Name", JVM_MACHINE_REPRESENTATION_NAME);
+		vmDetails.put("Up time of JVM", UP_TIME_OF_JVM);
+		vmDetails.put("JVM Vendor Name", JVM_VENDOR_NAME);
+		vmDetails.putAll(getValues());
+		jvm.put("jvm", vmDetails);
+
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerMetricsUtil.getVMStatus() " + jvm);
+		}
+
+		return new RangerMetrics(jvm);
+	}
+
+	public void writeMetricsToFile(File filePath) throws Throwable {
+
+		RangerMetrics rangerMetrics = null;
+		rangerMetrics = getVMStatus();
+		if (null == rangerMetrics || null == filePath) {
+			LOG.debug("RangerMetrics or filePath can not be null)");
+			return;
+		}
+		if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerMetricsUtil.writeMetricsToFIle() for path: "+ filePath);
+        }
+		Gson gson = new Gson();
+		try (FileWriter file = new FileWriter(filePath)) {
+			gson.toJson(rangerMetrics, file);
+			file.flush();
+		} catch (Exception e ) {
+			LOG.error("RangerMetricsUtil.writeMetricsToFile() got an error",e);
+			throw e;
+		}
+	}
+}
diff --git a/tagsync/conf/templates/installprop2xml.properties b/tagsync/conf/templates/installprop2xml.properties
index 510cd6f..55e015e 100644
--- a/tagsync/conf/templates/installprop2xml.properties
+++ b/tagsync/conf/templates/installprop2xml.properties
@@ -65,3 +65,8 @@
 # TODO - What property in ranger-tagsync-site.xml should hadoop_conf map to??
 hadoop_conf = hadoop_conf
 
+#JVM metrics related property
+JVM_METRICS_ENABLED=ranger.tagsync.metrics.enabled
+JVM_METRICS_FILENAME=ranger.tagsync.metrics.filename
+JVM_METRICS_FILEPATH=ranger.tagsync.metrics.filepath
+JVM_METRICS_FREQUENCY_TIME_IN_MILLIS=ranger.tagsync.metrics.frequencytimeinmillis
\ No newline at end of file
diff --git a/tagsync/conf/templates/ranger-tagsync-template.xml b/tagsync/conf/templates/ranger-tagsync-template.xml
index b8bfbf5..40bd3db 100644
--- a/tagsync/conf/templates/ranger-tagsync-template.xml
+++ b/tagsync/conf/templates/ranger-tagsync-template.xml
@@ -107,4 +107,20 @@
 		<name>ranger.tagsync.dest.ranger.session.cookie.name</name>
 		<value>RANGERADMINSESSIONID</value>
 	</property>
+	<property>
+		<name>ranger.tagsync.metrics.filepath</name>
+		<value></value>
+	</property>
+	<property>
+		<name>ranger.tagsync.metrics.filename</name>
+		<value></value>
+	</property>
+	<property>
+		<name>ranger.tagsync.metrics.frequencytimeinmillis</name>
+		<value></value>
+	</property>
+	<property>
+		<name>ranger.tagsync.metrics.enabled</name>
+		<value>false</value>
+	</property>
 </configuration>
diff --git a/tagsync/scripts/install.properties b/tagsync/scripts/install.properties
index be33cc2..ae2e555 100644
--- a/tagsync/scripts/install.properties
+++ b/tagsync/scripts/install.properties
@@ -109,3 +109,22 @@
 
 
 hadoop_conf=/etc/hadoop/conf
+
+# if you want to enable or disable jvm metrics for tagsync process
+# valid values: true, false
+# any value other than true would be treated as false
+# default value: false
+# if the value is false, jvm metrics is not created
+JVM_METRICS_ENABLED=
+
+# filename of jvm metrics created for tagsync process
+# default value: ranger_tagsync_metric.json
+JVM_METRICS_FILENAME=
+
+#file directory for jvm metrics
+# default value : logdir
+JVM_METRICS_FILEPATH=
+
+#frequency for jvm metrics to be updated
+# default value : 10000 milliseconds 
+JVM_METRICS_FREQUENCY_TIME_IN_MILLIS=
diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSyncConfig.java b/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSyncConfig.java
index c4173da..95c3482 100644
--- a/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSyncConfig.java
+++ b/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSyncConfig.java
@@ -31,6 +31,8 @@
 import java.io.InputStream;
 import java.net.URL;
 import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.Enumeration;
 import java.util.Properties;
 
@@ -105,6 +107,14 @@
 
 	private static String LOCAL_HOSTNAME = "unknown";
 
+    private static final String  TAGSYNC_METRICS_FILEPATH =   "ranger.tagsync.metrics.filepath";
+    private static final String  DEFAULT_TAGSYNC_METRICS_FILEPATH =   "/tmp/";
+    private static final String  TAGSYNC_METRICS_FILENAME =   "ranger.tagsync.metrics.filename";
+    private static final String  DEFAULT_TAGSYNC_METRICS_FILENAME =   "ranger_tagsync_metric.json";
+    private static final String  TAGSYNC_METRICS_FREQUENCY_TIME_IN_MILLIS_PARAM = "ranger.tagsync.metrics.frequencytimeinmillis";
+    private static final long    DEFAULT_TAGSYNC_METRICS_FREQUENCY__TIME_IN_MILLIS = 10000L;
+    private static final String  TAGSYNC_METRICS_ENABLED_PROP = "ranger.tagsync.metrics.enabled";
+
 	private Properties props;
 
 	static {
@@ -460,4 +470,48 @@
 		}
 	}
 
+	public String getTagSyncMetricsFileName() {
+		String val = getProperties().getProperty(TAGSYNC_METRICS_FILEPATH);
+		if (StringUtils.isBlank(val)) {
+			if (StringUtils.isBlank(System.getProperty("logdir"))) {
+				val = DEFAULT_TAGSYNC_METRICS_FILEPATH;
+			} else {
+				val = System.getProperty("logdir");
+			}
+		}
+
+		if (Files.notExists(Paths.get(val))) {
+				return null;
+		}
+
+		StringBuilder pathAndFileName = new StringBuilder(val);
+		if (!val.endsWith("/")) {
+			pathAndFileName.append("/");
+		}
+		String fileName = getProperties().getProperty(TAGSYNC_METRICS_FILENAME);
+		if (StringUtils.isBlank(fileName)) {
+			fileName = DEFAULT_TAGSYNC_METRICS_FILENAME;
+		}
+		pathAndFileName.append(fileName);
+		return pathAndFileName.toString();
+	}
+
+	public long getTagSyncMetricsFrequency() {
+		long ret = DEFAULT_TAGSYNC_METRICS_FREQUENCY__TIME_IN_MILLIS;
+		String val = getProperties().getProperty(TAGSYNC_METRICS_FREQUENCY_TIME_IN_MILLIS_PARAM);
+		if (StringUtils.isNotBlank(val)) {
+			try {
+				ret = Long.valueOf(val);
+			} catch (NumberFormatException exception) {
+				// Ignore
+			}
+		}
+		return ret;
+	}
+
+	public static boolean isTagSyncMetricsEnabled(Properties prop) {
+		String val = prop.getProperty(TAGSYNC_METRICS_ENABLED_PROP);
+		return "true".equalsIgnoreCase(StringUtils.trimToEmpty(val));
+	}
+
 }
diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSyncMetricsProducer.java b/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSyncMetricsProducer.java
new file mode 100644
index 0000000..dc6c8f1
--- /dev/null
+++ b/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSyncMetricsProducer.java
@@ -0,0 +1,89 @@
+package org.apache.ranger.tagsync.process;
+
+/*
+ * 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 org.apache.log4j.Logger;
+import org.apache.ranger.plugin.util.RangerMetricsUtil;
+
+public class TagSyncMetricsProducer implements Runnable {
+
+	private static final Logger LOG = Logger.getLogger(TagSyncMetricsProducer.class);
+	private boolean shutdownFlag = false;
+
+	public static void main(String[] args) {
+		TagSyncMetricsProducer tagSyncMetrics = new TagSyncMetricsProducer();
+		tagSyncMetrics.run();
+		// try { tagSyncMetrics.writeJVMMetrics(); } catch (Throwable e) { }
+	}
+
+	@Override
+	public void run() {
+		try {
+			TagSyncConfig config = TagSyncConfig.getInstance();
+			long sleepTimeBetweenCycleInMillis = config.getTagSyncMetricsFrequency();
+			String logFileNameWithPath = config.getTagSyncMetricsFileName();
+			LOG.info("Tagsync metrics frequency :  " + sleepTimeBetweenCycleInMillis +" and metrics file : "+logFileNameWithPath );
+			if (null != logFileNameWithPath) {
+				while (!shutdownFlag) {
+					try {
+						if (LOG.isDebugEnabled()) {
+							LOG.debug("Sleeping Tagsync metrics for [" + sleepTimeBetweenCycleInMillis
+									+ "] milliSeconds");
+						}
+						Thread.sleep(sleepTimeBetweenCycleInMillis);
+					} catch (InterruptedException e) {
+						LOG.error("Failed to wait for [" + sleepTimeBetweenCycleInMillis
+								+ "] milliseconds before attempting to tagsync metrics information", e);
+					}
+					try {
+						writeJVMMetrics(logFileNameWithPath);
+					} catch (Throwable t) {
+						LOG.error("Failed to write tagsync metrics into file. Error details: ", t);
+					}
+				}
+			} else {
+				LOG.info("No file directory found for tagsync metrics log ");
+			}
+		} catch (Throwable t) {
+			LOG.error("Failed to start Tagsync metrics. Error details: ", t);
+		} finally {
+			LOG.info("Shutting down the TagSync metrics thread");
+		}
+
+	}
+
+	private void writeJVMMetrics(String logFileNameWithPath) throws Throwable {
+		try {
+			File userMetricFile = null;
+			userMetricFile = new File(logFileNameWithPath);
+			if (!userMetricFile.exists()) {
+				userMetricFile.createNewFile();
+			}
+			RangerMetricsUtil rangerMetricsUtil = new RangerMetricsUtil();
+			rangerMetricsUtil.writeMetricsToFile(userMetricFile);
+
+		} catch (Throwable t) {
+			LOG.error("TagSyncMetricsProducer.writeJVMMetrics() failed to write metrics into file. Error details: ", t);
+			throw t;
+		}
+	}
+
+}
diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSynchronizer.java b/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSynchronizer.java
index 8806c74..fefb6bf 100644
--- a/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSynchronizer.java
+++ b/tagsync/src/main/java/org/apache/ranger/tagsync/process/TagSynchronizer.java
@@ -19,6 +19,13 @@
 
 package org.apache.ranger.tagsync.process;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
@@ -28,13 +35,6 @@
 import org.apache.ranger.tagsync.model.TagSink;
 import org.apache.ranger.tagsync.model.TagSource;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.StringTokenizer;
-
 public class TagSynchronizer {
 
 	private static final Logger LOG = Logger.getLogger(TagSynchronizer.class);
@@ -53,7 +53,6 @@
 	private volatile boolean isShutdownInProgress = false;
 
 	public static void main(String[] args) {
-
 		TagSynchronizer tagSynchronizer = new TagSynchronizer();
 
 		TagSyncConfig config = TagSyncConfig.getInstance();
@@ -134,6 +133,20 @@
 		try {
 			boolean threadsStarted = tagSink.start();
 
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("==> starting TagSyncMetricsProducer with default metrics location : "+System.getProperty("logdir"));
+			}
+			//Start the tag sync metrics
+			boolean isTagSyncMetricsEnabled = TagSyncConfig.isTagSyncMetricsEnabled(properties);
+			if (isTagSyncMetricsEnabled) {
+				TagSyncMetricsProducer tagSyncMetricsProducer = new TagSyncMetricsProducer();
+				Thread tagSyncMetricsProducerThread = new Thread(tagSyncMetricsProducer);
+				tagSyncMetricsProducerThread.setName("TagSyncMetricsProducerThread");
+				tagSyncMetricsProducerThread.setDaemon(true);
+				tagSyncMetricsProducerThread.start();
+			} else {
+				LOG.info(" Ranger tagsync metrics is not enabled");
+			}
 			for (TagSource tagSource : tagSources) {
 				threadsStarted = threadsStarted && tagSource.start();
 			}
diff --git a/tagsync/src/main/resources/ranger-tagsync-site.xml b/tagsync/src/main/resources/ranger-tagsync-site.xml
index 0b9ef84..c0f2631 100644
--- a/tagsync/src/main/resources/ranger-tagsync-site.xml
+++ b/tagsync/src/main/resources/ranger-tagsync-site.xml
@@ -101,7 +101,22 @@
 		<name>ranger.tagsync.dest.ranger.session.cookie.name</name>
 		<value>RANGERADMINSESSIONID</value>
 	</property>
-
+	<property>
+		<name>ranger.tagsync.metrics.filepath</name>
+		<value></value>
+	</property>
+	<property>
+		<name>ranger.tagsync.metrics.filename</name>
+		<value></value>
+	</property>
+	<property>
+		<name>ranger.tagsync.metrics.frequencytimeinmillis</name>
+		<value></value>
+	</property>
+	<property>
+		<name>ranger.tagsync.metrics.enabled</name>
+		<value>false</value>
+	</property>
     <!-- Ranger-tagsync uses the following two properties to derive name of Ranger Service in a Federated or non-Federated HDFS setup -->
 
     <!-- service-name for HDFS in 'cl1' cluster, that services nameService 'ns1' in a Federated setup
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
index a041345..ae4e474 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
@@ -19,6 +19,10 @@
 
 package org.apache.ranger.unixusersync.config;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -240,6 +244,14 @@
 
 	private static final String RANGER_ADMIN_COOKIE_NAME_PROPS = "ranger.usersync.dest.ranger.session.cookie.name";
 
+    private static final String  UGSYNC_METRICS_FILEPATH =   "ranger.usersync.metrics.filepath";
+    private static final String  DEFAULT_UGSYNC_METRICS_FILEPATH =   "/tmp/";
+    private static final String  UGSYNC_METRICS_FILENAME =   "ranger.usersync.metrics.filename";
+    private static final String  DEFAULT_UGSYNC_METRICS_FILENAME =   "ranger_usersync_metric.json";
+    private static final String  UGSYNC_METRICS_FREQUENCY_TIME_IN_MILLIS_PARAM = "ranger.usersync.metrics.frequencytimeinmillis";
+    private static final long    DEFAULT_UGSYNC_METRICS_FREQUENCY_TIME_IN_MILLIS = 10000L;
+    public static final String   UGSYNC_METRICS_ENABLED_PROP = "ranger.usersync.metrics.enabled";
+
     private Properties prop = new Properties();
 
 	private static volatile UserGroupSyncConfig me = null;
@@ -1056,6 +1068,59 @@
 
 	/* Used only for unit testing */
 	public void setGroupHierarchyLevel(int groupHierarchyLevel) {
-        	prop.setProperty(LGSYNC_GROUP_HIERARCHY_LEVELS, String.valueOf(groupHierarchyLevel));
-        }
+		prop.setProperty(LGSYNC_GROUP_HIERARCHY_LEVELS, String.valueOf(groupHierarchyLevel));
+	}
+
+	public String getUserSyncMetricsFileName() throws IOException {
+		String val = prop.getProperty(UGSYNC_METRICS_FILEPATH);
+		if (StringUtils.isBlank(val)) {
+			if (StringUtils.isBlank(prop.getProperty("ranger.usersync.logdir"))) {
+				if (StringUtils.isBlank(System.getProperty("logdir"))) {
+					val = DEFAULT_UGSYNC_METRICS_FILEPATH;
+				} else {
+					val = System.getProperty("logdir");
+				}
+			} else {
+				val = prop.getProperty("ranger.usersync.logdir");
+			}
+		}
+
+		if (Files.notExists(Paths.get(val))) {
+			String current = new File(".").getCanonicalPath();
+			val = current + "/" + val;
+			if (Files.notExists(Paths.get(val))) {
+				return null;
+			}
+		}
+
+		StringBuilder pathAndFileName = new StringBuilder(val);
+		if (!val.endsWith("/")) {
+			pathAndFileName.append("/");
+		}
+
+		String fileName = prop.getProperty(UGSYNC_METRICS_FILENAME);
+		if (StringUtils.isBlank(fileName)) {
+			fileName = DEFAULT_UGSYNC_METRICS_FILENAME;
+		}
+		pathAndFileName.append(fileName);
+		return pathAndFileName.toString();
+	}
+
+	public long getUserSyncMetricsFrequency() {
+		long ret = DEFAULT_UGSYNC_METRICS_FREQUENCY_TIME_IN_MILLIS;
+		String val = prop.getProperty(UGSYNC_METRICS_FREQUENCY_TIME_IN_MILLIS_PARAM);
+		if (StringUtils.isNotBlank(val)) {
+			try {
+				ret = Long.valueOf(val);
+			} catch (NumberFormatException exception) {
+				// Ignore
+			}
+		}
+		return ret;
+	}
+
+	public boolean isUserSyncMetricsEnabled() {
+		String val = prop.getProperty(UGSYNC_METRICS_ENABLED_PROP);
+		return "true".equalsIgnoreCase(StringUtils.trimToEmpty(val));
+	}
 }
diff --git a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserSyncMetricsProducer.java b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserSyncMetricsProducer.java
new file mode 100644
index 0000000..2d5b212
--- /dev/null
+++ b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserSyncMetricsProducer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.usergroupsync;
+
+import java.io.File;
+
+import org.apache.log4j.Logger;
+import org.apache.ranger.plugin.util.RangerMetricsUtil;
+import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
+
+public class UserSyncMetricsProducer implements Runnable {
+
+	private static final Logger LOG = Logger.getLogger(UserSyncMetricsProducer.class);
+	private boolean shutdownFlag = false;
+
+	public static void main(String[] args) {
+		UserSyncMetricsProducer userSyncMetrics = new UserSyncMetricsProducer();
+		userSyncMetrics.run();
+		/*
+		 * try { userSyncMetrics.writeJVMMetrics(); } catch (Throwable e) { // TODO
+		 * Auto-generated catch block e.printStackTrace(); }
+		 */
+	}
+
+	@Override
+	public void run() {
+		try {
+			UserGroupSyncConfig config = UserGroupSyncConfig.getInstance();
+			long sleepTimeBetweenCycleInMillis = config.getUserSyncMetricsFrequency();
+			String logFileNameWithPath = config.getUserSyncMetricsFileName();
+			LOG.info("user sync metrics frequency :  " + sleepTimeBetweenCycleInMillis + " and metrics file : "
+					+ logFileNameWithPath);
+			if (null != logFileNameWithPath) {
+				while (!shutdownFlag) {
+					try {
+						if (LOG.isDebugEnabled()) {
+							LOG.debug("Sleeping metrics for [" + sleepTimeBetweenCycleInMillis + "] milliSeconds");
+						}
+						Thread.sleep(sleepTimeBetweenCycleInMillis);
+					} catch (InterruptedException e) {
+						LOG.error("Failed to wait for [" + sleepTimeBetweenCycleInMillis
+								+ "] milliseconds before attempting to fetch userSync metrics information", e);
+					}
+					try {
+						writeJVMMetrics(logFileNameWithPath);
+					} catch (Throwable t) {
+						LOG.error("Failed to write user sync metrics into file. Error details: ", t);
+					}
+				}
+			} else {
+				LOG.info("No file directory found for usersync metrics log ");
+			}
+		} catch (Throwable t) {
+			LOG.error("Failed to start user sync metrics. Error details: ", t);
+		} finally {
+			LOG.info("Shutting down the User Sync metrics producer thread");
+		}
+
+	}
+
+	private void writeJVMMetrics(String logFileNameWithPath) throws Throwable {
+		try {
+			File userMetricFile = null;
+			userMetricFile = new File(logFileNameWithPath);
+			if (!userMetricFile.exists()) {
+				userMetricFile.createNewFile();
+			}
+			RangerMetricsUtil rangerMetricsUtil = new RangerMetricsUtil();
+			rangerMetricsUtil.writeMetricsToFile(userMetricFile);
+		} catch (Throwable t) {
+			LOG.error("UserSyncMetricsProducer.writeJVMMetrics() failed to write metrics into file. Error details: ", t);
+			throw t;
+		}
+	}
+}
diff --git a/unixauthservice/scripts/install.properties b/unixauthservice/scripts/install.properties
index d4c6513..d805a99 100644
--- a/unixauthservice/scripts/install.properties
+++ b/unixauthservice/scripts/install.properties
@@ -217,3 +217,22 @@
 SYNC_PAGED_RESULTS_SIZE=
 #LDAP context referral could be ignore or follow
 SYNC_LDAP_REFERRAL =ignore
+
+# if you want to enable or disable jvm metrics for usersync process
+# valid values: true, false
+# any value other than true would be treated as false
+# default value: false
+# if the value is false, jvm metrics is not created
+JVM_METRICS_ENABLED=
+
+# filename of jvm metrics created for usersync process
+# default value: ranger_usersync_metric.json
+JVM_METRICS_FILENAME=
+
+#file directory for jvm metrics
+# default value : logdir
+JVM_METRICS_FILEPATH=
+
+#frequency for jvm metrics to be updated
+# default value : 10000 milliseconds 
+JVM_METRICS_FREQUENCY_TIME_IN_MILLIS=
\ No newline at end of file
diff --git a/unixauthservice/scripts/templates/installprop2xml.properties b/unixauthservice/scripts/templates/installprop2xml.properties
index fa342fb..e64ca3a 100644
--- a/unixauthservice/scripts/templates/installprop2xml.properties
+++ b/unixauthservice/scripts/templates/installprop2xml.properties
@@ -57,3 +57,8 @@
 SYNC_LDAP_REFERRAL = ranger.usersync.ldap.referral
 usersync_principal= ranger.usersync.kerberos.principal
 usersync_keytab= ranger.usersync.kerberos.keytab
+#JVM metrics related property
+JVM_METRICS_ENABLED=ranger.usersync.metrics.enabled
+JVM_METRICS_FILENAME=ranger.usersync.metrics.filename
+JVM_METRICS_FILEPATH=ranger.usersync.metrics.filepath
+JVM_METRICS_FREQUENCY_TIME_IN_MILLIS=ranger.usersync.metrics.frequencytimeinmillis
\ No newline at end of file
diff --git a/unixauthservice/scripts/templates/ranger-ugsync-template.xml b/unixauthservice/scripts/templates/ranger-ugsync-template.xml
index 0cacc95..b5dd437 100644
--- a/unixauthservice/scripts/templates/ranger-ugsync-template.xml
+++ b/unixauthservice/scripts/templates/ranger-ugsync-template.xml
@@ -229,4 +229,20 @@
 		<name>ranger.usersync.dest.ranger.session.cookie.name</name>
 		<value>RANGERADMINSESSIONID</value>
 	</property>
+    <property>
+		<name>ranger.usersync.metrics.enabled</name>
+		<value>false</value>
+    </property>
+    <property>
+		<name>ranger.usersync.metrics.filepath</name>
+		<value></value>
+    </property>
+    <property>
+		<name>ranger.usersync.metrics.frequencytimeinmillis</name>
+		<value></value>
+    </property>
+    <property>
+		<name>ranger.usersync.metrics.filename</name>
+		<value></value>
+    </property>
 </configuration>
diff --git a/unixauthservice/src/main/java/org/apache/ranger/authentication/UnixAuthenticationService.java b/unixauthservice/src/main/java/org/apache/ranger/authentication/UnixAuthenticationService.java
index 1ee5e21..fa146c2 100644
--- a/unixauthservice/src/main/java/org/apache/ranger/authentication/UnixAuthenticationService.java
+++ b/unixauthservice/src/main/java/org/apache/ranger/authentication/UnixAuthenticationService.java
@@ -46,20 +46,22 @@
 import org.apache.log4j.Logger;
 import org.apache.ranger.credentialapi.CredentialReader;
 import org.apache.ranger.plugin.util.XMLUtils;
+import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
 import org.apache.ranger.usergroupsync.UserGroupSync;
+import org.apache.ranger.usergroupsync.UserSyncMetricsProducer;
 
 public class UnixAuthenticationService {
 
 	private static final Logger LOG = Logger.getLogger(UnixAuthenticationService.class);
-	
+
 	private static final String serviceName = "UnixAuthenticationService";
-	
+
 	private static final String SSL_ALGORITHM = "TLS";
 	private static final String REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM = "ranger.usersync.port";
-	
+
 	private static final String SSL_KEYSTORE_PATH_PARAM = "ranger.usersync.keystore.file";
 	private static final String SSL_TRUSTSTORE_PATH_PARAM = "ranger.usersync.truststore.file";
-	
+
 	private static final String SSL_KEYSTORE_PATH_PASSWORD_ALIAS = "usersync.ssl.key.password";
 	private static final String SSL_TRUSTSTORE_PATH_PASSWORD_ALIAS = "usersync.ssl.truststore.password";
 
@@ -123,11 +125,12 @@
 			LOG.info("Service: " + serviceName + " - STOPPED.");
 		}
 	}
-	
+
 	private void startUnixUserGroupSyncProcess() {
 		//
 		//  Start the synchronization service ...
 		//
+		LOG.info("Start : startUnixUserGroupSyncProcess " );
 		UserGroupSync syncProc = new UserGroupSync();
 		Thread newSyncProcThread = new Thread(syncProc);
 		newSyncProcThread.setName("UnixUserSyncThread");
@@ -135,13 +138,27 @@
         // Therefore this is marked as non-daemon thread. Don't change the following line
 		newSyncProcThread.setDaemon(false);
 		newSyncProcThread.start();
+        LOG.info("UnixUserSyncThread started");
+        LOG.info("creating UserSyncMetricsProducer thread with default metrics location : "+System.getProperty("logdir"));
+		//Start the user sync metrics
+		boolean isUserSyncMetricsEnabled = UserGroupSyncConfig.getInstance().isUserSyncMetricsEnabled();
+		if (isUserSyncMetricsEnabled) {
+			UserSyncMetricsProducer userSyncMetricsProducer = new UserSyncMetricsProducer();
+			Thread userSyncMetricsProducerThread = new Thread(userSyncMetricsProducer);
+			userSyncMetricsProducerThread.setName("UserSyncMetricsProducerThread");
+			userSyncMetricsProducerThread.setDaemon(false);
+			userSyncMetricsProducerThread.start();
+			LOG.info("UserSyncMetricsProducer started");
+		} else {
+			LOG.info(" Ranger userSync metrics is not enabled");
+		}
 	}
-	
+
 
 	//TODO: add more validation code
 	private void init() throws Throwable {
 		Properties prop = new Properties();
-		
+
 		for (String fn : UGSYNC_CONFIG_XML_FILES ) {
             XMLUtils.loadConfig(fn, prop);
 		}