Esempio n. 1
0
  @Override
  public void onTrigger(final ProcessContext context, final ProcessSession session) {
    final String logLevelValue = context.getProperty(LOG_LEVEL).getValue().toLowerCase();

    final DebugLevels logLevel;
    try {
      logLevel = DebugLevels.valueOf(logLevelValue);
    } catch (Exception e) {
      throw new ProcessException(e);
    }

    final ComponentLog LOG = getLogger();
    boolean isLogLevelEnabled = false;
    switch (logLevel) {
      case trace:
        isLogLevelEnabled = LOG.isTraceEnabled();
        break;
      case debug:
        isLogLevelEnabled = LOG.isDebugEnabled();
        break;
      case info:
        isLogLevelEnabled = LOG.isInfoEnabled();
        break;
      case warn:
        isLogLevelEnabled = LOG.isWarnEnabled();
        break;
      case error:
        isLogLevelEnabled = LOG.isErrorEnabled();
        break;
    }

    if (!isLogLevelEnabled) {
      transferChunk(session);
      return;
    }

    final FlowFile flowFile = session.get();
    if (flowFile == null) {
      return;
    }

    processFlowFile(LOG, logLevel, flowFile, session, context);
    session.transfer(flowFile, REL_SUCCESS);
  }
Esempio n. 2
0
@EventDriven
@SideEffectFree
@SupportsBatching
@Tags({"attributes", "logging"})
@InputRequirement(Requirement.INPUT_REQUIRED)
public class LogAttribute extends AbstractProcessor {

  public static final PropertyDescriptor LOG_LEVEL =
      new PropertyDescriptor.Builder()
          .name("Log Level")
          .required(true)
          .description("The Log Level to use when logging the Attributes")
          .allowableValues(DebugLevels.values())
          .defaultValue("info")
          .build();
  public static final PropertyDescriptor ATTRIBUTES_TO_LOG_CSV =
      new PropertyDescriptor.Builder()
          .name("Attributes to Log")
          .required(false)
          .description(
              "A comma-separated list of Attributes to Log. If not specified, all attributes will be logged.")
          .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
          .build();
  public static final PropertyDescriptor ATTRIBUTES_TO_IGNORE_CSV =
      new PropertyDescriptor.Builder()
          .name("Attributes to Ignore")
          .description(
              "A comma-separated list of Attributes to ignore. If not specified, no attributes will be ignored.")
          .required(false)
          .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
          .build();
  public static final PropertyDescriptor LOG_PAYLOAD =
      new PropertyDescriptor.Builder()
          .name("Log Payload")
          .required(true)
          .description(
              "If true, the FlowFile's payload will be logged, in addition to its attributes; otherwise, just the Attributes will be logged.")
          .defaultValue("false")
          .allowableValues("true", "false")
          .build();

  public static final PropertyDescriptor LOG_PREFIX =
      new PropertyDescriptor.Builder()
          .name("Log prefix")
          .required(false)
          .description(
              "Log prefix appended to the log lines. It helps to distinguish the output of multiple LogAttribute processors.")
          .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
          .expressionLanguageSupported(true)
          .build();

  public static final String FIFTY_DASHES = "--------------------------------------------------";

  public static enum DebugLevels {
    trace,
    debug,
    info,
    warn,
    error
  }

  public static final long ONE_MB = 1024 * 1024;
  private Set<Relationship> relationships;
  private List<PropertyDescriptor> supportedDescriptors;

  public static final Relationship REL_SUCCESS =
      new Relationship.Builder()
          .name("success")
          .description("All FlowFiles are routed to this relationship")
          .build();

  @Override
  protected void init(final ProcessorInitializationContext context) {
    final Set<Relationship> procRels = new HashSet<>();
    procRels.add(REL_SUCCESS);
    relationships = Collections.unmodifiableSet(procRels);

    // descriptors
    final List<PropertyDescriptor> supDescriptors = new ArrayList<>();
    supDescriptors.add(LOG_LEVEL);
    supDescriptors.add(LOG_PAYLOAD);
    supDescriptors.add(ATTRIBUTES_TO_LOG_CSV);
    supDescriptors.add(ATTRIBUTES_TO_IGNORE_CSV);
    supDescriptors.add(LOG_PREFIX);
    supportedDescriptors = Collections.unmodifiableList(supDescriptors);
  }

  @Override
  public Set<Relationship> getRelationships() {
    return relationships;
  }

