/* * 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.flume.interceptor; import org.apache.flume.Context; import org.apache.flume.Event; import org.apache.flume.conf.LogPrivacyUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This interceptor manipulates Flume event headers, by removing one or many * headers. It can remove a statically defined header, headers based on a * regular expression or headers in a list. If none of these is defined, or if * no header matches the criteria, the Flume events are not modified.<br /> * Note that if only one header needs to be removed, specifying it by name * provides performance benefits over the other two methods.<br /> * <br /> * Properties:<br /> * - .withName (optional): name of the header to remove<br /> * - .fromList (optional): list of headers to remove, separated with the * separator specified with .from.list.separator<br /> * - .fromListSeparator (optional): regular expression used to separate * multiple header names in the list specified using .from.list<br /> * - .matching (optional): All the headers which names match this regular expression are * removed */ public class RemoveHeaderInterceptor implements Interceptor { static final String WITH_NAME = "withName"; static final String FROM_LIST = "fromList"; static final String LIST_SEPARATOR = "fromListSeparator"; static final String LIST_SEPARATOR_DEFAULT = "\\s*,\\s*"; static final String MATCH_REGEX = "matching"; private static final Logger LOG = LoggerFactory .getLogger(RemoveHeaderInterceptor.class); private final String withName; private final Set<String> fromList; private final Pattern matchRegex; /** * Only {@link RemoveHeaderInterceptor.Builder} can build me */ private RemoveHeaderInterceptor(final String withName, final String fromList, final String listSeparator, final Pattern matchRegex) { this.withName = withName; assert listSeparator != null : "Default value used otherwise"; this.fromList = (fromList != null) ? new HashSet<>(Arrays.asList(fromList.split( listSeparator))) : null; this.matchRegex = matchRegex; } /** * @see org.apache.flume.interceptor.Interceptor#initialize() */ @Override public void initialize() { // Nothing to do } /** * @see org.apache.flume.interceptor.Interceptor#close() */ @Override public void close() { // Nothing to do } /** * @see org.apache.flume.interceptor.Interceptor#intercept(java.util.List) */ @Override public List<Event> intercept(final List<Event> events) { for (final Event event : events) { intercept(event); } return events; } /** * @see org.apache.flume.interceptor.Interceptor#intercept(org.apache.flume.Event) */ @Override public Event intercept(final Event event) { assert event != null : "Missing Flume event while intercepting"; try { final Map<String, String> headers = event.getHeaders(); // If withName matches, removing it directly if (withName != null && headers.remove(withName) != null) { LOG.trace("Removed header \"{}\" for event: {}", withName, event); } // Also, we need to go through the list if (fromList != null || matchRegex != null) { final Iterator<String> headerIterator = headers.keySet().iterator(); List<String> removedHeaders = new LinkedList<>(); while (headerIterator.hasNext()) { final String currentHeader = headerIterator.next(); if (fromList != null && fromList.contains(currentHeader)) { headerIterator.remove(); removedHeaders.add(currentHeader); } else if (matchRegex != null) { final Matcher matcher = matchRegex.matcher(currentHeader); if (matcher.matches()) { headerIterator.remove(); removedHeaders.add(currentHeader); } } } if (!removedHeaders.isEmpty() && LogPrivacyUtil.allowLogRawData()) { LOG.trace("Removed headers \"{}\" for event: {}", removedHeaders, event); } } } catch (final Exception e) { LOG.error("Failed to process event " + event, e); } return event; } /** * Builder which builds new instances of the {@link RemoveHeaderInterceptor}. */ public static class Builder implements Interceptor.Builder { String withName; String fromList; String listSeparator; Pattern matchRegex; /** * @see org.apache.flume.interceptor.Interceptor.Builder#build() */ @Override public Interceptor build() { if (LOG.isDebugEnabled()) { LOG.debug("Creating RemoveHeaderInterceptor with: withName={}, fromList={}, " + "listSeparator={}, matchRegex={}", new String[] {withName, fromList, listSeparator, String.valueOf(matchRegex)}); } return new RemoveHeaderInterceptor(withName, fromList, listSeparator, matchRegex); } /** * @see org.apache.flume.conf.Configurable#configure(org.apache.flume.Context) */ @Override public void configure(final Context context) { withName = context.getString(WITH_NAME); fromList = context.getString(FROM_LIST); listSeparator = context.getString(LIST_SEPARATOR, LIST_SEPARATOR_DEFAULT); final String matchRegexStr = context.getString(MATCH_REGEX); if (matchRegexStr != null) { matchRegex = Pattern.compile(matchRegexStr); } } } }