/*
* Copyright © 2014-2016 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.logging.context;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.logging.ApplicationLoggingContext;
import co.cask.cdap.common.logging.ComponentLoggingContext;
import co.cask.cdap.common.logging.LoggingContext;
import co.cask.cdap.common.logging.NamespaceLoggingContext;
import co.cask.cdap.common.logging.ServiceLoggingContext;
import co.cask.cdap.common.logging.SystemLoggingContext;
import co.cask.cdap.logging.filter.AndFilter;
import co.cask.cdap.logging.filter.Filter;
import co.cask.cdap.logging.filter.MdcExpression;
import co.cask.cdap.logging.filter.OrFilter;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.ProgramType;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Helper class for LoggingContext objects.
*/
public final class LoggingContextHelper {
private static final String ACCOUNT_ID = ".accountId";
private static final Logger LOG = LoggerFactory.getLogger(LoggingContext.class);
private static final Map<String, String> LOG_TAG_TO_METRICS_TAG_MAP =
ImmutableMap.<String, String>builder()
.put(FlowletLoggingContext.TAG_FLOWLET_ID, Constants.Metrics.Tag.FLOWLET)
.put(WorkflowLoggingContext.TAG_WORKFLOW_ID, Constants.Metrics.Tag.WORKFLOW)
.put(MapReduceLoggingContext.TAG_MAP_REDUCE_JOB_ID, Constants.Metrics.Tag.MAPREDUCE)
.put(SparkLoggingContext.TAG_SPARK_JOB_ID, Constants.Metrics.Tag.SPARK)
.put(UserServiceLoggingContext.TAG_USERSERVICE_ID, Constants.Metrics.Tag.HANDLER)
.put(WorkerLoggingContext.TAG_WORKER_ID, Constants.Metrics.Tag.WORKER)
.build();
private LoggingContextHelper() {}
public static String getNamespacedBaseDir(String logBaseDir, String logPartition) {
Preconditions.checkArgument(logBaseDir != null, "Log Base dir cannot be null");
Preconditions.checkArgument(logPartition != null, "Log partition cannot be null");
String [] partitions = logPartition.split(":");
Preconditions.checkArgument(partitions.length == 3,
"Expected log partition to be in the format <ns>:<entity>:<sub-entity>");
// don't care about the app or the program, only need the namespace
GenericLoggingContext loggingContext = new GenericLoggingContext(partitions[0], partitions[1], partitions[2]);
return loggingContext.getNamespacedLogBaseDir(logBaseDir);
}
public static LoggingContext getLoggingContext(Map<String, String> tags) {
// Tags are empty, cannot determine logging context.
if (tags == null || tags.isEmpty()) {
throw new IllegalArgumentException("Tags are empty, cannot determine logging context");
}
String namespaceId = tags.get(NamespaceLoggingContext.TAG_NAMESPACE_ID);
String applicationId = tags.get(ApplicationLoggingContext.TAG_APPLICATION_ID);
String systemId = tags.get(SystemLoggingContext.TAG_SYSTEM_ID);
String componentId = tags.get(ComponentLoggingContext.TAG_COMPONENT_ID);
// No namespace id or application id present.
if (namespaceId == null || applicationId == null) {
if (systemId == null || componentId == null) {
throw new IllegalArgumentException("No namespace/application or system/component id present");
}
}
if (tags.containsKey(FlowletLoggingContext.TAG_FLOW_ID)) {
if (!tags.containsKey(FlowletLoggingContext.TAG_FLOWLET_ID)) {
return null;
}
return new FlowletLoggingContext(namespaceId, applicationId, tags.get(FlowletLoggingContext.TAG_FLOW_ID),
tags.get(FlowletLoggingContext.TAG_FLOWLET_ID),
tags.get(ApplicationLoggingContext.TAG_RUNID_ID),
tags.get(ApplicationLoggingContext.TAG_INSTANCE_ID));
} else if (tags.containsKey(WorkflowLoggingContext.TAG_WORKFLOW_ID)) {
return new WorkflowLoggingContext(namespaceId, applicationId,
tags.get(WorkflowLoggingContext.TAG_WORKFLOW_ID),
tags.get(ApplicationLoggingContext.TAG_RUNID_ID));
} else if (tags.containsKey(MapReduceLoggingContext.TAG_MAP_REDUCE_JOB_ID)) {
return new MapReduceLoggingContext(namespaceId, applicationId,
tags.get(MapReduceLoggingContext.TAG_MAP_REDUCE_JOB_ID),
tags.get(ApplicationLoggingContext.TAG_RUNID_ID));
} else if (tags.containsKey(SparkLoggingContext.TAG_SPARK_JOB_ID)) {
return new SparkLoggingContext(namespaceId, applicationId, tags.get(SparkLoggingContext.TAG_SPARK_JOB_ID),
tags.get(ApplicationLoggingContext.TAG_RUNID_ID));
} else if (tags.containsKey(UserServiceLoggingContext.TAG_USERSERVICE_ID)) {
if (!tags.containsKey(UserServiceLoggingContext.TAG_HANDLER_ID)) {
return null;
}
return new UserServiceLoggingContext(namespaceId, applicationId,
tags.get(UserServiceLoggingContext.TAG_USERSERVICE_ID),
tags.get(UserServiceLoggingContext.TAG_HANDLER_ID),
tags.get(ApplicationLoggingContext.TAG_RUNID_ID),
tags.get(ApplicationLoggingContext.TAG_INSTANCE_ID));
} else if (tags.containsKey(ServiceLoggingContext.TAG_SERVICE_ID)) {
return new ServiceLoggingContext(systemId, componentId,
tags.get(ServiceLoggingContext.TAG_SERVICE_ID));
} else if (tags.containsKey(WorkerLoggingContext.TAG_WORKER_ID)) {
return new WorkerLoggingContext(namespaceId, applicationId, tags.get(WorkerLoggingContext.TAG_WORKER_ID),
tags.get(ApplicationLoggingContext.TAG_RUNID_ID),
tags.get(ApplicationLoggingContext.TAG_INSTANCE_ID));
}
throw new IllegalArgumentException("Unsupported logging context");
}
public static LoggingContext getLoggingContext(String systemId, String componentId, String serviceId) {
return new ServiceLoggingContext(systemId, componentId, serviceId);
}
public static LoggingContext getLoggingContext(String namespaceId, String applicationId, String entityId,
ProgramType programType) {
return getLoggingContext(namespaceId, applicationId, entityId, programType, null, null);
}
public static LoggingContext getLoggingContextWithRunId(String namespaceId, String applicationId, String entityId,
ProgramType programType, String runId,
Map<String, String> systemArgs) {
return getLoggingContext(namespaceId, applicationId, entityId, programType, runId, systemArgs);
}
public static LoggingContext getLoggingContext(String namespaceId, String applicationId, String entityId,
ProgramType programType, @Nullable String runId,
@Nullable Map<String, String> systemArgs) {
switch (programType) {
case FLOW:
return new FlowletLoggingContext(namespaceId, applicationId, entityId, "", runId, null);
case WORKFLOW:
return new WorkflowLoggingContext(namespaceId, applicationId, entityId, runId);
case MAPREDUCE:
if (systemArgs != null && systemArgs.containsKey("workflowRunId")) {
String workflowRunId = systemArgs.get("workflowRunId");
String workflowId = systemArgs.get("workflowName");
return new WorkflowProgramLoggingContext(namespaceId, applicationId, workflowId, workflowRunId, programType,
entityId);
}
return new MapReduceLoggingContext(namespaceId, applicationId, entityId, runId);
case SPARK:
if (systemArgs != null && systemArgs.containsKey("workflowRunId")) {
String workflowRunId = systemArgs.get("workflowRunId");
String workflowId = systemArgs.get("workflowName");
return new WorkflowProgramLoggingContext(namespaceId, applicationId, workflowId, workflowRunId, programType,
entityId);
}
return new SparkLoggingContext(namespaceId, applicationId, entityId, runId);
case SERVICE:
return new UserServiceLoggingContext(namespaceId, applicationId, entityId, "", runId, null);
case WORKER:
return new WorkerLoggingContext(namespaceId, applicationId, entityId, runId, null);
default:
throw new IllegalArgumentException(String.format("Illegal entity type for logging context: %s", programType));
}
}
public static Filter createFilter(LoggingContext loggingContext) {
if (loggingContext instanceof ServiceLoggingContext) {
String systemId = loggingContext.getSystemTagsMap().get(ServiceLoggingContext.TAG_SYSTEM_ID).getValue();
String componentId = loggingContext.getSystemTagsMap().get(ServiceLoggingContext.TAG_COMPONENT_ID).getValue();
String tagName = ServiceLoggingContext.TAG_SERVICE_ID;
String entityId = loggingContext.getSystemTagsMap().get(ServiceLoggingContext.TAG_SERVICE_ID).getValue();
return new AndFilter(
ImmutableList.of(new MdcExpression(ServiceLoggingContext.TAG_SYSTEM_ID, systemId),
new MdcExpression(ServiceLoggingContext.TAG_COMPONENT_ID, componentId),
new MdcExpression(tagName, entityId)));
} else {
String namespaceId = loggingContext.getSystemTagsMap().get(ApplicationLoggingContext.TAG_NAMESPACE_ID).getValue();
String applId = loggingContext.getSystemTagsMap().get(ApplicationLoggingContext.TAG_APPLICATION_ID).getValue();
LoggingContext.SystemTag entityTag = getEntityId(loggingContext);
ImmutableList.Builder<Filter> filterBuilder = ImmutableList.builder();
// For backward compatibility: The old logs before namespace have .accountId and developer as value so we don't
// want them to get filtered out if they belong to this application and entity
OrFilter namespaceFilter = new OrFilter(ImmutableList.of(new MdcExpression(
NamespaceLoggingContext.TAG_NAMESPACE_ID, namespaceId),
new MdcExpression(ACCOUNT_ID,
Constants.DEVELOPER_ACCOUNT)));
filterBuilder.add(namespaceFilter);
filterBuilder.add(new MdcExpression(ApplicationLoggingContext.TAG_APPLICATION_ID, applId));
filterBuilder.add(new MdcExpression(entityTag.getName(), entityTag.getValue()));
if (loggingContext instanceof WorkflowProgramLoggingContext) {
// Program is started by Workflow. Add Program information to filter.
Map<String, LoggingContext.SystemTag> systemTagsMap = loggingContext.getSystemTagsMap();
LoggingContext.SystemTag programTag
= systemTagsMap.get(WorkflowProgramLoggingContext.TAG_WORKFLOW_MAP_REDUCE_ID);
if (programTag != null) {
filterBuilder.add(new MdcExpression(WorkflowProgramLoggingContext.TAG_WORKFLOW_MAP_REDUCE_ID,
programTag.getValue()));
}
programTag = systemTagsMap.get(WorkflowProgramLoggingContext.TAG_WORKFLOW_SPARK_ID);
if (programTag != null) {
filterBuilder.add(new MdcExpression(WorkflowProgramLoggingContext.TAG_WORKFLOW_SPARK_ID,
programTag.getValue()));
}
}
// Add runid filter if required
LoggingContext.SystemTag runId = loggingContext.getSystemTagsMap().get(ApplicationLoggingContext.TAG_RUNID_ID);
if (runId != null && runId.getValue() != null) {
filterBuilder.add(new MdcExpression(ApplicationLoggingContext.TAG_RUNID_ID, runId.getValue()));
}
return new AndFilter(filterBuilder.build());
}
}
public static LoggingContext.SystemTag getEntityId(LoggingContext loggingContext) {
final String tagName;
if (loggingContext instanceof FlowletLoggingContext) {
tagName = FlowletLoggingContext.TAG_FLOW_ID;
} else if (loggingContext instanceof WorkflowLoggingContext) {
tagName = WorkflowLoggingContext.TAG_WORKFLOW_ID;
} else if (loggingContext instanceof MapReduceLoggingContext) {
tagName = MapReduceLoggingContext.TAG_MAP_REDUCE_JOB_ID;
} else if (loggingContext instanceof SparkLoggingContext) {
tagName = SparkLoggingContext.TAG_SPARK_JOB_ID;
} else if (loggingContext instanceof UserServiceLoggingContext) {
tagName = UserServiceLoggingContext.TAG_USERSERVICE_ID;
} else if (loggingContext instanceof WorkerLoggingContext) {
tagName = WorkerLoggingContext.TAG_WORKER_ID;
} else {
throw new IllegalArgumentException(String.format("Invalid logging context: %s", loggingContext));
}
final String entityId = loggingContext.getSystemTagsMap().get(tagName).getValue();
return new LoggingContext.SystemTag() {
@Override
public String getName() {
return tagName;
}
@Override
public String getValue() {
return entityId;
}
};
}
public static Map<String, String> getMetricsTags(LoggingContext context) throws IllegalArgumentException {
if (context instanceof ServiceLoggingContext) {
return getMetricsTagsFromSystemContext((ServiceLoggingContext) context);
} else {
return getMetricsTagsFromLoggingContext(context);
}
}
private static Map<String, String> getMetricsTagsFromLoggingContext(LoggingContext context) {
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
Map<String, LoggingContext.SystemTag> loggingTags = context.getSystemTagsMap();
String namespace = getValueFromTag(loggingTags.get(NamespaceLoggingContext.TAG_NAMESPACE_ID));
if (namespace == null || namespace.isEmpty()) {
throw new IllegalArgumentException("Cannot find namespace in logging context");
}
builder.put(Constants.Metrics.Tag.NAMESPACE, namespace);
String applicationId = getValueFromTag(loggingTags.get(ApplicationLoggingContext.TAG_APPLICATION_ID));
// Must be an application
if (applicationId == null) {
throw new IllegalArgumentException("Missing application id");
}
builder.put(Constants.Metrics.Tag.APP, applicationId);
LoggingContext.SystemTag entityId = getEntityId(context);
String entityName = getMetricsTagNameFromLoggingContext(entityId);
if (entityName != null) {
builder.put(entityName, entityId.getValue());
}
return builder.build();
}
private static String getMetricsTagNameFromLoggingContext(LoggingContext.SystemTag tag) {
return LOG_TAG_TO_METRICS_TAG_MAP.get(tag.getName());
}
private static String getValueFromTag(LoggingContext.SystemTag tag) {
return tag == null ? null : tag.getValue();
}
private static Map<String, String> getMetricsTagsFromSystemContext(ServiceLoggingContext context) {
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
builder.put(Constants.Metrics.Tag.NAMESPACE, Id.Namespace.SYSTEM.getId());
builder.put(Constants.Metrics.Tag.COMPONENT,
context.getSystemTagsMap().get(ServiceLoggingContext.TAG_SERVICE_ID).getValue());
return builder.build();
}
}