  @Override
  protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
    return supportedDescriptors;
  }

  protected String processFlowFile(
      final ComponentLog logger,
      final DebugLevels logLevel,
      final FlowFile flowFile,
      final ProcessSession session,
      final ProcessContext context) {
    final Set<String> attributeKeys =
        getAttributesToLog(flowFile.getAttributes().keySet(), context);
    final ComponentLog LOG = getLogger();
    final String dashedLine;

    String logPrefix =
        context.getProperty(LOG_PREFIX).evaluateAttributeExpressions(flowFile).getValue();

    if (StringUtil.isBlank(logPrefix)) {
      dashedLine = StringUtils.repeat('-', 50);
    } else {
      // abbreviate long lines
      logPrefix = StringUtils.abbreviate(logPrefix, 40);
      // center the logPrefix and pad with dashes
      logPrefix = StringUtils.center(logPrefix, 40, '-');
      // five dashes on the left and right side, plus the dashed logPrefix
      dashedLine = StringUtils.repeat('-', 5) + logPrefix + StringUtils.repeat('-', 5);
    }

    // Pretty print metadata
    final StringBuilder message = new StringBuilder();
    message.append("logging for flow file ").append(flowFile);
    message.append("\n");
    message.append(dashedLine);
    message.append("\nStandard FlowFile Attributes");
    message.append(
        String.format(
            "\nKey: '%1$s'\n\tValue: '%2$s'", "entryDate", new Date(flowFile.getEntryDate())));
    message.append(
        String.format(
            "\nKey: '%1$s'\n\tValue: '%2$s'",
            "lineageStartDate", new Date(flowFile.getLineageStartDate())));
    message.append(String.format("\nKey: '%1$s'\n\tValue: '%2$s'", "fileSize", flowFile.getSize()));
    message.append("\nFlowFile Attribute Map Content");
    for (final String key : attributeKeys) {
      message.append(
          String.format("\nKey: '%1$s'\n\tValue: '%2$s'", key, flowFile.getAttribute(key)));
    }
    message.append("\n");
    message.append(dashedLine);

    // The user can request to log the payload
    final boolean logPayload = context.getProperty(LOG_PAYLOAD).asBoolean();
    if (logPayload) {
      message.append("\n");
      if (flowFile.getSize() < ONE_MB) {
        final FlowFilePayloadCallback callback = new FlowFilePayloadCallback();
        session.read(flowFile, callback);
        message.append(callback.getContents());
      } else {
        message.append("\n Not including payload since it is larger than one mb.");
      }
    }
    final String outputMessage = message.toString().trim();
    // Uses optional property to specify logging level
    switch (logLevel) {
      case info:
        LOG.info(outputMessage);
        break;
      case debug:
        LOG.debug(outputMessage);
        break;
      case warn:
        LOG.warn(outputMessage);
        break;
      case trace:
        LOG.trace(outputMessage);
        break;
      case error:
        LOG.error(outputMessage);
        break;
      default:
        LOG.debug(outputMessage);
    }

    return outputMessage;
  }

  private Set<String> getAttributesToLog(
      final Set<String> flowFileAttrKeys, final ProcessContext context) {
    final Set<String> result = new TreeSet<>();

    final String attrsToLogValue = context.getProperty(ATTRIBUTES_TO_LOG_CSV).getValue();
    if (StringUtils.isBlank(attrsToLogValue)) {
      result.addAll(flowFileAttrKeys);
    } else {
      result.addAll(Arrays.asList(attrsToLogValue.split("\\s*,\\s*")));
    }

    final String attrsToRemoveValue = context.getProperty(ATTRIBUTES_TO_IGNORE_CSV).getValue();
    if (StringUtils.isNotBlank(attrsToRemoveValue)) {
      result.removeAll(Arrays.asList(attrsToRemoveValue.split("\\s*,\\s*")));
    }

    return result;
  }

  private void transferChunk(final ProcessSession session) {
    final List<FlowFile> flowFiles = session.get(50);
    if (!flowFiles.isEmpty()) {
      session.transfer(flowFiles, REL_SUCCESS);
    }
  }

  @Override
  public void onTrigger(final ProcessContext context, final ProcessSession session) {
    final String logLevelValue = context.getProperty(LOG_LEVEL).getValue().toLowerCase();

    final DebugLevels logLevel;
    try {
      logLevel = DebugLevels.valueOf(logLevelValue);
    } catch (Exception e) {
      throw new ProcessException(e);
    }

    final ComponentLog LOG = getLogger();
    boolean isLogLevelEnabled = false;
    switch (logLevel) {
      case trace:
        isLogLevelEnabled = LOG.isTraceEnabled();
        break;
      case debug:
        isLogLevelEnabled = LOG.isDebugEnabled();
        break;
      case info:
        isLogLevelEnabled = LOG.isInfoEnabled();
        break;
      case warn:
        isLogLevelEnabled = LOG.isWarnEnabled();
        break;
      case error:
        isLogLevelEnabled = LOG.isErrorEnabled();
        break;
    }

    if (!isLogLevelEnabled) {
      transferChunk(session);
      return;
    }

    final FlowFile flowFile = session.get();
    if (flowFile == null) {
      return;
    }

    processFlowFile(LOG, logLevel, flowFile, session, context);
    session.transfer(flowFile, REL_SUCCESS);
  }

  protected static class FlowFilePayloadCallback implements InputStreamCallback {

    private String contents = "";

    @Override
    public void process(final InputStream in) throws IOException {
      contents = IOUtils.toString(in);
    }

    public String getContents() {
      return contents;
    }
  }
}