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: " ", + 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: " ", + 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: " ", + 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,
