azkaban-developers

Merge pull request #453 from evlstyle/master Add feature

8/17/2015 6:57:55 PM

Details

diff --git a/azkaban-common/src/main/java/azkaban/jobExecutor/AbstractProcessJob.java b/azkaban-common/src/main/java/azkaban/jobExecutor/AbstractProcessJob.java
index 73592f0..831e49b 100644
--- a/azkaban-common/src/main/java/azkaban/jobExecutor/AbstractProcessJob.java
+++ b/azkaban-common/src/main/java/azkaban/jobExecutor/AbstractProcessJob.java
@@ -43,6 +43,9 @@ public abstract class AbstractProcessJob extends AbstractJob {
   public static final String JOB_PROP_ENV = "JOB_PROP_FILE";
   public static final String JOB_NAME_ENV = "JOB_NAME";
   public static final String JOB_OUTPUT_PROP_FILE = "JOB_OUTPUT_PROP_FILE";
+  private static final String SENSITIVE_JOB_PROP_NAME_SUFFIX = "_X";
+  private static final String SENSITIVE_JOB_PROP_VALUE_PLACEHOLDER = "[MASKED]";
+  private static final String JOB_DUMP_PROPERTIES_IN_LOG = "job.dump.properties";
 
   protected final String _jobPath;
 
@@ -81,6 +84,31 @@ public abstract class AbstractProcessJob extends AbstractJob {
     jobProps = PropsUtils.resolveProps(jobProps);
   }
 
+  /**
+   * prints the current Job props to the Job log.
+   */
+  protected void logJobProperties() {
+    if (this.jobProps != null &&
+        this.jobProps.getBoolean(JOB_DUMP_PROPERTIES_IN_LOG, false)){
+      try {
+        Map<String,String> flattenedProps = this.jobProps.getFlattened();
+        this.info("******   Job properties   ******");
+        this.info(String.format("- Note : value is masked if property name ends with '%s'.",
+            SENSITIVE_JOB_PROP_NAME_SUFFIX ));
+        for(Map.Entry<String, String> entry : flattenedProps.entrySet()){
+          String key = entry.getKey();
+          String value = key.endsWith(SENSITIVE_JOB_PROP_NAME_SUFFIX)?
+                                      SENSITIVE_JOB_PROP_VALUE_PLACEHOLDER :
+                                      entry.getValue();
+          this.info(String.format("%s=%s",key,value));
+        }
+        this.info("****** End Job properties  ******");
+      } catch (Exception ex){
+        log.error("failed to log job properties ", ex);
+      }
+    }
+  }
+
   @Override
   public Props getJobGeneratedProperties() {
     return generatedProperties;
@@ -101,7 +129,6 @@ public abstract class AbstractProcessJob extends AbstractJob {
 
     files[1] = createOutputPropsFile(getId(), _cwd);
     jobProps.put(ENV_PREFIX + JOB_OUTPUT_PROP_FILE, files[1].getAbsolutePath());
-
     return files;
   }
 
diff --git a/azkaban-common/src/main/java/azkaban/jobExecutor/LongArgJob.java b/azkaban-common/src/main/java/azkaban/jobExecutor/LongArgJob.java
index 36ba23a..6188e99 100644
--- a/azkaban-common/src/main/java/azkaban/jobExecutor/LongArgJob.java
+++ b/azkaban-common/src/main/java/azkaban/jobExecutor/LongArgJob.java
@@ -69,6 +69,9 @@ public abstract class LongArgJob extends AbstractProcessJob {
 
     File[] propFiles = initPropsFiles();
 
+    // print out the Job properties to the job log.
+    this.logJobProperties();
+
     boolean success = false;
     this.process = builder.build();
     try {
diff --git a/azkaban-common/src/main/java/azkaban/jobExecutor/ProcessJob.java b/azkaban-common/src/main/java/azkaban/jobExecutor/ProcessJob.java
index 18b7ffa..4e268a0 100644
--- a/azkaban-common/src/main/java/azkaban/jobExecutor/ProcessJob.java
+++ b/azkaban-common/src/main/java/azkaban/jobExecutor/ProcessJob.java
@@ -93,6 +93,9 @@ public class ProcessJob extends AbstractProcessJob {
       }
       info("Working directory: " + builder.getWorkingDir());
 
+      // print out the Job properties to the job log.
+      this.logJobProperties();
+
       boolean success = false;
       this.process = builder.build();
 
@@ -120,7 +123,7 @@ public class ProcessJob extends AbstractProcessJob {
    * This is used to get the min/max memory size requirement by processes.
    * SystemMemoryInfo can use the info to determine if the memory request
    * can be fulfilled. For Java process, this should be Xms/Xmx setting.
-   *  
+   *
    * @return pair of min/max memory size
    */
   protected Pair<Long, Long> getProcMemoryRequirement() throws Exception {
diff --git a/azkaban-common/src/main/java/azkaban/utils/Props.java b/azkaban-common/src/main/java/azkaban/utils/Props.java
index db2b8fe..68fcbd3 100644
--- a/azkaban-common/src/main/java/azkaban/utils/Props.java
+++ b/azkaban-common/src/main/java/azkaban/utils/Props.java
@@ -34,6 +34,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.TreeMap;
 
 import org.apache.log4j.Logger;
 
@@ -811,20 +812,28 @@ public class Props {
   }
 
   /**
+   * Returns a map of all the flattened properties, the item in the returned map is sorted alphabetically
+   * by the key value.
+   *
+   *
+   * @Return
+   */
+  public Map<String,String> getFlattened(){
+    TreeMap<String,String> returnVal = new TreeMap<String,String>(); 
+    returnVal.putAll(getMapByPrefix(""));
+    return returnVal; 
+  }
+
+  /**
    * Get a map of all properties by string prefix
    *
    * @param prefix The string prefix
    */
   public Map<String, String> getMapByPrefix(String prefix) {
-    Map<String, String> values = new HashMap<String, String>();
-
-    if (_parent != null) {
-      for (Map.Entry<String, String> entry : _parent.getMapByPrefix(prefix)
-          .entrySet()) {
-        values.put(entry.getKey(), entry.getValue());
-      }
-    }
+    Map<String, String> values = _parent == null ? new HashMap<String, String>():
+                                                   _parent.getMapByPrefix(prefix);
 
+    // when there is a conflict, value from the child takes the priority.
     for (String key : this.localKeySet()) {
       if (key.startsWith(prefix)) {
         values.put(key.substring(prefix.length()), get(key));
diff --git a/azkaban-common/src/test/java/azkaban/utils/PropsUtilsTest.java b/azkaban-common/src/test/java/azkaban/utils/PropsUtilsTest.java
index 158558e..54464cd 100644
--- a/azkaban-common/src/test/java/azkaban/utils/PropsUtilsTest.java
+++ b/azkaban-common/src/test/java/azkaban/utils/PropsUtilsTest.java
@@ -17,6 +17,7 @@
 package azkaban.utils;
 
 import java.io.IOException;
+import java.util.Map;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -127,6 +128,52 @@ public class PropsUtilsTest {
   }
 
   @Test
+  public void testGetFlattenedProps() throws Exception {
+
+    // for empty props empty flattened map is expected to be returned.
+    Props grandParentProps = new Props();
+    Assert.assertTrue(grandParentProps.getFlattened().isEmpty());
+
+    // single level
+    grandParentProps.put("test1","value1");
+    grandParentProps.put("test2","value2");
+    Map<String,String> set = grandParentProps.getFlattened();
+    Assert.assertEquals(2,set.size());
+    Assert.assertEquals("value1", set.get("test1"));
+    Assert.assertEquals("value2", set.get("test2"));
+
+    // multiple levels .
+    Props parentProps = new Props(grandParentProps);
+    parentProps.put("test3","value3");
+    parentProps.put("test4","value4");
+    set = parentProps.getFlattened();
+    Assert.assertEquals(4,set.size());
+    Assert.assertEquals("value3", set.get("test3"));
+    Assert.assertEquals("value1", set.get("test1"));
+
+    // multiple levels with same keys  .
+    Props props = new Props(parentProps);
+    props.put("test5","value5");
+    props.put("test1","value1.1");
+    set = props.getFlattened();
+    Assert.assertEquals(5,set.size());
+    Assert.assertEquals("value5", set.get("test5"));
+    Assert.assertEquals("value1.1", set.get("test1"));
+
+    // verify when iterating the elements are sorted by the key value.
+    Props props2 = new Props();
+    props2.put("2","2");
+    props2.put("0","0");
+    props2.put("1","1");
+    set = props2.getFlattened();
+    int index = 0 ;
+    for (Map.Entry<String, String> item : set.entrySet())
+    {
+      Assert.assertEquals(item.getKey(),Integer.toString(index++));
+    }
+  }
+
+  @Test
   public void testCyclesResolveProps() throws IOException {
     Props propsGrandParent = new Props();
     Props propsParent = new Props(propsGrandParent);