Repository: oozie
Updated Branches:
  refs/heads/master 31dbb1bff -> 95d17f26b


OOZIE-1894 Better error reporting to user


Project: http://git-wip-us.apache.org/repos/asf/oozie/repo
Commit: http://git-wip-us.apache.org/repos/asf/oozie/commit/95d17f26
Tree: http://git-wip-us.apache.org/repos/asf/oozie/tree/95d17f26
Diff: http://git-wip-us.apache.org/repos/asf/oozie/diff/95d17f26

Branch: refs/heads/master
Commit: 95d17f26b0f36c9c82e110fba8fe432f25925092
Parents: 31dbb1b
Author: Purshotam Shah <[email protected]>
Authored: Mon Jan 26 17:26:40 2015 -0800
Committer: Purshotam Shah <[email protected]>
Committed: Mon Jan 26 17:26:40 2015 -0800

----------------------------------------------------------------------
 .../java/org/apache/oozie/cli/OozieCLI.java     |  15 ++
 .../org/apache/oozie/client/OozieClient.java    |  23 +++
 .../apache/oozie/client/rest/RestConstants.java |   2 +
 core/src/main/conf/oozie-log4j.properties       |  27 ++-
 .../main/java/org/apache/oozie/BaseEngine.java  |  13 ++
 .../java/org/apache/oozie/BundleEngine.java     |  25 ++-
 .../org/apache/oozie/CoordinatorEngine.java     |  27 ++-
 .../main/java/org/apache/oozie/DagEngine.java   |  31 +++-
 .../org/apache/oozie/service/XLogService.java   | 119 ++++--------
 .../oozie/service/XLogStreamingService.java     |  23 +++
 .../java/org/apache/oozie/service/XLogUtil.java | 144 +++++++++++++++
 .../oozie/service/ZKXLogStreamingService.java   |  59 ++++--
 .../apache/oozie/servlet/BaseJobServlet.java    |  19 +-
 .../org/apache/oozie/servlet/V0JobServlet.java  |   7 +
 .../org/apache/oozie/servlet/V1JobServlet.java  |   6 +
 .../org/apache/oozie/servlet/V2JobServlet.java  |  41 ++++
 .../apache/oozie/util/XLogUserFilterParam.java  |   2 +-
 .../oozie/service/TestXLogStreamingService.java | 185 ++++++++++++++++++-
 .../service/TestZKXLogStreamingService.java     | 126 ++++++++++++-
 .../resources/test-no-dash-log4j.properties     |  22 ++-
 docs/src/site/twiki/DG_CommandLineTool.twiki    |  26 +++
 docs/src/site/twiki/WebServicesAPI.twiki        |  23 +++
 release-log.txt                                 |   1 +
 webapp/src/main/webapp/oozie-console.js         | 130 +++++++++++--
 24 files changed, 965 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/client/src/main/java/org/apache/oozie/cli/OozieCLI.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/oozie/cli/OozieCLI.java 
