/**
* 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.falcon.logging;
import org.apache.commons.lang3.StringUtils;
import org.apache.falcon.FalconException;
import org.apache.falcon.entity.ClusterHelper;
import org.apache.falcon.entity.EntityUtil;
import org.apache.falcon.entity.store.ConfigurationStore;
import org.apache.falcon.entity.v0.Entity;
import org.apache.falcon.entity.v0.EntityType;
import org.apache.falcon.entity.v0.cluster.Cluster;
import org.apache.falcon.hadoop.HadoopClientFactory;
import org.apache.falcon.resource.InstancesResult.Instance;
import org.apache.falcon.resource.InstancesResult.InstanceAction;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.oozie.client.OozieClientException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* Get oozie action execution logs corresponding to a run as saved by the log mover.
*/
public final class LogProvider {
private static final Logger LOG = LoggerFactory.getLogger(LogProvider.class);
public Instance populateLogUrls(Entity entity, Instance instance,
String runId) throws FalconException {
Cluster clusterObj = ConfigurationStore.get().get(
EntityType.CLUSTER, instance.cluster);
try {
Configuration conf = ClusterHelper.getConfiguration(clusterObj);
// fs on behalf of the end user.
FileSystem fs = HadoopClientFactory.get().createProxiedFileSystem(conf);
String resolvedRunId = getResolvedRunId(fs, clusterObj, entity, instance, runId);
// if runId param is not resolved, i.e job is killed or not started or running
if (resolvedRunId.equals("-")
&& StringUtils.isEmpty(instance.logFile)) {
instance.logFile = "-";
return instance;
}
return populateActionLogUrls(fs, clusterObj, entity, instance,
resolvedRunId);
} catch (IOException e) {
LOG.warn("Exception while getting FS in LogProvider", e);
} catch (Exception e) {
LOG.warn("Exception in LogProvider while getting resolving run id",
e);
}
return instance;
}
public String getResolvedRunId(FileSystem fs, Cluster cluster,
Entity entity, Instance instance, String runId)
throws FalconException, IOException {
if (StringUtils.isEmpty(runId)) {
Path jobPath = new Path(ClusterHelper.getStorageUrl(cluster),
EntityUtil.getLogPath(cluster, entity) + "/job-"
+ EntityUtil.fromUTCtoURIDate(instance.instance) + "/*");
//Assumption here is - Each wf run will have a directory
//the corresponding job logs are stored within the respective dir
Path maxRunPath = findMaxRunIdPath(fs, jobPath);
if (maxRunPath != null) {
return maxRunPath.getName();
} else {
LOG.warn("No run dirs are available in logs dir: {}", jobPath);
return "-";
}
} else {
Path jobPath = new Path(ClusterHelper.getStorageUrl(cluster),
EntityUtil.getLogPath(cluster, entity) + "/job-"
+ EntityUtil.fromUTCtoURIDate(instance.instance) + "/"
+ getFormatedRunId(runId));
if (fs.exists(jobPath)) {
return getFormatedRunId(runId);
} else {
LOG.warn("No run dirs are available in logs dir:" + jobPath);
return "-";
}
}
}
private Instance populateActionLogUrls(FileSystem fs, Cluster cluster,
Entity entity, Instance instance, String formattedRunId)
throws FalconException, OozieClientException, IOException {
Path actionPaths = new Path(ClusterHelper.getStorageUrl(cluster),
EntityUtil.getLogPath(cluster, entity) + "/job-"
+ EntityUtil.fromUTCtoURIDate(instance.instance) + "/"
+ formattedRunId + "/*");
FileStatus[] actions = fs.globStatus(actionPaths);
if (actions != null && actions.length > 0) {
InstanceAction[] instanceActions = new InstanceAction[actions.length - 1];
instance.actions = instanceActions;
int i = 0;
for (FileStatus file : actions) {
Path filePath = file.getPath();
String dfsBrowserUrl = getDFSbrowserUrl(
ClusterHelper.getStorageUrl(cluster),
EntityUtil.getLogPath(cluster, entity) + "/job-"
+ EntityUtil.fromUTCtoURIDate(instance.instance) + "/"
+ formattedRunId, file.getPath().getName());
if (filePath.getName().equals("oozie.log")) {
instance.logFile = dfsBrowserUrl;
continue;
}
InstanceAction instanceAction = new InstanceAction(
getActionName(filePath.getName()),
getActionStatus(filePath.getName()), dfsBrowserUrl);
instanceActions[i++] = instanceAction;
}
}
return instance;
}
private String getActionName(String fileName) {
return fileName.replaceAll("_SUCCEEDED\\.log", "").replaceAll(
"_FAILED\\.log", "");
}
private String getActionStatus(String fileName) {
return fileName.matches("(.*)SUCCEEDED(.*).log") ? "SUCCEEDED" : "FAILED";
}
private String getDFSbrowserUrl(String hdfsPath, String logPath,
String fileName) throws FalconException {
String scheme = new Path(hdfsPath).toUri().getScheme();
String httpUrl = hdfsPath.replaceAll(scheme + "://", "http://").replaceAll(
":[0-9]+", ":50070");
return new Path(httpUrl, "/data/" + logPath + "/" + fileName).toString();
}
private String getFormatedRunId(String runId) {
return String.format("%03d", Integer.parseInt(runId));
}
private Path findMaxRunIdPath(FileSystem fs, Path jobLogPath) throws IOException{
// In case of multiple runs, dirs are sorted in increasing
// order of runs. If runId is not specified, return the max runId (whose dir exists)
Path maxRunIdPath = null;
for (FileStatus fileStatus : fs.globStatus(jobLogPath)) {
if (fs.isDirectory(fileStatus.getPath())) {
maxRunIdPath = fileStatus.getPath();
}
}
return maxRunIdPath;
}
}