b/client/src/main/java/org/apache/oozie/cli/OozieCLI.java
index bc8fa50..0c28732 100644
--- a/client/src/main/java/org/apache/oozie/cli/OozieCLI.java
+++ b/client/src/main/java/org/apache/oozie/cli/OozieCLI.java
@@ -111,6 +111,8 @@ public class OozieCLI {
     public static final String RERUN_OPTION = "rerun";
     public static final String INFO_OPTION = "info";
     public static final String LOG_OPTION = "log";
+    public static final String ERROR_LOG_OPTION = "errorlog";
+
     public static final String ACTION_OPTION = "action";
     public static final String DEFINITION_OPTION = "definition";
     public static final String CONFIG_CONTENT_OPTION = "configcontent";
@@ -312,6 +314,8 @@ public class OozieCLI {
         Option timezone = new Option(TIME_ZONE_OPTION, true,
                 "use time zone with the specified ID (default GMT).\nSee 
'oozie info -timezones' for a list");
         Option log = new Option(LOG_OPTION, true, "job log");
+        Option errorlog = new Option(ERROR_LOG_OPTION, true, "job error log");
+
         Option logFilter = new Option(
                 RestConstants.LOG_FILTER_OPTION, true,
                 "job log search parameter. Can be specified as -logfilter 
opt1=val1;opt2=val1;opt3=val1. "
@@ -359,6 +363,7 @@ public class OozieCLI {
         actions.addOption(info);
         actions.addOption(rerun);
         actions.addOption(log);
+        actions.addOption(errorlog);
         actions.addOption(definition);
         actions.addOption(config_content);
         actions.addOption(ignore);
@@ -1159,6 +1164,16 @@ public class OozieCLI {
                     }
                 }
             }
+            else if (options.contains(ERROR_LOG_OPTION)) {
+                PrintStream ps = System.out;
+                try {
+                    
wc.getJobErrorLog(commandLine.getOptionValue(ERROR_LOG_OPTION), ps);
+                }
+                finally {
+                    ps.close();
+                }
+            }
+
             else if (options.contains(DEFINITION_OPTION)) {
                 
System.out.println(wc.getJobDefinition(commandLine.getOptionValue(DEFINITION_OPTION)));
             }

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/client/src/main/java/org/apache/oozie/client/OozieClient.java
----------------------------------------------------------------------
diff --git a/client/src/main/java/org/apache/oozie/client/OozieClient.java 
b/client/src/main/java/org/apache/oozie/client/OozieClient.java
index 800e871..29b1131 100644
--- a/client/src/main/java/org/apache/oozie/client/OozieClient.java
+++ b/client/src/main/java/org/apache/oozie/client/OozieClient.java
@@ -990,6 +990,17 @@ public class OozieClient {
     }
 
     /**
+     * Get the error log of a job.
+     *
+     * @param jobId
+     * @param ps
+     * @throws OozieClientException
+     */
+    public void getJobErrorLog(String jobId, PrintStream ps) throws 
OozieClientException {
+        new JobErrorLog(jobId, ps).call();
+    }
+
+    /**
      * Get the log of a job.
      *
      * @param jobId job Id.
@@ -1012,6 +1023,12 @@ public class OozieClient {
         }
     }
 
+    private class JobErrorLog extends JobMetadata {
+        JobErrorLog(String jobId, PrintStream ps) {
+            super(jobId, RestConstants.JOB_SHOW_ERROR_LOG, ps);
+        }
+    }
+
     /**
      * Gets the JMS topic name for a particular job
      * @param jobId given jobId
@@ -1068,6 +1085,12 @@ public class OozieClient {
                     metaType));
         }
 
+        JobMetadata(String jobId, String metaType, PrintStream ps) {
+            this(jobId, metaType);
+            printStream = ps;
+
+        }
+
         JobMetadata(String jobId, String logRetrievalType, String 
logRetrievalScope, String metaType, String logFilter,
                 PrintStream ps) {
             super("GET", RestConstants.JOB, notEmpty(jobId, "jobId"), 
prepareParams(RestConstants.JOB_SHOW_PARAM,

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/client/src/main/java/org/apache/oozie/client/rest/RestConstants.java
----------------------------------------------------------------------
diff --git 
a/client/src/main/java/org/apache/oozie/client/rest/RestConstants.java 
b/client/src/main/java/org/apache/oozie/client/rest/RestConstants.java
index 85efecf..3c2afc3 100644
--- a/client/src/main/java/org/apache/oozie/client/rest/RestConstants.java
+++ b/client/src/main/java/org/apache/oozie/client/rest/RestConstants.java
@@ -87,6 +87,8 @@ public interface RestConstants {
 
     public static final String JOB_SHOW_LOG = "log";
 
+    public static final String JOB_SHOW_ERROR_LOG = "errorlog";
+
     public static final String JOB_SHOW_DEFINITION = "definition";
 
     public static final String JOB_SHOW_GRAPH = "graph";

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/conf/oozie-log4j.properties
----------------------------------------------------------------------
diff --git a/core/src/main/conf/oozie-log4j.properties 
b/core/src/main/conf/oozie-log4j.properties
index b91bfa4..dd3e53c 100644
--- a/core/src/main/conf/oozie-log4j.properties
+++ b/core/src/main/conf/oozie-log4j.properties
@@ -44,6 +44,29 @@ 
log4j.appender.oozie.RollingPolicy.FileNamePattern=${log4j.appender.oozie.File}-
 # The MaxHistory controls how many log files will be retained (720 hours / 24 
hours per day = 30 days); -1 to disable
 log4j.appender.oozie.RollingPolicy.MaxHistory=720
 
+
+
+log4j.appender.oozieError=org.apache.log4j.rolling.RollingFileAppender
+log4j.appender.oozieError.RollingPolicy=org.apache.oozie.util.OozieRollingPolicy
+log4j.appender.oozieError.File=${oozie.log.dir}/oozie-error.log
+log4j.appender.oozieError.Append=true
+log4j.appender.oozieError.layout=org.apache.log4j.PatternLayout
+log4j.appender.oozieError.layout.ConversionPattern=%d{ISO8601} %5p %c{1}:%L - 
SERVER[${oozie.instance.id}] %m%n
+# The FileNamePattern must end with "-%d{yyyy-MM-dd-HH}.gz" or 
"-%d{yyyy-MM-dd-HH}" and also start with the
+# value of log4j.appender.oozieError.File
+log4j.appender.oozieError.RollingPolicy.FileNamePattern=${log4j.appender.oozieError.File}-%d{yyyy-MM-dd-HH}
+# The MaxHistory controls how many log files will be retained (720 hours / 24 
hours per day = 30 days); -1 to disable
+log4j.appender.oozieError.RollingPolicy.MaxHistory=720
+log4j.appender.oozieError.filter.1 = org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.oozieError.filter.1.levelToMatch = WARN
+log4j.appender.oozieError.filter.2 = org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.oozieError.filter.2.levelToMatch = ERROR
+log4j.appender.oozieError.filter.3 = org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.oozieError.filter.3.levelToMatch = FATAL
+log4j.appender.oozieError.filter.4 = org.apache.log4j.varia.DenyAllFilter
+
+
+
 # Uncomment the below two lines to use the DailyRollingFileAppender instead
 # The DatePattern must end with either "dd" or "HH"
 #log4j.appender.oozie=org.apache.log4j.DailyRollingFileAppender
@@ -81,8 +104,8 @@ log4j.logger.openjpa=INFO, openjpa
 log4j.logger.oozieops=INFO, oozieops
 log4j.logger.oozieinstrumentation=ALL, oozieinstrumentation
 log4j.logger.oozieaudit=ALL, oozieaudit
-log4j.logger.org.apache.oozie=INFO, oozie
+log4j.logger.org.apache.oozie=ALL, oozie, oozieError
 log4j.logger.org.apache.hadoop=WARN, oozie
 log4j.logger.org.mortbay=WARN, oozie
 log4j.logger.org.hsqldb=WARN, oozie
-log4j.logger.org.apache.hadoop.security.authentication.server=WARN, oozie
+log4j.logger.org.apache.hadoop.security.authentication.server=WARN, oozie
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/BaseEngine.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/BaseEngine.java 
b/core/src/main/java/org/apache/oozie/BaseEngine.java
index a982ad4..bf38a0c 100644
--- a/core/src/main/java/org/apache/oozie/BaseEngine.java
+++ b/core/src/main/java/org/apache/oozie/BaseEngine.java
@@ -171,6 +171,19 @@ public abstract class BaseEngine {
             throws IOException, BaseEngineException;
 
     /**
+     * Stream error log of a job.
+     *
+     * @param jobId job Id.
+     * @param writer writer to stream the log to.
+     * @param params additional parameters from the request
+     * @throws IOException thrown if the log cannot be streamed.
+     * @throws BaseEngineException thrown if there is error in getting the 
Workflow/Coordinator Job Information for
+     *         jobId.
+     */
+    public abstract void streamErrorLog(String jobId, Writer writer, 
Map<String, String[]> params) throws IOException,
+            BaseEngineException;
+
+    /**
      * Return the workflow Job ID for an external ID.
      * <p/>
      * This is reverse lookup for recovery purposes.

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/BundleEngine.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/BundleEngine.java 
b/core/src/main/java/org/apache/oozie/BundleEngine.java
index 27afe73..9818acc 100644
--- a/core/src/main/java/org/apache/oozie/BundleEngine.java
+++ b/core/src/main/java/org/apache/oozie/BundleEngine.java
@@ -242,12 +242,19 @@ public class BundleEngine extends BaseEngine {
         }
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.oozie.BaseEngine#streamLog(java.lang.String, 
java.io.Writer)
-     */
-    @Override
+
     public void streamLog(String jobId, Writer writer, Map<String, String[]> 
params) throws IOException,
             BundleEngineException {
+        streamJobLog(jobId, writer, params, false);
+    }
+
+    public void streamErrorLog(String jobId, Writer writer, Map<String, 
String[]> params) throws IOException,
+            BundleEngineException {
+        streamJobLog(jobId, writer, params, true);
+    }
+
+    private void streamJobLog(String jobId, Writer writer, Map<String, 
String[]> params, boolean isErrorLog) throws IOException,
+            BundleEngineException {
 
         BundleJobBean job;
         try {
@@ -261,7 +268,15 @@ public class BundleEngine extends BaseEngine {
             if (lastTime == null) {
                 lastTime = new Date();
             }
-            Services.get().get(XLogStreamingService.class).streamLog(filter, 
job.getCreatedTime(), lastTime, writer, params);
+            if (isErrorLog) {
+                Services.get().get(XLogStreamingService.class)
+                        .streamErrorLog(filter, job.getCreatedTime(), 
lastTime, writer, params);
+            }
+            else {
+                Services.get().get(XLogStreamingService.class)
+                        .streamLog(filter, job.getCreatedTime(), lastTime, 
writer, params);
+
+            }
         }
         catch (Exception ex) {
             throw new IOException(ex);

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/CoordinatorEngine.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/CoordinatorEngine.java 
b/core/src/main/java/org/apache/oozie/CoordinatorEngine.java
index 3406c6f..136c097 100644
--- a/core/src/main/java/org/apache/oozie/CoordinatorEngine.java
+++ b/core/src/main/java/org/apache/oozie/CoordinatorEngine.java
@@ -293,15 +293,20 @@ public class CoordinatorEngine extends BaseEngine {
         throw new BaseEngineException(new XException(ErrorCode.E0301, "invalid 
use of start"));
     }
 
-    /*
-     * (non-Javadoc)
-     *
-     * @see org.apache.oozie.BaseEngine#streamLog(java.lang.String,
-     * java.io.Writer)
-     */
     @Override
     public void streamLog(String jobId, Writer writer, Map<String, String[]> 
params) throws IOException,
             BaseEngineException {
+        streamJobLog(jobId, writer, params, false);
+    }
+
+    @Override
+    public void streamErrorLog(String jobId, Writer writer, Map<String, 
String[]> params) throws IOException,
+            BaseEngineException {
+        streamJobLog(jobId, writer, params, true);
+    }
+
+    private void streamJobLog(String jobId, Writer writer, Map<String, 
String[]> params, boolean isErrorLog)
+            throws IOException, BaseEngineException {
 
         try {
             XLogFilter filter = new XLogFilter(new 
XLogUserFilterParam(params));
@@ -314,8 +319,14 @@ public class CoordinatorEngine extends BaseEngine {
             if (lastTime == null) {
                 lastTime = new Date();
             }
-            Services.get().get(XLogStreamingService.class)
-                    .streamLog(filter, job.getCreatedTime(), lastTime, writer, 
params);
+            if (isErrorLog) {
+                Services.get().get(XLogStreamingService.class)
+                        .streamErrorLog(filter, job.getCreatedTime(), 
lastTime, writer, params);
+            }
+            else {
+                Services.get().get(XLogStreamingService.class)
+                        .streamLog(filter, job.getCreatedTime(), lastTime, 
writer, params);
+            }
         }
         catch (Exception e) {
             throw new IOException(e);

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/DagEngine.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/DagEngine.java 
b/core/src/main/java/org/apache/oozie/DagEngine.java
index 70ddd44..cd90390 100644
--- a/core/src/main/java/org/apache/oozie/DagEngine.java
+++ b/core/src/main/java/org/apache/oozie/DagEngine.java
@@ -379,7 +379,7 @@ public class DagEngine extends BaseEngine {
     @Override
     public String getDefinition(String jobId) throws DagEngineException {
         try {
-                       return new DefinitionXCommand(jobId).call();
+            return new DefinitionXCommand(jobId).call();
         }
         catch (CommandException ex) {
             throw new DagEngineException(ex);
@@ -398,6 +398,25 @@ public class DagEngine extends BaseEngine {
     @Override
     public void streamLog(String jobId, Writer writer, Map<String, String[]> 
params) throws IOException,
             DagEngineException {
+        streamJobLog(jobId, writer, params, false);
+    }
+
+    /**
+     * Stream the error log of a job.
+     *
+     * @param jobId job Id.
+     * @param writer writer to stream the log to.
+     * @param params additional parameters from the request
+     * @throws IOException thrown if the log cannot be streamed.
+     * @throws DagEngineException thrown if there is error in getting the 
Workflow Information for jobId.
+     */
+    public void streamErrorLog(String jobId, Writer writer, Map<String, 
String[]> params) throws IOException,
+            DagEngineException {
+        streamJobLog(jobId, writer, params, true);
+    }
+
+    public void streamJobLog(String jobId, Writer writer, Map<String, 
String[]> params, boolean isErrorLog)
+            throws IOException, DagEngineException {
         try {
             XLogFilter filter = new XLogFilter(new 
XLogUserFilterParam(params));
             filter.setParameter(DagXLogInfoService.JOB, jobId);
@@ -406,7 +425,15 @@ public class DagEngine extends BaseEngine {
             if (lastTime == null) {
                 lastTime = job.getLastModifiedTime();
             }
-            Services.get().get(XLogStreamingService.class).streamLog(filter, 
job.getCreatedTime(), lastTime, writer, params);
+            if (isErrorLog) {
+                Services.get().get(XLogStreamingService.class)
+                        .streamErrorLog(filter, job.getCreatedTime(), 
lastTime, writer, params);
+            }
+            else {
+                Services.get().get(XLogStreamingService.class)
+                        .streamLog(filter, job.getCreatedTime(), lastTime, 
writer, params);
+            }
+
         }
         catch (Exception e) {
             throw new IOException(e);

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/service/XLogService.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/service/XLogService.java 
b/core/src/main/java/org/apache/oozie/service/XLogService.java
index e97c38a..c291fa8 100644
--- a/core/src/main/java/org/apache/oozie/service/XLogService.java
+++ b/core/src/main/java/org/apache/oozie/service/XLogService.java
@@ -15,7 +15,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.oozie.service;
 
 import org.apache.commons.logging.LogFactory;
@@ -37,7 +36,6 @@ import java.io.InputStream;
 import java.net.URL;
 import java.util.Properties;
 import java.util.Map;
-import java.util.regex.Pattern;
 
 /**
  * Built-in service that initializes and manages Logging via Log4j.
@@ -94,6 +92,7 @@ public class XLogService implements Service, Instrumentable {
     private boolean fromClasspath;
     private String log4jFileName;
     private boolean logOverWS = true;
+    private boolean errorLogEnable = true;
 
     private static final String STARTUP_MESSAGE = "{E}"
             + " 
******************************************************************************* 
{E}"
@@ -103,19 +102,32 @@ public class XLogService implements Service, 
Instrumentable {
 
     private String oozieLogPath;
     private String oozieLogName;
+    private String oozieErrorLogPath;
+    private String oozieErrorLogName;
     private int oozieLogRotation = -1;
+    private int oozieErrorLogRotation = -1;
+
 
     public XLogService() {
     }
-    
+
     public String getOozieLogPath() {
         return oozieLogPath;
     }
-    
+
+    public String getOozieErrorLogPath() {
+        return oozieErrorLogPath;
+    }
+
     public String getOozieLogName() {
         return oozieLogName;
     }
 
+    public String getOozieErrorLogName() {
+        return oozieErrorLogName;
+    }
+
+
     /**
      * Initialize the log service.
      *
@@ -190,7 +202,6 @@ public class XLogService implements Service, Instrumentable 
{
     }
 
     private void extractInfoForLogWebService(InputStream is) throws 
IOException {
-        logOverWS = true;
         Properties props = new Properties();
         props.load(is);
 
@@ -198,83 +209,18 @@ public class XLogService implements Service, 
Instrumentable {
         for (Map.Entry entry : props.entrySet()) {
             conf.set((String) entry.getKey(), (String) entry.getValue());
         }
-        String logFile = conf.get("log4j.appender.oozie.File");
-        if (logFile == null) {
-            log.warn("Oozie WS log will be disabled, missing property 
'log4j.appender.oozie.File' for 'oozie' "
-                    + "appender");
-            logOverWS = false;
-        }
-        else {
-            logFile = logFile.trim();
-            int i = logFile.lastIndexOf("/");
-            if (i == -1) {
-                log.warn("Oozie WS log will be disabled, log file is not an 
absolute path [{0}] for 'oozie' appender",
-                        logFile);
-                logOverWS = false;
-            }
-            else {
-                String appenderClass = conf.get("log4j.appender.oozie");
-                if (appenderClass == null) {
-                    log.warn("Oozie WS log will be disabled, missing property 
[log4j.appender.oozie]");
-                    logOverWS = false;
-                }
-                else if 
(appenderClass.equals("org.apache.log4j.DailyRollingFileAppender")) {
-                    String pattern = 
conf.get("log4j.appender.oozie.DatePattern");
-                    if (pattern == null) {
-                        log.warn("Oozie WS log will be disabled, missing 
property [log4j.appender.oozie.DatePattern]");
-                        logOverWS = false;
-                    }
-                    else {
-                        pattern = pattern.trim();
-                        if (pattern.endsWith("HH")) {
-                            oozieLogRotation = 60 * 60;
-                        }
-                        else if (pattern.endsWith("dd")) {
-                                oozieLogRotation = 60 * 60 * 24;
-                        }
-                        else {
-                            log.warn("Oozie WS log will be disabled, 
DatePattern [{0}] should end with 'HH' or 'dd'",
-                                    pattern);
-                            logOverWS = false;
-                        }
-                        if (oozieLogRotation > 0) {
-                            oozieLogPath = logFile.substring(0, i);
-                            oozieLogName = logFile.substring(i + 1);
-                        }
-                    }
-                }
-                else if 
(appenderClass.equals("org.apache.log4j.rolling.RollingFileAppender")) {
-                    String pattern = 
conf.get("log4j.appender.oozie.RollingPolicy.FileNamePattern");
-                    if (pattern == null) {
-                        log.warn("Oozie WS log will be disabled, missing 
property "
-                                + 
"[log4j.appender.oozie.RollingPolicy.FileNamePattern]");
-                        logOverWS = false;
-                    }
-                    else {
-                        pattern = pattern.trim();
-                        if (pattern.matches(Pattern.quote(logFile) + 
".*-%d\\{yyyy-MM-dd-HH\\}(\\.gz)?")) {
-                            oozieLogRotation = 60 * 60;
-                        }
-                        else {
-                            log.warn("Oozie WS log will be disabled, 
RollingPolicy.FileNamePattern [{0}] should end with " 
-                                    + "'-%d{yyyy-MM-dd-HH}' or 
'-%d{yyyy-MM-dd-HH}.gz' and also start with the value of "
-                                    + "log4j.appender.oozie.File [{1}]", 
pattern, logFile);
-                            logOverWS = false;
-                        }
-                        if (oozieLogRotation > 0) {
-                            oozieLogPath = logFile.substring(0, i);
-                            oozieLogName = logFile.substring(i + 1);
-                        }
-                    }
-                }
-                else {
-                    log.warn("Oozie WS log will be disabled, 
log4j.appender.oozie [" + appenderClass + "] should be "
-                            + "either 
org.apache.log4j.DailyRollingFileAppender or 
org.apache.log4j.rolling.RollingFileAppender "
-                            + "to enable it");
-                    logOverWS = false;
-                }
-            }
-        }
+
+        XLogUtil logUtil = new XLogUtil(conf, "oozie");
+        logOverWS = logUtil.isLogOverEnable();
+        oozieLogRotation = logUtil.getLogRotation() == 0 ? oozieLogRotation : 
logUtil.getLogRotation();
+        oozieLogPath = logUtil.getLogPath() == null ? oozieLogPath : 
logUtil.getLogPath();
+        oozieLogName = logUtil.getLogFileName() == null ? oozieLogName : 
logUtil.getLogFileName();
+
+        logUtil = new XLogUtil(conf, "oozieError");
+        errorLogEnable = logUtil.isLogOverEnable();
+        oozieErrorLogRotation = logUtil.getLogRotation() == 0 ? 
oozieErrorLogRotation : logUtil.getLogRotation();
+        oozieErrorLogPath = logUtil.getLogPath() == null ? oozieErrorLogPath : 
logUtil.getLogPath();
+        oozieErrorLogName = logUtil.getLogFileName() == null ? 
oozieErrorLogName : logUtil.getLogFileName();
     }
 
     /**
@@ -313,6 +259,7 @@ public class XLogService implements Service, Instrumentable 
{
      * @param instr instrumentation to use.
      */
     public void instrument(Instrumentation instr) {
+
         instr.addVariable(INSTRUMENTATION_GROUP, "config.file", new 
Instrumentation.Variable<String>() {
             public String getValue() {
                 return log4jFileName;
@@ -339,9 +286,17 @@ public class XLogService implements Service, 
Instrumentable {
         return logOverWS;
     }
 
+    boolean isErrorLogEnable(){
+        return errorLogEnable;
+    }
+
     int getOozieLogRotation() {
         return oozieLogRotation;
     }
+    int getOozieErrorLogRotation() {
+        return oozieErrorLogRotation;
+    }
+
 
     String getLog4jProperties() {
         return log4jFileName;

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/service/XLogStreamingService.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/oozie/service/XLogStreamingService.java 
b/core/src/main/java/org/apache/oozie/service/XLogStreamingService.java
index 9a42f2f..9066c7a 100644
--- a/core/src/main/java/org/apache/oozie/service/XLogStreamingService.java
+++ b/core/src/main/java/org/apache/oozie/service/XLogStreamingService.java
@@ -93,6 +93,29 @@ public class XLogStreamingService implements Service, 
Instrumentable {
         }
     }
 
+    /**
+     * Stream the error log of a job.
+     *
+     * @param filter log streamer filter.
+     * @param startTime start time for log events to filter.
+     * @param endTime end time for log events to filter.
+     * @param writer writer to stream the log to.
+     * @param params additional parameters from the request
+     * @throws IOException thrown if the log cannot be streamed.
+     */
+    public void streamErrorLog(XLogFilter filter, Date startTime, Date 
endTime, Writer writer, Map<String, String[]> params)
+            throws IOException {
+        XLogService xLogService = Services.get().get(XLogService.class);
+        if (xLogService.isErrorLogEnable()) {
+            new XLogStreamer(filter, xLogService.getOozieErrorLogPath(), 
xLogService.getOozieErrorLogName(),
+                    xLogService.getOozieErrorLogRotation()).streamLog(writer, 
startTime, endTime, bufferLen);
+        }
+        else {
+            writer.write("Error Log is disabled!!");
+        }
+    }
+
+
     public int getBufferLen() {
         return bufferLen;
     }

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/service/XLogUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/service/XLogUtil.java 
b/core/src/main/java/org/apache/oozie/service/XLogUtil.java
new file mode 100644
index 0000000..59a4d22
--- /dev/null
+++ b/core/src/main/java/org/apache/oozie/service/XLogUtil.java
@@ -0,0 +1,144 @@
+/**
+ * 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.oozie.service;
+
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.oozie.util.XLog;
+
+public class XLogUtil {
+
+    private String logPath;
+    private String logFileName;
+    private boolean isLogOverEnable = true;
+    private int logRotation;
+
+    public static XLog log = new XLog(LogFactory.getLog(XLogUtil.class));
+
+    public XLogUtil(Configuration conf, String logType) {
+        extractInfoForLogWebService(conf, logType);
+    }
+
+    private void extractInfoForLogWebService(Configuration conf, String 
logType) {
+        String logFile = conf.get("log4j.appender." + logType + ".File");
+        if (logFile == null) {
+            log.warn("Oozie WS " + logType
+                    + " log will be disabled, missing property 
'log4j.appender.oozie.File' for 'oozie' " + "appender");
+            isLogOverEnable = false;
+        }
+        else {
+            logFile = logFile.trim();
+            int i = logFile.lastIndexOf("/");
+            if (i == -1) {
+                log.warn("Oozie WS " + logType
+                        + " log will be disabled, log file is not an absolute 
path [{0}] for 'oozie' appender", logFile);
+                isLogOverEnable = false;
+            }
+            else {
+                String appenderClass = conf.get("log4j.appender." + logType);
+                if (appenderClass == null) {
+                    log.warn("Oozie WS " + logType + " log will be disabled, 
missing property [log4j.appender.oozie]");
+                    isLogOverEnable = false;
+                }
+                else if 
(appenderClass.equals("org.apache.log4j.DailyRollingFileAppender")) {
+                    String pattern = conf.get("log4j.appender." + logType + 
".DatePattern");
+                    if (pattern == null) {
+                        log.warn("Oozie WS " + logType
+                                + " log will be disabled, missing property 
[log4j.appender." + logType + ".DatePattern]");
+                        isLogOverEnable = false;
+                    }
+                    else {
+                        pattern = pattern.trim();
+                        if (pattern.endsWith("HH")) {
+                            logRotation = 60 * 60;
+                        }
+                        else if (pattern.endsWith("dd")) {
+                            logRotation = 60 * 60 * 24;
+                        }
+                        else {
+                            log.warn("Oozie WS " + logType
+                                    + " log will be disabled, DatePattern 
[{0}] should end with 'HH' or 'dd'", pattern);
+                            isLogOverEnable = false;
+                        }
+                        if (logRotation > 0) {
+                            logPath = logFile.substring(0, i);
+                            logFileName = logFile.substring(i + 1);
+                        }
+                    }
+                }
+                else if 
(appenderClass.equals("org.apache.log4j.rolling.RollingFileAppender")) {
+                    String pattern = conf.get("log4j.appender." + logType + 
".RollingPolicy.FileNamePattern");
+                    if (pattern == null) {
+                        log.warn("Oozie WS " + logType + " log will be 
disabled, missing property "
+                                + "[log4j.appender." + logType + 
".RollingPolicy.FileNamePattern]");
+                        isLogOverEnable = false;
+                    }
+                    else {
+                        pattern = pattern.trim();
+                        if (pattern.matches(Pattern.quote(logFile) + 
".*-%d\\{yyyy-MM-dd-HH\\}(\\.gz)?")) {
+                            logRotation = 60 * 60;
+                        }
+                        else {
+                            log.warn(
+                                    "Oozie WS "
+                                            + logType
+                                            + " log will be disabled, 
RollingPolicy.FileNamePattern [{0}] should end with "
+                                            + "'-%d{yyyy-MM-dd-HH}' or 
'-%d{yyyy-MM-dd-HH}.gz' and also start with the value of "
+                                            + "log4j.appender." + logType + 
".File [{1}]", pattern, logFile);
+                            isLogOverEnable = false;
+                        }
+                        if (logRotation > 0) {
+                            logPath = logFile.substring(0, i);
+                            logFileName = logFile.substring(i + 1);
+                        }
+                    }
+                }
+                else {
+                    log.warn("Oozie WS "
+                            + logType
+                            + " log will be disabled, log4j.appender.oozie ["
+                            + appenderClass
+                            + "] should be "
+                            + "either 
org.apache.log4j.DailyRollingFileAppender or 
org.apache.log4j.rolling.RollingFileAppender "
+                            + "to enable it");
+                    isLogOverEnable = false;
+                }
+            }
+        }
+    }
+
+    public String getLogPath() {
+        return logPath;
+    }
+
+    public String getLogFileName() {
+        return logFileName;
+    }
+
+    public boolean isLogOverEnable() {
+        return isLogOverEnable;
+    }
+
+    public int getLogRotation() {
+        return logRotation;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/service/ZKXLogStreamingService.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/oozie/service/ZKXLogStreamingService.java 
b/core/src/main/java/org/apache/oozie/service/ZKXLogStreamingService.java
index 94d8cb3..b28a591 100644
--- a/core/src/main/java/org/apache/oozie/service/ZKXLogStreamingService.java
+++ b/core/src/main/java/org/apache/oozie/service/ZKXLogStreamingService.java
@@ -15,7 +15,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.oozie.service;
 
 import java.io.BufferedReader;
@@ -114,7 +113,8 @@ public class ZKXLogStreamingService extends 
XLogStreamingService implements Serv
             }
             // Otherwise, we have to go collate relevant logs from the other 
Oozie servers
             else {
-                collateLogs(filter, startTime, endTime, writer, params);
+                collateLogs(filter, startTime, endTime, writer, params, 
xLogService.getOozieLogPath(),
+                        xLogService.getOozieLogName(), 
xLogService.getOozieLogRotation(), RestConstants.JOB_SHOW_LOG);
             }
         }
         else {
@@ -123,19 +123,56 @@ public class ZKXLogStreamingService extends 
XLogStreamingService implements Serv
     }
 
     /**
+     * Stream the error log of a job.  It contacts any other running Oozie 
servers to collate relevant error logs while streaming.
+     *
+     * @param filter log streamer filter.
+     * @param startTime start time for log events to filter.
+     * @param endTime end time for log events to filter.
+     * @param writer writer to stream the log to.
+     * @param params additional parameters from the request
+     * @throws IOException thrown if the log cannot be streamed.
+     */
+    public void streamErrorLog(XLogFilter filter, Date startTime, Date 
endTime, Writer writer, Map<String, String[]> params)
+            throws IOException {
+        XLogService xLogService = Services.get().get(XLogService.class);
+        if (xLogService.isErrorLogEnable()) {
+            // If ALL_SERVERS_PARAM is set to false, then only stream our log
+            if 
(!Services.get().get(JobsConcurrencyService.class).isAllServerRequest(params)) {
+                new XLogStreamer(filter, xLogService.getOozieErrorLogPath(), 
xLogService.getOozieErrorLogName(),
+                        
xLogService.getOozieErrorLogRotation()).streamLog(writer, startTime, endTime, 
bufferLen);
+            }
+            // Otherwise, we have to go collate relevant logs from the other 
Oozie servers
+            else {
+                collateLogs(filter, startTime, endTime, writer, params, 
xLogService.getOozieLogPath(),
+                        xLogService.getOozieErrorLogName(), 
xLogService.getOozieErrorLogRotation(),
+                        RestConstants.JOB_SHOW_ERROR_LOG);
+            }
+        }
+        else {
+            writer.write("Error Log streaming disabled!!");
+        }
+    }
+
+
+    /**
      * Contacts each of the other Oozie servers, gets their logs for the job, 
collates them, and sends them to the user via the
      * Writer.  It will make sure to not read all of the log messages into 
memory at the same time to not use up the heap.  If there
      * is a problem talking to one of the other servers, it will ignore that 
server and prepend a message to the Writer about it.
      * For getting the logs from this server, it won't use the REST API and 
instead get them directly to be more efficient.
      *
-     * @param filter
-     * @param startTime
-     * @param endTime
-     * @param writer
-     * @throws IOException
+     * @param filter the job filter
+     * @param startTime the job start time
+     * @param endTime the job end time
+     * @param writer the writer
+     * @param params the params
+     * @param logPath the log path
+     * @param logName the log name
+     * @param rotation the rotation
+     * @param logType the log type
+     * @throws IOException Signals that an I/O exception has occurred.
      */
     private void collateLogs(XLogFilter filter, Date startTime, Date endTime, 
Writer writer,
-            Map<String, String[]> params) throws IOException {
+            Map<String, String[]> params, String logPath, String logName, int 
rotation, final String logType) throws IOException {
         XLogService xLogService = Services.get().get(XLogService.class);
         List<String> badOozies = new ArrayList<String>();
         List<ServiceInstance<Map>> oozies = null;
@@ -153,8 +190,8 @@ public class ZKXLogStreamingService extends 
XLogStreamingService implements Serv
                 String otherId = 
oozieMeta.get(ZKUtils.ZKMetadataKeys.OOZIE_ID);
                 // If it's this server, we can just get them directly
                 if (otherId.equals(zk.getZKId())) {
-                    BufferedReader reader = new XLogStreamer(filter, 
xLogService.getOozieLogPath(), xLogService.getOozieLogName(),
-                                                             
xLogService.getOozieLogRotation()).makeReader(startTime, endTime);
+                    BufferedReader reader = new XLogStreamer(filter, logPath, 
logName, rotation).makeReader(startTime,
+                            endTime);
                     parsers.add(new TimestampedMessageParser(reader, filter));
                 }
                 // If it's another server, we'll have to use the REST API
@@ -165,7 +202,7 @@ public class ZKXLogStreamingService extends 
XLogStreamingService implements Serv
                      // It's important that we specify ALL_SERVERS_PARAM=false 
in the GET request to prevent the other Oozie
                      // Server from trying aggregate logs from the other Oozie 
servers (and creating an infinite recursion)
                         final String url = otherUrl + "/v" + 
OozieClient.WS_PROTOCOL_VERSION + "/" + RestConstants.JOB
-                                + "/" + jobId + "?" + 
RestConstants.JOB_SHOW_PARAM + "=" + RestConstants.JOB_SHOW_LOG
+                                + "/" + jobId + "?" + 
RestConstants.JOB_SHOW_PARAM + "=" + logType
                                 + "&" + RestConstants.ALL_SERVER_REQUEST + 
"=false" + AuthUrlClient.getQueryParamString(params);
 
                         BufferedReader reader = AuthUrlClient.callServer(url);

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/servlet/BaseJobServlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/servlet/BaseJobServlet.java 
b/core/src/main/java/org/apache/oozie/servlet/BaseJobServlet.java
index c94d1e2..5690787 100644
--- a/core/src/main/java/org/apache/oozie/servlet/BaseJobServlet.java
+++ b/core/src/main/java/org/apache/oozie/servlet/BaseJobServlet.java
@@ -295,6 +295,10 @@ public abstract class BaseJobServlet extends 
JsonRestServlet {
             response.setContentType(TEXT_UTF8);
             streamJobLog(request, response);
         }
+        else if (show.equals(RestConstants.JOB_SHOW_ERROR_LOG)) {
+            response.setContentType(TEXT_UTF8);
+            streamJobErrorLog(request, response);
+        }
         else if (show.equals(RestConstants.JOB_SHOW_DEFINITION)) {
             stopCron();
             response.setContentType(XML_UTF8);
@@ -426,6 +430,18 @@ public abstract class BaseJobServlet extends 
JsonRestServlet {
             IOException;
 
     /**
+     * abstract method to get and stream error log information of job, either 
workflow, coordinator or bundle
+     *
+     * @param request
+     * @param response
+     * @throws XServletException
+     * @throws IOException
+     */
+    abstract void streamJobErrorLog(HttpServletRequest request, 
HttpServletResponse response) throws XServletException,
+    IOException;
+
+
+    /**
      * abstract method to create and stream image for runtime DAG -- workflow 
only
      *
      * @param request
@@ -482,5 +498,4 @@ public abstract class BaseJobServlet extends 
JsonRestServlet {
      */
     abstract String getJobStatus(HttpServletRequest request, 
HttpServletResponse response)
             throws XServletException, IOException;
-}
-
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/servlet/V0JobServlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/servlet/V0JobServlet.java 
b/core/src/main/java/org/apache/oozie/servlet/V0JobServlet.java
index b160b46..3e186f9 100644
--- a/core/src/main/java/org/apache/oozie/servlet/V0JobServlet.java
+++ b/core/src/main/java/org/apache/oozie/servlet/V0JobServlet.java
@@ -194,6 +194,13 @@ public class V0JobServlet extends BaseJobServlet {
         }
     }
 
+    @Override
+    protected void streamJobErrorLog(HttpServletRequest request, 
HttpServletResponse response) throws XServletException,
+            IOException {
+        throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, 
ErrorCode.E0306);
+    }
+
+
     /*
      * Not implemented in v0
      */

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/servlet/V1JobServlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/servlet/V1JobServlet.java 
b/core/src/main/java/org/apache/oozie/servlet/V1JobServlet.java
index eed7ca1..64b97c2 100644
--- a/core/src/main/java/org/apache/oozie/servlet/V1JobServlet.java
+++ b/core/src/main/java/org/apache/oozie/servlet/V1JobServlet.java
@@ -1097,4 +1097,10 @@ public class V1JobServlet extends BaseJobServlet {
             IOException {
         throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, 
ErrorCode.E0302, "Not supported in v1");
     }
+
+    @Override
+    protected void streamJobErrorLog(HttpServletRequest request, 
HttpServletResponse response) throws XServletException,
+            IOException {
+        throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, 
ErrorCode.E0302, "Not supported in v1");
+    }
 }

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/servlet/V2JobServlet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/servlet/V2JobServlet.java 
b/core/src/main/java/org/apache/oozie/servlet/V2JobServlet.java
index da81b49..5238426 100644
--- a/core/src/main/java/org/apache/oozie/servlet/V2JobServlet.java
+++ b/core/src/main/java/org/apache/oozie/servlet/V2JobServlet.java
@@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.oozie.BaseEngine;
 import org.apache.oozie.BaseEngineException;
 import org.apache.oozie.BundleEngine;
 import org.apache.oozie.CoordinatorActionBean;
@@ -218,4 +219,44 @@ public class V2JobServlet extends V1JobServlet {
         }
         return status;
     }
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void streamJobErrorLog(HttpServletRequest request, 
HttpServletResponse response) throws XServletException,
+            IOException {
+
+        String jobId = getResourceName(request);
+        try {
+            getBaseEngine(jobId, getUser(request)).streamErrorLog(jobId, 
response.getWriter(), request.getParameterMap());
+        }
+        catch (DagEngineException ex) {
+            throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, 
ex);
+        }
+        catch (BaseEngineException e) {
+            throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, e);
+        }
+
+    }
+
+    /**
+     * Gets the base engine based on jobId.
+     *
+     * @param jobId the jobId
+     * @param user the user
+     * @return the baseEngine
+     */
+    final public BaseEngine getBaseEngine(String jobId, String user) {
+        if (jobId.endsWith("-W")) {
+            return 
Services.get().get(DagEngineService.class).getDagEngine(user);
+        }
+        else if (jobId.endsWith("-B")) {
+            return 
Services.get().get(BundleEngineService.class).getBundleEngine(user);
+        }
+        else if (jobId.endsWith("-C")) {
+            return 
Services.get().get(CoordinatorEngineService.class).getCoordinatorEngine(user);
+        }
+        else {
+            throw new RuntimeException("Unknown job Type");
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/main/java/org/apache/oozie/util/XLogUserFilterParam.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/util/XLogUserFilterParam.java 
b/core/src/main/java/org/apache/oozie/util/XLogUserFilterParam.java
index af9b979..a00a79f 100644
--- a/core/src/main/java/org/apache/oozie/util/XLogUserFilterParam.java
+++ b/core/src/main/java/org/apache/oozie/util/XLogUserFilterParam.java
@@ -109,7 +109,7 @@ public class XLogUserFilterParam {
     private void parseFilterParam(String param) throws Exception {
         this.params = param;
 
-        if (StringUtils.isEmpty(param)) {
+        if (StringUtils.isEmpty(param) || StringUtils.equalsIgnoreCase(param, 
"null")) {
             return;
         }
         for (String keyValue : param.split(";")) {

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/test/java/org/apache/oozie/service/TestXLogStreamingService.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/oozie/service/TestXLogStreamingService.java 
b/core/src/test/java/org/apache/oozie/service/TestXLogStreamingService.java
index 5ad6dea..aedd35c 100644
--- a/core/src/test/java/org/apache/oozie/service/TestXLogStreamingService.java
+++ b/core/src/test/java/org/apache/oozie/service/TestXLogStreamingService.java
@@ -21,7 +21,6 @@ package org.apache.oozie.service;
 import org.apache.commons.logging.LogFactory;
 import org.apache.oozie.test.XTestCase;
 import org.apache.oozie.util.XLogFilter;
-import org.apache.oozie.util.IOUtils;
 import org.apache.oozie.util.XLogUserFilterParam;
 
 import java.io.File;
@@ -129,6 +128,95 @@ public class TestXLogStreamingService extends XTestCase {
         assertFalse(doStreamDisabledCheckWithServices());
     }
 
+    public void testDisableErrorLogOverWS() throws Exception {
+        Properties props = new Properties();
+        // Test missing logfile
+        props.setProperty("log4j.appender.oozieError.File", "");
+        File propsFile = new File(getTestCaseConfDir(), 
"test-disable-log-over-ws-log4j.properties");
+        FileOutputStream fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        setSystemProperty(XLogService.LOG4J_FILE, propsFile.getName());
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test non-absolute path for logfile
+        props.setProperty("log4j.appender.oozieError.File", "oozie-error.log");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test missing appender class
+        props.setProperty("log4j.appender.oozieError.File", 
"${oozie.log.dir}/oozie-error.log");
+        props.setProperty("log4j.appender.oozieError", "");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test appender class not DailyRollingFileAppender or 
RollingFileAppender
+        props.setProperty("log4j.appender.oozieError", "org.blah.blah");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test DailyRollingFileAppender but missing DatePattern
+        props.setProperty("log4j.appender.oozieError", 
"org.apache.log4j.DailyRollingFileAppender");
+        props.setProperty("log4j.appender.oozieError.DatePattern", "");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test DailyRollingFileAppender but DatePattern that doesn't end with 
'HH' or 'dd'
+        props.setProperty("log4j.appender.oozieError.DatePattern", 
"'.'yyyy-MM");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test DailyRollingFileAppender with everything correct (dd)
+        props.setProperty("log4j.appender.oozieError.DatePattern", 
"'.'yyyy-MM-dd");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertFalse(doerrorStreamDisabledCheckWithServices());
+
+        // Test DailyRollingFileAppender with everything correct (HH)
+        props.setProperty("log4j.appender.oozieError.DatePattern", 
"'.'yyyy-MM-dd-HH");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertFalse(doerrorStreamDisabledCheckWithServices());
+
+        // Test RollingFileAppender but missing FileNamePattern
+        props.setProperty("log4j.appender.oozieError", 
"org.apache.log4j.rolling.RollingFileAppender");
+        
props.setProperty("log4j.appender.oozieError.RollingPolicy.FileNamePattern", 
"");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test RollingFileAppender but FileNamePattern with incorrect ending
+        
props.setProperty("log4j.appender.oozieError.RollingPolicy.FileNamePattern", 
"${oozie.log.dir}/oozie-error.log-blah");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test RollingFileAppender but FileNamePattern with incorrect 
beginning
+        
props.setProperty("log4j.appender.oozieError.RollingPolicy.FileNamePattern",
+                "${oozie.log.dir}/blah.log-%d{yyyy-MM-dd-HH}");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertTrue(doerrorStreamDisabledCheckWithServices());
+
+        // Test RollingFileAppender with everything correct
+        
props.setProperty("log4j.appender.oozieError.RollingPolicy.FileNamePattern",
+                "${oozie.log.dir}/oozie-error.log-%d{yyyy-MM-dd-HH}");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertFalse(doerrorStreamDisabledCheckWithServices());
+
+        // Test RollingFileAppender with everything correct (gz)
+        
props.setProperty("log4j.appender.oozieError.RollingPolicy.FileNamePattern",
+                "${oozie.log.dir}/oozie-error.log-%d{yyyy-MM-dd-HH}.gz");
+        fos = new FileOutputStream(propsFile);
+        props.store(fos, "");
+        assertFalse(doerrorStreamDisabledCheckWithServices());
+    }
+
     public void testNoDashInConversionPattern() throws Exception{
         XLogFilter.reset();
         XLogFilter.defineParameter("USER");
@@ -177,6 +265,79 @@ public class TestXLogStreamingService extends XTestCase {
         }
     }
 
+    public void testErrorLog() throws Exception{
+        XLogFilter.reset();
+        XLogFilter.defineParameter("USER");
+        XLogFilter.defineParameter("GROUP");
+        XLogFilter.defineParameter("TOKEN");
+        XLogFilter.defineParameter("APP");
+        XLogFilter.defineParameter("JOB");
+        XLogFilter.defineParameter("ACTION");
+        XLogFilter xf = new XLogFilter(new XLogUserFilterParam(null));
+
+        xf.setParameter("USER", "oozie");
+        xf.setLogLevel("DEBUG|INFO");
+        // Previously, a dash ("-") was always required somewhere in a line in 
order for that line to pass the filter; this test
+        // checks that this condition is no longer required for log streaming 
to work
+        File log4jFile = new File(getTestCaseConfDir(), 
"test-log4j.properties");
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        InputStream is = 
cl.getResourceAsStream("test-no-dash-log4j.properties");
+        Properties log4jProps = new Properties();
+        log4jProps.load(is);
+        // prevent conflicts with other tests by changing the log file location
+        log4jProps.setProperty("log4j.appender.oozie.File", getTestCaseDir() + 
"/oozie.log");
+        log4jProps.setProperty("log4j.appender.oozieError.File", 
getTestCaseDir() + "/oozie-error.log");
+
+        log4jProps.store(new FileOutputStream(log4jFile), "");
+        setSystemProperty(XLogService.LOG4J_FILE, log4jFile.getName());
+        try {
+            new Services().init();
+            assertFalse(doStreamDisabledCheck());
+            LogFactory.getLog("a").info("2009-06-24 02:43:14,505 INFO _L1_:317 
- SERVER[foo] USER[oozie] GROUP[oozie] TOKEN[-] "
+                    + "APP[-] JOB[-] ACTION[-] Released Lock");
+            LogFactory.getLog("a").warn("2009-06-24 02:44:14,505 WARN _L2_:317 
- SERVER[foo] USER[oozie] GROUP[oozie] TOKEN[-] "
+                    + "APP[-] JOB[-] ACTION[-] Error while writing to file");
+            LogFactory.getLog("a").info("2009-06-24 02:45:14,505 INFO _L3_:317 
- SERVER[foo] USER[blah] GROUP[oozie] TOKEN[-]"
+                    + "APP[-] JOB[-] ACTION[-] Released Lock");
+            LogFactory.getLog("a").error("2009-06-24 02:46:14,505 ERROR 
_L4_:317 - SERVER[foo] USER[oozie] GROUP[oozie] TOKEN[-] "
+                    + "APP[-] JOB[-] ACTION[-] Error while writing to DB");
+            LogFactory.getLog("a").info("2009-06-24 02:47:14,505 INFO _L5_:317 
SERVER[foo] USER[oozie] GROUP[oozie] TOKEN[-] "
+                    + "APP[-] JOB[-] ACTION[-] Released Lock");
+            LogFactory.getLog("a").fatal("2009-06-24 02:48:14,505 FATAL 
_L6_:317 SERVER[foo] USER[blah] GROUP[oozie] TOKEN[-] "
+                    + "APP[-] JOB[-] ACTION[-] Released Lock");
+            String out = doStreamLog(xf);
+            String outArr[] = out.split("\n");
+            // Lines 2 and 4 are filtered out because they have the wrong user
+            assertEquals(2, outArr.length);
+            assertTrue(outArr[0].contains("_L1_"));
+            assertFalse(out.contains("_L3_"));
+            assertTrue(outArr[1].contains("_L5_"));
+            assertFalse(out.contains("_L4_"));
+            XLogFilter.reset();
+            XLogFilter.defineParameter("USER");
+            XLogFilter.defineParameter("GROUP");
+            XLogFilter.defineParameter("TOKEN");
+            XLogFilter.defineParameter("APP");
+            XLogFilter.defineParameter("JOB");
+            XLogFilter.defineParameter("ACTION");
+            xf = new XLogFilter(new XLogUserFilterParam(null));
+
+            xf.setParameter("USER", "oozie");
+
+            String errorOut = doStreamErrorLog(xf);
+            String errorOutArr[] = errorOut.split("\n");
+            // Lines 2 and 4 are filtered out because they have the wrong user
+            assertEquals(2, errorOutArr.length);
+            assertTrue(errorOutArr[0].contains("_L2_"));
+            assertTrue(errorOutArr[1].contains("_L4_"));
+            assertFalse(errorOut.contains("_L6_"));
+        }
+        finally {
+            Services.get().destroy();
+        }
+    }
+
+
     private boolean doStreamDisabledCheckWithServices() throws Exception {
         boolean result = false;
         try {
@@ -200,4 +361,26 @@ public class TestXLogStreamingService extends XTestCase {
         Services.get().get(XLogStreamingService.class).streamLog(xf, null, 
null, w, new HashMap<String, String[]>());
         return w.toString();
     }
+    private String doStreamErrorLog(XLogFilter xf) throws Exception {
+        StringWriter w = new StringWriter();
+        Services.get().get(XLogStreamingService.class).streamErrorLog(xf, 
null, null, w, new HashMap<String, String[]>());
+        return w.toString();
+    }
+
+    private boolean doErrorStreamDisabledCheck() throws Exception {
+        XLogFilter xf = new XLogFilter(new XLogUserFilterParam(null));
+        return doStreamErrorLog(xf).equals("Error Log is disabled!!");
+    }
+
+    private boolean doerrorStreamDisabledCheckWithServices() throws Exception {
+        boolean result = false;
+        try {
+            new Services().init();
+            result = doErrorStreamDisabledCheck();
+        }
+        finally {
+            Services.get().destroy();
+        }
+        return result;
+    }
 }

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/test/java/org/apache/oozie/service/TestZKXLogStreamingService.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/oozie/service/TestZKXLogStreamingService.java 
b/core/src/test/java/org/apache/oozie/service/TestZKXLogStreamingService.java
index 2fd08b3..fca8d84 100644
--- 
a/core/src/test/java/org/apache/oozie/service/TestZKXLogStreamingService.java
+++ 
b/core/src/test/java/org/apache/oozie/service/TestZKXLogStreamingService.java
@@ -15,7 +15,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.oozie.service;
 
 import java.io.File;
@@ -34,7 +33,6 @@ import org.apache.oozie.test.EmbeddedServletContainer;
 import org.apache.oozie.test.ZKXTestCase;
 import org.apache.oozie.util.DateUtils;
 import org.apache.oozie.util.XLogFilter;
-import org.apache.oozie.util.IOUtils;
 import org.apache.oozie.util.ZKUtils;
 
 public class TestZKXLogStreamingService extends ZKXTestCase {
@@ -200,7 +198,15 @@ public class TestZKXLogStreamingService extends 
ZKXTestCase {
         return doStreamLog(xf, new HashMap<String, String[]>());
     }
 
+    protected String doStreamErrorLog(XLogFilter xf) throws Exception {
+        return doStreamLog(xf, new HashMap<String, String[]>(), true);
+    }
+
     protected String doStreamLog(XLogFilter xf, Map<String, String[]> param) 
throws Exception {
+        return doStreamLog(xf, param, false);
+    }
+
+    protected String doStreamLog(XLogFilter xf, Map<String, String[]> param, 
boolean isErrorLog) throws Exception {
         StringWriter w = new StringWriter();
         ZKXLogStreamingService zkxlss = new ZKXLogStreamingService();
         try {
@@ -208,7 +214,12 @@ public class TestZKXLogStreamingService extends 
ZKXTestCase {
             services.setService(ZKJobsConcurrencyService.class);
             zkxlss.init(services);
             sleep(1000); // Sleep to allow ZKUtils ServiceCache to update
-            zkxlss.streamLog(xf, null, null, w, param);
+            if (isErrorLog) {
+                zkxlss.streamErrorLog(xf, null, null, w, param);
+            }
+            else {
+                zkxlss.streamLog(xf, null, null, w, param);
+            }
         }
         finally {
             zkxlss.destroy();
@@ -463,4 +474,113 @@ public class TestZKXLogStreamingService extends 
ZKXTestCase {
         }
     }
 
+    public void testStreamingWithMultipleOozieServers_errorLog() throws 
Exception {
+        XLogFilter.reset();
+
+        File log4jFile = new File(getTestCaseConfDir(), 
"test-log4j.properties");
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        InputStream is = 
cl.getResourceAsStream("test-no-dash-log4j.properties");
+        Properties log4jProps = new Properties();
+        log4jProps.load(is);
+        // prevent conflicts with other tests by changing the log file location
+        log4jProps.setProperty("log4j.appender.oozie.File", getTestCaseDir() + 
"/oozie.log");
+        log4jProps.setProperty("log4j.appender.oozieError.File", 
getTestCaseDir() + "/oozie-error.log");
+
+        log4jProps.store(new FileOutputStream(log4jFile), "");
+        setSystemProperty(XLogService.LOG4J_FILE, log4jFile.getName());
+        Services.get().get(XLogService.class).init(Services.get());
+
+        File logFile = new 
File(Services.get().get(XLogService.class).getOozieErrorLogPath(), 
Services.get()
+                .get(XLogService.class).getOozieErrorLogName());
+        logFile.getParentFile().mkdirs();
+        FileWriter logWriter = new FileWriter(logFile);
+        // local logs
+        StringBuffer bf = new StringBuffer();
+        bf.append(
+                "2014-02-06 00:26:56,126 WARN 
CoordActionInputCheckXCommand:545 [pool-2-thread-26] - USER[-] GROUP[-] "
+                     + "TOKEN[-] APP[-] 
JOB[0000003-140205233038063-oozie-oozi-C] 
ACTION[0000003-140205233038063-oozie-oozi-C@1] "
+                     + "checking for the file 
~:8020/user/purushah/examples/input-data/rawLogs/2010/01/01/01/00/_SUCCESS\n")
+                .append("2014-02-06 00:26:56,150  WARN 
CoordActionInputCheckXCommand:539 [pool-2-thread-26] - USER[-] GROUP[-] "
+                     + "TOKEN[-] APP[-] 
JOB[0000003-140205233038063-oozie-oozi-C] 
ACTION[0000003-140205233038063-oozie-oozi-C@1] "
+                     + 
"[0000003-140205233038063-oozie-oozi-C@1]::ActionInputCheck::File::8020/user/purushah/examples/input-data/"
+                     + "rawLogs/2010/01/01/01/00/_SUCCESS, Exists? :false" + 
"Action updated in DB! _L1_")
+                .append("\n")
+                .append("2014-02-06 00:27:56,126 WARN 
CoordActionInputCheckXCommand:545 [pool-2-thread-26] - USER[-] GROUP[-] "
+                     + "TOKEN[-] APP[-] 
JOB[0000003-140205233038063-oozie-oozi-C] 
ACTION[0000003-140205233038063-oozie-oozi-C@2] "
+                     + "checking for the file 
~:8020/user/purushah/examples/input-data/rawLogs/2010/01/01/01/00/_SUCCESS\n")
+                .append("2014-02-06 00:27:56,150  WARN 
CoordActionInputCheckXCommand:539 [pool-2-thread-26] - USER[-] GROUP[-] "
+                     + "TOKEN[-] APP[-] 
JOB[0000003-140205233038063-oozie-oozi-C] 
ACTION[0000003-140205233038063-oozie-oozi-C@2] "
+                     + 
"[0000003-140205233038063-oozie-oozi-C@2]::ActionInputCheck::File::8020/user/purushah/examples/input-data/"
+                     + "rawLogs/2010/01/01/01/00/_SUCCESS, Exists? :false" + 
"Action updated in DB! _L2_")
+                .append("\n");
+        logWriter.append(bf);
+
+        logWriter.close();
+
+        XLogFilter.reset();
+        XLogFilter.defineParameter("USER");
+        XLogFilter.defineParameter("GROUP");
+        XLogFilter.defineParameter("TOKEN");
+        XLogFilter.defineParameter("APP");
+        XLogFilter.defineParameter("JOB");
+        XLogFilter.defineParameter("ACTION");
+
+        XLogFilter xf = new XLogFilter();
+
+        xf.setParameter("USER", ".*");
+        xf.setParameter("GROUP", ".*");
+        xf.setParameter("TOKEN", ".*");
+        xf.setParameter("APP", ".*");
+        xf.setParameter("JOB", "0000003-140205233038063-oozie-oozi-C");
+        xf.setParameter(DagXLogInfoService.ACTION, 
"0000003-140205233038063-oozie-oozi-C@1");
+
+
+        String out = doStreamErrorLog(xf);
+        String[] outArr = out.split("\n");
+        assertEquals(2, outArr.length);
+        assertTrue(out.contains("_L1_"));
+        assertFalse(out.contains("_L2_"));
+
+
+        // We'll use a DummyZKOozie to create an entry in ZK and then set its
+        // url to an (unrelated) servlet that will simply return
+        // some log messages
+        DummyZKOozie dummyOozie = null;
+        EmbeddedServletContainer container = new 
EmbeddedServletContainer("oozie");
+        container.addServletEndpoint("/other-oozie-server/*", 
DummyLogStreamingServlet.class);
+        try {
+            container.start();
+            dummyOozie = new DummyZKOozie("9876", 
container.getServletURL("/other-oozie-server/*"));
+            StringBuffer newLog = new StringBuffer();
+
+            newLog.append(
+                    "2014-02-07 00:26:56,126 WARN 
CoordActionInputCheckXCommand:545 [pool-2-thread-26] - USER[-] GROUP[-] "
+                     + "TOKEN[-] APP[-] 
JOB[0000003-140205233038063-oozie-oozi-C] 
ACTION[0000003-140205233038063-oozie-oozi-C@1] "
+                     + "checking for the file 
~:8020/user/purushah/examples/input-data/rawLogs/2010/01/01/01/00/_SUCCESS\n")
+                 .append("2014-02-07 00:26:56,150  WARN 
CoordActionInputCheckXCommand:539 [pool-2-thread-26] - USER[-] GROUP[-] "
+                     + "TOKEN[-] APP[-] 
JOB[0000003-140205233038063-oozie-oozi-C] 
ACTION[0000003-140205233038063-oozie-oozi-C@1] "
+                     + 
"[0000003-140205233038063-oozie-oozi-C@1]::ActionInputCheck::File::8020/user/purushah/examples/input-data/"
+                     + "rawLogs/2010/01/01/01/00/_SUCCESS, Exists? :false" + 
"Action updated in DB! _L3_")
+                    .append("\n");
+
+            DummyLogStreamingServlet.logs = newLog.toString();
+
+
+            out = doStreamErrorLog(xf);
+            outArr = out.split("\n");
+            assertEquals(4, outArr.length);
+            assertTrue(out.contains("_L1_"));
+            assertTrue(out.contains("_L3_"));
+            assertFalse(out.contains("_L2_"));
+
+            container.stop();
+        }
+        finally {
+            if (dummyOozie != null) {
+                dummyOozie.teardown();
+            }
+            container.stop();
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/core/src/test/resources/test-no-dash-log4j.properties
----------------------------------------------------------------------
diff --git a/core/src/test/resources/test-no-dash-log4j.properties 
b/core/src/test/resources/test-no-dash-log4j.properties
index 36b7b18..2ff353e 100644
--- a/core/src/test/resources/test-no-dash-log4j.properties
+++ b/core/src/test/resources/test-no-dash-log4j.properties
@@ -27,4 +27,24 @@ log4j.appender.oozie.layout.ConversionPattern=%d{ISO8601} 
%5p %c{1}:%L %m%n
 
log4j.appender.oozie.RollingPolicy.FileNamePattern=${log4j.appender.oozie.File}-%d{yyyy-MM-dd-HH}
 log4j.appender.oozie.RollingPolicy.MaxHistory=720
 
-log4j.logger.a=ALL, oozie
+log4j.appender.oozieError=org.apache.log4j.rolling.RollingFileAppender
+log4j.appender.oozieError.RollingPolicy=org.apache.oozie.util.OozieRollingPolicy
+log4j.appender.oozieError.File=${oozie.log.dir}/oozie-error.log
+log4j.appender.oozieError.Append=true
+log4j.appender.oozieError.layout=org.apache.log4j.PatternLayout
+log4j.appender.oozieError.layout.ConversionPattern=%d{ISO8601} %5p %c{1}:%L - 
SERVER[${oozie.instance.id}] %m%n
+# The FileNamePattern must end with "-%d{yyyy-MM-dd-HH}.gz" or 
"-%d{yyyy-MM-dd-HH}" and also start with the
+# value of log4j.appender.oozieError.File
+log4j.appender.oozieError.RollingPolicy.FileNamePattern=${log4j.appender.oozieError.File}-%d{yyyy-MM-dd-HH}
+# The MaxHistory controls how many log files will be retained (720 hours / 24 
hours per day = 30 days); -1 to disable
+log4j.appender.oozieError.RollingPolicy.MaxHistory=720
+log4j.appender.oozieError.filter.1 = org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.oozieError.filter.1.levelToMatch = WARN
+log4j.appender.oozieError.filter.2 = org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.oozieError.filter.2.levelToMatch = ERROR
+log4j.appender.oozieError.filter.3 = org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.oozieError.filter.3.levelToMatch = FATAL
+log4j.appender.oozieError.filter.4 = org.apache.log4j.varia.DenyAllFilter
+
+
+log4j.logger.a=ALL, oozie, oozieError

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/docs/src/site/twiki/DG_CommandLineTool.twiki
----------------------------------------------------------------------
diff --git a/docs/src/site/twiki/DG_CommandLineTool.twiki 
b/docs/src/site/twiki/DG_CommandLineTool.twiki
index 4ac73f9..e801e31 100644
--- a/docs/src/site/twiki/DG_CommandLineTool.twiki
+++ b/docs/src/site/twiki/DG_CommandLineTool.twiki
@@ -606,6 +606,32 @@ $ oozie job -oozie http://localhost:11000/oozie -log 
14-20090525161321-oozie-joe
 
 </verbatim>
 
+---+++ Checking the server error logs of a Workflow, Coordinator or Bundle Job
+
+Example:
+
+<verbatim>
+$ oozie job -oozie http://localhost:11000/oozie -errorlog 
0000000-150121110331712-oozie-puru-B
+2015-01-21 11:33:29,090  WARN CoordSubmitXCommand:523 - SERVER[-] USER[-] 
GROUP[-] TOKEN[-] APP[-] JOB[0000000-150121110331712-oozie-puru-B] ACTION[] 
SAXException :
+org.xml.sax.SAXParseException; lineNumber: 20; columnNumber: 22; 
cvc-complex-type.2.4.a: Invalid content was found starting with element 
'concurrency'. One of '{"uri:oozie:coordinator:0.2":controls, 
"uri:oozie:coordinator:0.2":datasets, "uri:oozie:coordinator:0.2":input-events, 
"uri:oozie:coordinator:0.2":output-events, "uri:oozie:coordinator:0.2":action}' 
is expected.
+        at 
org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown 
Source)
+        at org.apache.xerces.util.ErrorHandlerWrapper.error(Unknown Source)
+        at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
+        at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
+        at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
+        at 
org.apache.xerces.impl.xs.XMLSchemaValidator$XSIErrorReporter.reportError(Unknown
 Source)
+        at 
org.apache.xerces.impl.xs.XMLSchemaValidator.reportSchemaError(Unknown Source)
+        at 
org.apache.xerces.impl.xs.XMLSchemaValidator.handleStartElement(Unknown Source)
+        at org.apache.xerces.impl.xs.XMLSchemaValidator.startElement(Unknown 
Source)
+        at 
org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
+        at 
org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown
 Source)
+        at 
org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown 
Source)
+        at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
+        at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
+        at 
org.apache.xerces.jaxp.validation.StreamValidatorHelper.validate(Unknown Source)
+        at org.apache.xerces.jaxp.validation.ValidatorImpl.validate(Unknown 
Source)
+</verbatim>
+
 ---+++ Checking the server logs for particular actions of a Coordinator Job
 
 Example:

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/docs/src/site/twiki/WebServicesAPI.twiki
----------------------------------------------------------------------
diff --git a/docs/src/site/twiki/WebServicesAPI.twiki 
b/docs/src/site/twiki/WebServicesAPI.twiki
index fc51934..fcefb4a 100644
--- a/docs/src/site/twiki/WebServicesAPI.twiki
+++ b/docs/src/site/twiki/WebServicesAPI.twiki
@@ -1431,6 +1431,29 @@ Content-Type: text/plain;charset=UTF-8
 ...
 </verbatim>
 
+---++++ Job Error Log
+
+An HTTP GET request retrieves the job error log.
+
+*Request:*
+
+<verbatim>
+GET /oozie/v2/job/0000000-150121110331712-oozie-puru-B?show=errorlog
+</verbatim>
+
+*Response:*
+
+<verbatim>
+HTTP/1.1 200 OK
+Content-Type: text/plain;charset=UTF-8
+2015-01-21 11:33:29,090  WARN CoordSubmitXCommand:523 - SERVER[-] USER[-] 
GROUP[-] TOKEN[-] APP[-] JOB[0000000-150121110331712-oozie-puru-B] ACTION[] 
SAXException :
+org.xml.sax.SAXParseException; lineNumber: 20; columnNumber: 22; 
cvc-complex-type.2.4.a: Invalid content was found starting with element 
'concurrency'. One of '{"uri:oozie:coordinator:0.2":controls, 
"uri:oozie:coordinator:0.2":datasets, "uri:oozie:coordinator:0.2":input-events, 
"uri:oozie:coordinator:0.2":output-events, "uri:oozie:coordinator:0.2":action}' 
is expected.
+        at 
org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown 
Source)
+        at org.apache.xerces.util.ErrorHandlerWrapper.error(Unknown Source)
+...
+</verbatim>
+
+
 ---++++ Filtering the server logs with logfilter options
 User can provide multiple option to filter logs using -logfilter 
opt1=val1;opt2=val1;opt3=val1. This can be used to fetch only just logs of 
interest faster as fetching Oozie server logs is slow due to the overhead of 
pattern matching.
 

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index 4c60c64..88ba9e3 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
 -- Oozie 4.2.0 release (trunk - unreleased)
 
+OOZIE-1894 Better error reporting to user (puru)
 OOZIE-2120 Many JPAExecutor names are wrong (rkanter)
 OOZIE-1722 Add support to Hadoop-2 for AM restarts of the launcher job 
(jaydeepvishwakarma via rkanter)
 OOZIE-2107 Schema config properties should be consistent with ActionExecutor 
config properties (rkanter)

http://git-wip-us.apache.org/repos/asf/oozie/blob/95d17f26/webapp/src/main/webapp/oozie-console.js
----------------------------------------------------------------------
diff --git a/webapp/src/main/webapp/oozie-console.js 
b/webapp/src/main/webapp/oozie-console.js
index 8f2098d..e2b37a6 100644
--- a/webapp/src/main/webapp/oozie-console.js
+++ b/webapp/src/main/webapp/oozie-console.js
@@ -45,13 +45,13 @@ Ext.override(Ext.Component, {
 
 function getLogs(url, searchFilter, logStatus, textArea, shouldParseResponse, 
errorMsg) {
     textArea.getEl().dom.value = '';
-    url = url + "&logfilter=" +searchFilter;
+    if (searchFilter) {
+        url = url + "&logfilter=" + searchFilter;
+    }
     logStatus.getEl().dom.innerText = "Log status : Loading... done";
 
     if (!errorMsg) {
         errorMsg = "Fatal Error. Can't load logs.";
-        logStatus.getEl().dom.innerText = "Log status : Errored out";
-
     }
     if (!window.XMLHttpRequest) {
         Ext.Ajax.request({
@@ -404,6 +404,17 @@ function jobDetailsPopup(response, request) {
         "Available options are recent, start, end, loglevel, text, limit and 
debug.\n" +
         "For more detail refer documentation 
(/oozie/docs/DG_CommandLineTool.html#Filtering_the_server_logs_with_logfilter_options)"
     });
+
+    var jobErrorLogArea = new Ext.form.TextArea({
+        fieldLabel: 'ErrorLogs',
+        editable: false,
+        name: 'errorlogs',
+        width: 1035,
+        height: 400,
+        autoScroll: true,
+        emptyText: ""
+    });
+
     function fetchDefinition(workflowId) {
         Ext.Ajax.request({
             url: getOozieBase() + 'job/' + workflowId + "?show=definition",
@@ -414,6 +425,10 @@ function jobDetailsPopup(response, request) {
 
         });
     }
+
+    function fetchErrorLogs(workflowId, errorLogStatus) {
+        getLogs(getOozieBase() + 'job/' + workflowId + "?show=errorlog", null, 
errorLogStatus, jobErrorLogArea, false, null);
+    }
     function fetchLogs(workflowId, logStatus) {
         getLogs(getOozieBase() + 'job/' + workflowId + "?show=log", 
searchFilterBox.getValue(), logStatus, jobLogArea, false, null);
 
@@ -816,6 +831,9 @@ function jobDetailsPopup(response, request) {
         text : 'Log Status : '
     });
 
+    var errorLogStatus = new Ext.form.Label({
+        text : 'Log Status : '
+    });
 
     var getLogButton = new Ext.Button({
         text: 'Get Logs',
@@ -826,6 +844,7 @@ function jobDetailsPopup(response, request) {
     });
 
     var isLoadedDAG = false;
+    var isErrorLogLoaded = false;
 
     var jobDetailsTab = new Ext.TabPanel({
         activeTab: 0,
@@ -853,7 +872,19 @@ function jobDetailsPopup(response, request) {
             title: 'Job Log',
             items: jobLogArea,
             tbar: [ searchFilter, searchFilterBox, getLogButton, {xtype: 
'tbfill'}, logStatus]
-        }, {
+        },
+        {
+            title: 'Job Error Log',
+            items: jobErrorLogArea,
+            tbar: [ {
+                text: "&nbsp;&nbsp;&nbsp;",
+                icon: 'ext-2.2/resources/images/default/grid/refresh.gif',
+                handler: function() {
+                    fetchErrorLogs(workflowId, errorLogStatus);
+                }
+            }, {xtype: 'tbfill'}, errorLogStatus]
+        },
+        {
             title: 'Job DAG',
             items: imageContainer,
             tbar: [{
@@ -873,6 +904,12 @@ function jobDetailsPopup(response, request) {
         }
         else if (selectedTab.title == 'Job Definition') {
             fetchDefinition(workflowId);
+        }
+        else if (selectedTab.title == 'Job Error Log') {
+            if(!isErrorLogLoaded){
+                fetchErrorLogs(workflowId, errorLogStatus);
+                isErrorLogLoaded=true;
+            }
         } else if(selectedTab.title == 'Job DAG') {
                 if(!isLoadedDAG){
                 var dagImage=   new Ext.ux.Image({
@@ -903,6 +940,7 @@ function jobDetailsPopup(response, request) {
 }
 
 function coordJobDetailsPopup(response, request) {
+    var isErrorLogLoaded= false;
 
     var jobDefinitionArea = new Ext.form.TextArea({
         fieldLabel: 'Definition',
@@ -927,6 +965,16 @@ function coordJobDetailsPopup(response, request) {
         "Available options are recent, start, end, loglevel, text, limit and 
debug.\n" +
         "For more detail refer documentation 
(/oozie/docs/DG_CommandLineTool.html#Filtering_the_server_logs_with_logfilter_options)"
     });
+    var jobErrorLogArea = new Ext.form.TextArea({
+        fieldLabel: 'ErrorLogs',
+        editable: false,
+        id: 'jobErrorLogAreaId',
+        name: 'errorlogs',
+        width: 1035,
+        height: 400,
+        autoScroll: true,
+        emptyText: ""
+    });
     var getLogButton = new Ext.Button({
         text: 'Get Logs',
         ctCls: 'x-btn-over',
@@ -959,8 +1007,9 @@ function coordJobDetailsPopup(response, request) {
     var logStatus = new Ext.form.Label({
         text : 'Log Status : '
     });
-
-
+    var errorLogStatus = new Ext.form.Label({
+        text : 'Log Status : '
+    });
     function fetchDefinition(coordJobId) {
         Ext.Ajax.request({
             url: getOozieBase() + 'job/' + coordJobId + "?show=definition",
@@ -981,6 +1030,11 @@ function coordJobDetailsPopup(response, request) {
                     'Action List format is wrong. Format should be similar to 
1,3-4,7-40');
         }
     }
+    function fetchErrorLogs(coordJobId) {
+            getLogs(getOozieBase() + 'job/' + coordJobId + "?show=errorlog",
+                    null, errorLogStatus, jobErrorLogArea, true, null);
+    }
+
 
     var jobDetails = eval("(" + response.responseText + ")");
     var coordJobId = jobDetails["coordJobId"];
@@ -1386,13 +1440,25 @@ function coordJobDetailsPopup(response, request) {
            items: jobLogArea,
            tbar: [
                   actionsText,actionsTextBox, searchFilter, searchFilterBox, 
getLogButton, {xtype: 'tbfill'}, logStatus]
-       },{
+       },
+       {
+           title: 'Coord Error Log',
+           items: jobErrorLogArea,
+           tbar: [ {
+               text: "&nbsp;&nbsp;&nbsp;",
+               icon: 'ext-2.2/resources/images/default/grid/refresh.gif',
+               handler: function() {
+                   fetchErrorLogs(coordJobId);
+               }
+           },{xtype: 'tbfill'}, errorLogStatus]
+       },
+       {
            title: 'Coord Action Reruns',
            items: rerunsUnit,
            tbar: [
                rerunActionText, rerunActionTextBox, getRerunsButton]
-       }]
-});
+       },
+    ]});
 
     jobDetailsTab.addListener("tabchange", function(panel, selectedTab) {
         if (selectedTab.title == "Coord Job Info") {
@@ -1405,6 +1471,12 @@ function coordJobDetailsPopup(response, request) {
         else if (selectedTab.title == 'Coord Action Reruns') {
             rerunsUnit.setVisible(true);
         }
+        else if (selectedTab.title == 'Coord Error Log') {
+            if(!isErrorLogLoaded){
+                fetchErrorLogs(coordJobId);
+                isErrorLogLoaded=true;
+            }
+        }
         coord_jobs_grid.setVisible(false);
     });
     var win = new Ext.Window({
@@ -1420,6 +1492,8 @@ function coordJobDetailsPopup(response, request) {
 }
 
 function bundleJobDetailsPopup(response, request) {
+
+    var isErrorLogLoaded = false;
     var jobDefinitionArea = new Ext.form.TextArea({
         fieldLabel: 'Definition',
         editable: false,
@@ -1593,6 +1667,16 @@ function bundleJobDetailsPopup(response, request) {
         "For more detail refer documentation 
(/oozie/docs/DG_CommandLineTool.html#Filtering_the_server_logs_with_logfilter_options)"
     });
 
+    var jobErrorLogArea = new Ext.form.TextArea({
+        fieldLabel: 'ErrorLogs',
+        editable: false,
+        name: 'errorlogs',
+        width: 1010,
+        height: 400,
+        autoScroll: true,
+        emptyText: ""
+    });
+
     var searchFilter = new Ext.form.Label({
         text : 'Enter Search Filter'
     });
@@ -1608,6 +1692,9 @@ function bundleJobDetailsPopup(response, request) {
         text : 'Log Status : '
     });
 
+    var errorLogStatus = new Ext.form.Label({
+        text : 'Log Status : '
+    });
 
     var getLogButton = new Ext.Button({
         text: 'Get Logs',
@@ -1643,8 +1730,20 @@ function bundleJobDetailsPopup(response, request) {
            title: 'Bundle Job Log',
            items: jobLogArea,
              tbar: [searchFilter, searchFilterBox, getLogButton, {xtype: 
'tbfill'}, logStatus]
-      }]
-     });
+      },
+     {
+         title: 'Bundle Error Log',
+         items: jobErrorLogArea,
+         tbar: [ {
+             text: "&nbsp;&nbsp;&nbsp;",
+             icon: 'ext-2.2/resources/images/default/grid/refresh.gif',
+             handler: function() {
+                 getLogs(getOozieBase() + 'job/' + bundleJobId + 
"?show=errorlog", null, errorLogStatus, jobErrorLogArea,
+                         false, null);
+             }
+         }, {xtype: 'tbfill'}, errorLogStatus]
+     }
+      ]});
 
     jobDetailsTab.addListener("tabchange", function(panel, selectedTab) {
         if (selectedTab.title == "Bundle Job Info") {
@@ -1654,6 +1753,13 @@ function bundleJobDetailsPopup(response, request) {
         else if (selectedTab.title == 'Bundle Job Definition') {
             fetchDefinition(bundleJobId);
         }
+        else if (selectedTab.title == 'Bundle Error Log') {
+            if(!isErrorLogLoaded){
+                getLogs(getOozieBase() + 'job/' + bundleJobId + 
"?show=errorlog", null, errorLogStatus, jobErrorLogArea,
+                        false, null);
+                isErrorLogLoaded=true;
+            }
+        }
         coord_jobs_grid.setVisible(false);
     });
 
@@ -1667,8 +1773,6 @@ function bundleJobDetailsPopup(response, request) {
         });
     }
 
-
-
     var win = new Ext.Window({
         title: 'Job (Name: ' + bundleJobName + '/bundleJobId: ' + bundleJobId 
+ ')',
         closable: true,

Reply via email to