diff --git a/agent/accumulator.go b/agent/accumulator.go
index ce3e22eeb26dcc29567345bed898af4f00153ed9..a0d0461a4cef422d630d13c408a3acd4d26af815 100644
--- a/agent/accumulator.go
+++ b/agent/accumulator.go
@@ -83,13 +83,8 @@ func (ac *accumulator) makeMetric(
 	if len(fields) == 0 || len(measurement) == 0 {
 		return nil
 	}
-
-	if !ac.inputConfig.Filter.ShouldNamePass(measurement) {
-		return nil
-	}
-
-	if !ac.inputConfig.Filter.ShouldTagsPass(tags) {
-		return nil
+	if tags == nil {
+		tags = make(map[string]string)
 	}
 
 	// Override measurement name if set
@@ -104,9 +99,6 @@ func (ac *accumulator) makeMetric(
 		measurement = measurement + ac.inputConfig.MeasurementSuffix
 	}
 
-	if tags == nil {
-		tags = make(map[string]string)
-	}
 	// Apply plugin-wide tags if set
 	for k, v := range ac.inputConfig.Tags {
 		if _, ok := tags[k]; !ok {
@@ -119,25 +111,21 @@ func (ac *accumulator) makeMetric(
 			tags[k] = v
 		}
 	}
-	ac.inputConfig.Filter.FilterTags(tags)
 
-	result := make(map[string]interface{})
-	for k, v := range fields {
-		// Filter out any filtered fields
-		if ac.inputConfig != nil {
-			if !ac.inputConfig.Filter.ShouldFieldsPass(k) {
-				continue
-			}
-		}
+	// Apply the metric filter(s)
+	if ok := ac.inputConfig.Filter.Apply(measurement, fields, tags); !ok {
+		return nil
+	}
 
+	for k, v := range fields {
 		// Validate uint64 and float64 fields
 		switch val := v.(type) {
 		case uint64:
 			// InfluxDB does not support writing uint64
 			if val < uint64(9223372036854775808) {
-				result[k] = int64(val)
+				fields[k] = int64(val)
 			} else {
-				result[k] = int64(9223372036854775807)
+				fields[k] = int64(9223372036854775807)
 			}
 			continue
 		case float64:
@@ -148,15 +136,12 @@ func (ac *accumulator) makeMetric(
 						"field, skipping",
 						measurement, k)
 				}
+				delete(fields, k)
 				continue
 			}
 		}
 
-		result[k] = v
-	}
-	fields = nil
-	if len(result) == 0 {
-		return nil
+		fields[k] = v
 	}
 
 	var timestamp time.Time
@@ -171,11 +156,11 @@ func (ac *accumulator) makeMetric(
 	var err error
 	switch mType {
 	case telegraf.Counter:
-		m, err = telegraf.NewCounterMetric(measurement, tags, result, timestamp)
+		m, err = telegraf.NewCounterMetric(measurement, tags, fields, timestamp)
 	case telegraf.Gauge:
-		m, err = telegraf.NewGaugeMetric(measurement, tags, result, timestamp)
+		m, err = telegraf.NewGaugeMetric(measurement, tags, fields, timestamp)
 	default:
-		m, err = telegraf.NewMetric(measurement, tags, result, timestamp)
+		m, err = telegraf.NewMetric(measurement, tags, fields, timestamp)
 	}
 	if err != nil {
 		log.Printf("Error adding point [%s]: %s\n", measurement, err.Error())
diff --git a/agent/accumulator_test.go b/agent/accumulator_test.go
index 7da8c96e2e761f18c9f79c702511581f4c51d67f..ef5a34ec92d6e098ed3cfbc37c0de5c2ba9cc2f0 100644
--- a/agent/accumulator_test.go
+++ b/agent/accumulator_test.go
@@ -560,7 +560,7 @@ func TestAccFilterTags(t *testing.T) {
 	filter := models.Filter{
 		TagExclude: []string{"acc"},
 	}
-	assert.NoError(t, filter.CompileFilter())
+	assert.NoError(t, filter.Compile())
 	a.inputConfig = &models.InputConfig{}
 	a.inputConfig.Filter = filter
 
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
index a0117891986adbcc9d7ba3213e08fe5e3d2dc2e6..46f044ab766a16cad903104cab3f824f4f4d6659 100644
--- a/docs/CONFIGURATION.md
+++ b/docs/CONFIGURATION.md
@@ -86,6 +86,10 @@ as it is more efficient to filter out tags at the ingestion point.
 * **taginclude**: taginclude is the inverse of tagexclude. It will only include
 the tag keys in the final measurement.
 
+**NOTE** `tagpass` and `tagdrop` parameters must be defined at the _end_ of
+the plugin definition, otherwise subsequent plugin config options will be
+interpreted as part of the tagpass/tagdrop map.
+
 ## Input Configuration
 
 Some configuration options are configurable per input:
@@ -129,6 +133,10 @@ fields which begin with `time_`.
 
 #### Input Config: tagpass and tagdrop
 
+**NOTE** `tagpass` and `tagdrop` parameters must be defined at the _end_ of
+the plugin definition, otherwise subsequent plugin config options will be
+interpreted as part of the tagpass/tagdrop map.
+
 ```toml
 [[inputs.cpu]]
   percpu = true
diff --git a/filter/filter.go b/filter/filter.go
index 85eed17ac0e7c32d53f9a5445f242bd740583766..9a28c26270cd169a14750a181f29dcd5ea089df1 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -10,16 +10,16 @@ type Filter interface {
 	Match(string) bool
 }
 
-// CompileFilter takes a list of string filters and returns a Filter interface
+// Compile takes a list of string filters and returns a Filter interface
 // for matching a given string against the filter list. The filter list
 // supports glob matching too, ie:
 //
-//   f, _ := CompileFilter([]string{"cpu", "mem", "net*"})
+//   f, _ := Compile([]string{"cpu", "mem", "net*"})
 //   f.Match("cpu")     // true
 //   f.Match("network") // true
 //   f.Match("memory")  // false
 //
-func CompileFilter(filters []string) (Filter, error) {
+func Compile(filters []string) (Filter, error) {
 	// return if there is nothing to compile
 	if len(filters) == 0 {
 		return nil, nil
diff --git a/filter/filter_test.go b/filter/filter_test.go
index 85072e2ac53541097852458d2a6f9a50e90cb818..2f52e036a88b78c0a4058abb12b57433e30bec55 100644
--- a/filter/filter_test.go
+++ b/filter/filter_test.go
@@ -6,30 +6,30 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestCompileFilter(t *testing.T) {
-	f, err := CompileFilter([]string{})
+func TestCompile(t *testing.T) {
+	f, err := Compile([]string{})
 	assert.NoError(t, err)
 	assert.Nil(t, f)
 
-	f, err = CompileFilter([]string{"cpu"})
+	f, err = Compile([]string{"cpu"})
 	assert.NoError(t, err)
 	assert.True(t, f.Match("cpu"))
 	assert.False(t, f.Match("cpu0"))
 	assert.False(t, f.Match("mem"))
 
-	f, err = CompileFilter([]string{"cpu*"})
+	f, err = Compile([]string{"cpu*"})
 	assert.NoError(t, err)
 	assert.True(t, f.Match("cpu"))
 	assert.True(t, f.Match("cpu0"))
 	assert.False(t, f.Match("mem"))
 
-	f, err = CompileFilter([]string{"cpu", "mem"})
+	f, err = Compile([]string{"cpu", "mem"})
 	assert.NoError(t, err)
 	assert.True(t, f.Match("cpu"))
 	assert.False(t, f.Match("cpu0"))
 	assert.True(t, f.Match("mem"))
 
-	f, err = CompileFilter([]string{"cpu", "mem", "net*"})
+	f, err = Compile([]string{"cpu", "mem", "net*"})
 	assert.NoError(t, err)
 	assert.True(t, f.Match("cpu"))
 	assert.False(t, f.Match("cpu0"))
@@ -40,7 +40,7 @@ func TestCompileFilter(t *testing.T) {
 var benchbool bool
 
 func BenchmarkFilterSingleNoGlobFalse(b *testing.B) {
-	f, _ := CompileFilter([]string{"cpu"})
+	f, _ := Compile([]string{"cpu"})
 	var tmp bool
 	for n := 0; n < b.N; n++ {
 		tmp = f.Match("network")
@@ -49,7 +49,7 @@ func BenchmarkFilterSingleNoGlobFalse(b *testing.B) {
 }
 
 func BenchmarkFilterSingleNoGlobTrue(b *testing.B) {
-	f, _ := CompileFilter([]string{"cpu"})
+	f, _ := Compile([]string{"cpu"})
 	var tmp bool
 	for n := 0; n < b.N; n++ {
 		tmp = f.Match("cpu")
@@ -58,7 +58,7 @@ func BenchmarkFilterSingleNoGlobTrue(b *testing.B) {
 }
 
 func BenchmarkFilter(b *testing.B) {
-	f, _ := CompileFilter([]string{"cpu", "mem", "net*"})
+	f, _ := Compile([]string{"cpu", "mem", "net*"})
 	var tmp bool
 	for n := 0; n < b.N; n++ {
 		tmp = f.Match("network")
@@ -67,7 +67,7 @@ func BenchmarkFilter(b *testing.B) {
 }
 
 func BenchmarkFilterNoGlob(b *testing.B) {
-	f, _ := CompileFilter([]string{"cpu", "mem", "net"})
+	f, _ := Compile([]string{"cpu", "mem", "net"})
 	var tmp bool
 	for n := 0; n < b.N; n++ {
 		tmp = f.Match("net")
@@ -76,7 +76,7 @@ func BenchmarkFilterNoGlob(b *testing.B) {
 }
 
 func BenchmarkFilter2(b *testing.B) {
-	f, _ := CompileFilter([]string{"aa", "bb", "c", "ad", "ar", "at", "aq",
+	f, _ := Compile([]string{"aa", "bb", "c", "ad", "ar", "at", "aq",
 		"aw", "az", "axxx", "ab", "cpu", "mem", "net*"})
 	var tmp bool
 	for n := 0; n < b.N; n++ {
@@ -86,7 +86,7 @@ func BenchmarkFilter2(b *testing.B) {
 }
 
 func BenchmarkFilter2NoGlob(b *testing.B) {
-	f, _ := CompileFilter([]string{"aa", "bb", "c", "ad", "ar", "at", "aq",
+	f, _ := Compile([]string{"aa", "bb", "c", "ad", "ar", "at", "aq",
 		"aw", "az", "axxx", "ab", "cpu", "mem", "net"})
 	var tmp bool
 	for n := 0; n < b.N; n++ {
diff --git a/internal/config/config.go b/internal/config/config.go
index 24c1af3fa7affb9b98bf2ac0256e869bb3f113ab..30e62789023ec11739c19035e48b6799c550f3c3 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -665,7 +665,6 @@ func buildFilter(tbl *ast.Table) (models.Filter, error) {
 				for _, elem := range ary.Value {
 					if str, ok := elem.(*ast.String); ok {
 						f.NamePass = append(f.NamePass, str.Value)
-						f.IsActive = true
 					}
 				}
 			}
@@ -678,7 +677,6 @@ func buildFilter(tbl *ast.Table) (models.Filter, error) {
 				for _, elem := range ary.Value {
 					if str, ok := elem.(*ast.String); ok {
 						f.NameDrop = append(f.NameDrop, str.Value)
-						f.IsActive = true
 					}
 				}
 			}
@@ -693,7 +691,6 @@ func buildFilter(tbl *ast.Table) (models.Filter, error) {
 					for _, elem := range ary.Value {
 						if str, ok := elem.(*ast.String); ok {
 							f.FieldPass = append(f.FieldPass, str.Value)
-							f.IsActive = true
 						}
 					}
 				}
@@ -709,7 +706,6 @@ func buildFilter(tbl *ast.Table) (models.Filter, error) {
 					for _, elem := range ary.Value {
 						if str, ok := elem.(*ast.String); ok {
 							f.FieldDrop = append(f.FieldDrop, str.Value)
-							f.IsActive = true
 						}
 					}
 				}
@@ -730,7 +726,6 @@ func buildFilter(tbl *ast.Table) (models.Filter, error) {
 						}
 					}
 					f.TagPass = append(f.TagPass, *tagfilter)
-					f.IsActive = true
 				}
 			}
 		}
@@ -749,7 +744,6 @@ func buildFilter(tbl *ast.Table) (models.Filter, error) {
 						}
 					}
 					f.TagDrop = append(f.TagDrop, *tagfilter)
-					f.IsActive = true
 				}
 			}
 		}
@@ -778,7 +772,7 @@ func buildFilter(tbl *ast.Table) (models.Filter, error) {
 			}
 		}
 	}
-	if err := f.CompileFilter(); err != nil {
+	if err := f.Compile(); err != nil {
 		return f, err
 	}
 
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index cb8c9192cd393a8bab8376465b184e990d9d0a23..3498d815d0078560d73adbc98e0e38645ca55b69 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -43,9 +43,8 @@ func TestConfig_LoadSingleInputWithEnvVars(t *testing.T) {
 				Filter: []string{"mytag"},
 			},
 		},
-		IsActive: true,
 	}
-	assert.NoError(t, filter.CompileFilter())
+	assert.NoError(t, filter.Compile())
 	mConfig := &models.InputConfig{
 		Name:     "memcached",
 		Filter:   filter,
@@ -83,9 +82,8 @@ func TestConfig_LoadSingleInput(t *testing.T) {
 				Filter: []string{"mytag"},
 			},
 		},
-		IsActive: true,
 	}
-	assert.NoError(t, filter.CompileFilter())
+	assert.NoError(t, filter.Compile())
 	mConfig := &models.InputConfig{
 		Name:     "memcached",
 		Filter:   filter,
@@ -130,9 +128,8 @@ func TestConfig_LoadDirectory(t *testing.T) {
 				Filter: []string{"mytag"},
 			},
 		},
-		IsActive: true,
 	}
-	assert.NoError(t, filter.CompileFilter())
+	assert.NoError(t, filter.Compile())
 	mConfig := &models.InputConfig{
 		Name:     "memcached",
 		Filter:   filter,
diff --git a/internal/models/filter.go b/internal/models/filter.go
index 9ad4c00498f183024899a749d7ff5d876f8e6fdf..b87c595012ab5fcbc903c528f4c3e303b3c3cfe3 100644
--- a/internal/models/filter.go
+++ b/internal/models/filter.go
@@ -3,7 +3,6 @@ package models
 import (
 	"fmt"
 
-	"github.com/influxdata/telegraf"
 	"github.com/influxdata/telegraf/filter"
 )
 
@@ -34,47 +33,59 @@ type Filter struct {
 	TagInclude []string
 	tagInclude filter.Filter
 
-	IsActive bool
+	isActive bool
 }
 
 // Compile all Filter lists into filter.Filter objects.
-func (f *Filter) CompileFilter() error {
+func (f *Filter) Compile() error {
+	if len(f.NameDrop) == 0 &&
+		len(f.NamePass) == 0 &&
+		len(f.FieldDrop) == 0 &&
+		len(f.FieldPass) == 0 &&
+		len(f.TagInclude) == 0 &&
+		len(f.TagExclude) == 0 &&
+		len(f.TagPass) == 0 &&
+		len(f.TagDrop) == 0 {
+		return nil
+	}
+
+	f.isActive = true
 	var err error
-	f.nameDrop, err = filter.CompileFilter(f.NameDrop)
+	f.nameDrop, err = filter.Compile(f.NameDrop)
 	if err != nil {
 		return fmt.Errorf("Error compiling 'namedrop', %s", err)
 	}
-	f.namePass, err = filter.CompileFilter(f.NamePass)
+	f.namePass, err = filter.Compile(f.NamePass)
 	if err != nil {
 		return fmt.Errorf("Error compiling 'namepass', %s", err)
 	}
 
-	f.fieldDrop, err = filter.CompileFilter(f.FieldDrop)
+	f.fieldDrop, err = filter.Compile(f.FieldDrop)
 	if err != nil {
 		return fmt.Errorf("Error compiling 'fielddrop', %s", err)
 	}
-	f.fieldPass, err = filter.CompileFilter(f.FieldPass)
+	f.fieldPass, err = filter.Compile(f.FieldPass)
 	if err != nil {
 		return fmt.Errorf("Error compiling 'fieldpass', %s", err)
 	}
 
-	f.tagExclude, err = filter.CompileFilter(f.TagExclude)
+	f.tagExclude, err = filter.Compile(f.TagExclude)
 	if err != nil {
 		return fmt.Errorf("Error compiling 'tagexclude', %s", err)
 	}
-	f.tagInclude, err = filter.CompileFilter(f.TagInclude)
+	f.tagInclude, err = filter.Compile(f.TagInclude)
 	if err != nil {
 		return fmt.Errorf("Error compiling 'taginclude', %s", err)
 	}
 
 	for i, _ := range f.TagDrop {
-		f.TagDrop[i].filter, err = filter.CompileFilter(f.TagDrop[i].Filter)
+		f.TagDrop[i].filter, err = filter.Compile(f.TagDrop[i].Filter)
 		if err != nil {
 			return fmt.Errorf("Error compiling 'tagdrop', %s", err)
 		}
 	}
 	for i, _ := range f.TagPass {
-		f.TagPass[i].filter, err = filter.CompileFilter(f.TagPass[i].Filter)
+		f.TagPass[i].filter, err = filter.Compile(f.TagPass[i].Filter)
 		if err != nil {
 			return fmt.Errorf("Error compiling 'tagpass', %s", err)
 		}
@@ -82,16 +93,52 @@ func (f *Filter) CompileFilter() error {
 	return nil
 }
 
-func (f *Filter) ShouldMetricPass(metric telegraf.Metric) bool {
-	if f.ShouldNamePass(metric.Name()) && f.ShouldTagsPass(metric.Tags()) {
+// Apply applies the filter to the given measurement name, fields map, and
+// tags map. It will return false if the metric should be "filtered out", and
+// true if the metric should "pass".
+// It will modify tags in-place if they need to be deleted.
+func (f *Filter) Apply(
+	measurement string,
+	fields map[string]interface{},
+	tags map[string]string,
+) bool {
+	if !f.isActive {
 		return true
 	}
-	return false
+
+	// check if the measurement name should pass
+	if !f.shouldNamePass(measurement) {
+		return false
+	}
+
+	// check if the tags should pass
+	if !f.shouldTagsPass(tags) {
+		return false
+	}
+
+	// filter fields
+	for fieldkey, _ := range fields {
+		if !f.shouldFieldPass(fieldkey) {
+			delete(fields, fieldkey)
+		}
+	}
+	if len(fields) == 0 {
+		return false
+	}
+
+	// filter tags
+	f.filterTags(tags)
+
+	return true
+}
+
+func (f *Filter) IsActive() bool {
+	return f.isActive
 }
 
-// ShouldFieldsPass returns true if the metric should pass, false if should drop
+// shouldNamePass returns true if the metric should pass, false if should drop
 // based on the drop/pass filter parameters
-func (f *Filter) ShouldNamePass(key string) bool {
+func (f *Filter) shouldNamePass(key string) bool {
 	if f.namePass != nil {
 		if f.namePass.Match(key) {
 			return true
@@ -107,9 +154,9 @@ func (f *Filter) ShouldNamePass(key string) bool {
 	return true
 }
 
-// ShouldFieldsPass returns true if the metric should pass, false if should drop
+// shouldFieldPass returns true if the metric should pass, false if should drop
 // based on the drop/pass filter parameters
-func (f *Filter) ShouldFieldsPass(key string) bool {
+func (f *Filter) shouldFieldPass(key string) bool {
 	if f.fieldPass != nil {
 		if f.fieldPass.Match(key) {
 			return true
@@ -125,9 +172,9 @@ func (f *Filter) ShouldFieldsPass(key string) bool {
 	return true
 }
 
-// ShouldTagsPass returns true if the metric should pass, false if should drop
+// shouldTagsPass returns true if the metric should pass, false if should drop
 // based on the tagdrop/tagpass filter parameters
-func (f *Filter) ShouldTagsPass(tags map[string]string) bool {
+func (f *Filter) shouldTagsPass(tags map[string]string) bool {
 	if f.TagPass != nil {
 		for _, pat := range f.TagPass {
 			if pat.filter == nil {
@@ -161,7 +208,7 @@ func (f *Filter) ShouldTagsPass(tags map[string]string) bool {
 
 // Apply TagInclude and TagExclude filters.
 // modifies the tags map in-place.
-func (f *Filter) FilterTags(tags map[string]string) {
+func (f *Filter) filterTags(tags map[string]string) {
 	if f.tagInclude != nil {
 		for k, _ := range tags {
 			if !f.tagInclude.Match(k) {
diff --git a/internal/models/filter_test.go b/internal/models/filter_test.go
index 497d08532fa848190e04e5bd93706fc679160adc..95b63e30a711f32153066cb2142c15b7f44f7990 100644
--- a/internal/models/filter_test.go
+++ b/internal/models/filter_test.go
@@ -3,12 +3,62 @@ package models
 import (
 	"testing"
 
-	"github.com/influxdata/telegraf/testutil"
-
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
+func TestFilter_ApplyEmpty(t *testing.T) {
+	f := Filter{}
+	require.NoError(t, f.Compile())
+	assert.False(t, f.IsActive())
+
+	assert.True(t, f.Apply("m", map[string]interface{}{"value": int64(1)}, map[string]string{}))
+}
+
+func TestFilter_ApplyTagsDontPass(t *testing.T) {
+	filters := []TagFilter{
+		TagFilter{
+			Name:   "cpu",
+			Filter: []string{"cpu-*"},
+		},
+	}
+	f := Filter{
+		TagDrop: filters,
+	}
+	require.NoError(t, f.Compile())
+	require.NoError(t, f.Compile())
+	assert.True(t, f.IsActive())
+
+	assert.False(t, f.Apply("m",
+		map[string]interface{}{"value": int64(1)},
+		map[string]string{"cpu": "cpu-total"}))
+}
+
+func TestFilter_ApplyDeleteFields(t *testing.T) {
+	f := Filter{
+		FieldDrop: []string{"value"},
+	}
+	require.NoError(t, f.Compile())
+	require.NoError(t, f.Compile())
+	assert.True(t, f.IsActive())
+
+	fields := map[string]interface{}{"value": int64(1), "value2": int64(2)}
+	assert.True(t, f.Apply("m", fields, nil))
+	assert.Equal(t, map[string]interface{}{"value2": int64(2)}, fields)
+}
+
+func TestFilter_ApplyDeleteAllFields(t *testing.T) {
+	f := Filter{
+		FieldDrop: []string{"value*"},
+	}
+	require.NoError(t, f.Compile())
+	require.NoError(t, f.Compile())
+	assert.True(t, f.IsActive())
+
+	fields := map[string]interface{}{"value": int64(1), "value2": int64(2)}
+	assert.False(t, f.Apply("m", fields, nil))
+}
+
 func TestFilter_Empty(t *testing.T) {
 	f := Filter{}
 
@@ -23,7 +73,7 @@ func TestFilter_Empty(t *testing.T) {
 	}
 
 	for _, measurement := range measurements {
-		if !f.ShouldFieldsPass(measurement) {
+		if !f.shouldFieldPass(measurement) {
 			t.Errorf("Expected measurement %s to pass", measurement)
 		}
 	}
@@ -33,7 +83,7 @@ func TestFilter_NamePass(t *testing.T) {
 	f := Filter{
 		NamePass: []string{"foo*", "cpu_usage_idle"},
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
 	passes := []string{
 		"foo",
@@ -51,13 +101,13 @@ func TestFilter_NamePass(t *testing.T) {
 	}
 
 	for _, measurement := range passes {
-		if !f.ShouldNamePass(measurement) {
+		if !f.shouldNamePass(measurement) {
 			t.Errorf("Expected measurement %s to pass", measurement)
 		}
 	}
 
 	for _, measurement := range drops {
-		if f.ShouldNamePass(measurement) {
+		if f.shouldNamePass(measurement) {
 			t.Errorf("Expected measurement %s to drop", measurement)
 		}
 	}
@@ -67,7 +117,7 @@ func TestFilter_NameDrop(t *testing.T) {
 	f := Filter{
 		NameDrop: []string{"foo*", "cpu_usage_idle"},
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
 	drops := []string{
 		"foo",
@@ -85,13 +135,13 @@ func TestFilter_NameDrop(t *testing.T) {
 	}
 
 	for _, measurement := range passes {
-		if !f.ShouldNamePass(measurement) {
+		if !f.shouldNamePass(measurement) {
 			t.Errorf("Expected measurement %s to pass", measurement)
 		}
 	}
 
 	for _, measurement := range drops {
-		if f.ShouldNamePass(measurement) {
+		if f.shouldNamePass(measurement) {
 			t.Errorf("Expected measurement %s to drop", measurement)
 		}
 	}
@@ -101,7 +151,7 @@ func TestFilter_FieldPass(t *testing.T) {
 	f := Filter{
 		FieldPass: []string{"foo*", "cpu_usage_idle"},
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
 	passes := []string{
 		"foo",
@@ -119,13 +169,13 @@ func TestFilter_FieldPass(t *testing.T) {
 	}
 
 	for _, measurement := range passes {
-		if !f.ShouldFieldsPass(measurement) {
+		if !f.shouldFieldPass(measurement) {
 			t.Errorf("Expected measurement %s to pass", measurement)
 		}
 	}
 
 	for _, measurement := range drops {
-		if f.ShouldFieldsPass(measurement) {
+		if f.shouldFieldPass(measurement) {
 			t.Errorf("Expected measurement %s to drop", measurement)
 		}
 	}
@@ -135,7 +185,7 @@ func TestFilter_FieldDrop(t *testing.T) {
 	f := Filter{
 		FieldDrop: []string{"foo*", "cpu_usage_idle"},
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
 	drops := []string{
 		"foo",
@@ -153,13 +203,13 @@ func TestFilter_FieldDrop(t *testing.T) {
 	}
 
 	for _, measurement := range passes {
-		if !f.ShouldFieldsPass(measurement) {
+		if !f.shouldFieldPass(measurement) {
 			t.Errorf("Expected measurement %s to pass", measurement)
 		}
 	}
 
 	for _, measurement := range drops {
-		if f.ShouldFieldsPass(measurement) {
+		if f.shouldFieldPass(measurement) {
 			t.Errorf("Expected measurement %s to drop", measurement)
 		}
 	}
@@ -178,7 +228,7 @@ func TestFilter_TagPass(t *testing.T) {
 	f := Filter{
 		TagPass: filters,
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
 	passes := []map[string]string{
 		{"cpu": "cpu-total"},
@@ -197,13 +247,13 @@ func TestFilter_TagPass(t *testing.T) {
 	}
 
 	for _, tags := range passes {
-		if !f.ShouldTagsPass(tags) {
+		if !f.shouldTagsPass(tags) {
 			t.Errorf("Expected tags %v to pass", tags)
 		}
 	}
 
 	for _, tags := range drops {
-		if f.ShouldTagsPass(tags) {
+		if f.shouldTagsPass(tags) {
 			t.Errorf("Expected tags %v to drop", tags)
 		}
 	}
@@ -222,7 +272,7 @@ func TestFilter_TagDrop(t *testing.T) {
 	f := Filter{
 		TagDrop: filters,
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
 	drops := []map[string]string{
 		{"cpu": "cpu-total"},
@@ -241,30 +291,18 @@ func TestFilter_TagDrop(t *testing.T) {
 	}
 
 	for _, tags := range passes {
-		if !f.ShouldTagsPass(tags) {
+		if !f.shouldTagsPass(tags) {
 			t.Errorf("Expected tags %v to pass", tags)
 		}
 	}
 
 	for _, tags := range drops {
-		if f.ShouldTagsPass(tags) {
+		if f.shouldTagsPass(tags) {
 			t.Errorf("Expected tags %v to drop", tags)
 		}
 	}
 }
 
-func TestFilter_ShouldMetricsPass(t *testing.T) {
-	m := testutil.TestMetric(1, "testmetric")
-	f := Filter{
-		NameDrop: []string{"foobar"},
-	}
-	require.NoError(t, f.CompileFilter())
-	require.True(t, f.ShouldMetricPass(m))
-
-	m = testutil.TestMetric(1, "foobar")
-	require.False(t, f.ShouldMetricPass(m))
-}
-
 func TestFilter_FilterTagsNoMatches(t *testing.T) {
 	pretags := map[string]string{
 		"host":  "localhost",
@@ -273,9 +311,9 @@ func TestFilter_FilterTagsNoMatches(t *testing.T) {
 	f := Filter{
 		TagExclude: []string{"nomatch"},
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
-	f.FilterTags(pretags)
+	f.filterTags(pretags)
 	assert.Equal(t, map[string]string{
 		"host":  "localhost",
 		"mytag": "foobar",
@@ -284,9 +322,9 @@ func TestFilter_FilterTagsNoMatches(t *testing.T) {
 	f = Filter{
 		TagInclude: []string{"nomatch"},
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
-	f.FilterTags(pretags)
+	f.filterTags(pretags)
 	assert.Equal(t, map[string]string{}, pretags)
 }
 
@@ -298,9 +336,9 @@ func TestFilter_FilterTagsMatches(t *testing.T) {
 	f := Filter{
 		TagExclude: []string{"ho*"},
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
-	f.FilterTags(pretags)
+	f.filterTags(pretags)
 	assert.Equal(t, map[string]string{
 		"mytag": "foobar",
 	}, pretags)
@@ -312,9 +350,9 @@ func TestFilter_FilterTagsMatches(t *testing.T) {
 	f = Filter{
 		TagInclude: []string{"my*"},
 	}
-	require.NoError(t, f.CompileFilter())
+	require.NoError(t, f.Compile())
 
-	f.FilterTags(pretags)
+	f.filterTags(pretags)
 	assert.Equal(t, map[string]string{
 		"mytag": "foobar",
 	}, pretags)
diff --git a/internal/models/running_output.go b/internal/models/running_output.go
index 82a6885d53d5474fa5899872219344b07fb54a98..c4de4afd95d7febd1b989be5c6722044635d2ec3 100644
--- a/internal/models/running_output.go
+++ b/internal/models/running_output.go
@@ -57,21 +57,17 @@ func NewRunningOutput(
 // AddMetric adds a metric to the output. This function can also write cached
 // points if FlushBufferWhenFull is true.
 func (ro *RunningOutput) AddMetric(metric telegraf.Metric) {
-	if ro.Config.Filter.IsActive {
-		if !ro.Config.Filter.ShouldMetricPass(metric) {
-			return
-		}
-	}
-
 	// Filter any tagexclude/taginclude parameters before adding metric
-	if len(ro.Config.Filter.TagExclude) != 0 || len(ro.Config.Filter.TagInclude) != 0 {
+	if ro.Config.Filter.IsActive() {
 		// In order to filter out tags, we need to create a new metric, since
 		// metrics are immutable once created.
+		name := metric.Name()
 		tags := metric.Tags()
 		fields := metric.Fields()
 		t := metric.Time()
-		name := metric.Name()
-		ro.Config.Filter.FilterTags(tags)
+		if ok := ro.Config.Filter.Apply(name, fields, tags); !ok {
+			return
+		}
 		// error is not possible if creating from another metric, so ignore.
 		metric, _ = telegraf.NewMetric(name, tags, fields, t)
 	}
diff --git a/internal/models/running_output_test.go b/internal/models/running_output_test.go
index a552629e9c05a1df1f7746cdf98b837e741d8cb8..a42d6fc7e6ff298ed42ee31eb01a271c065a4d2c 100644
--- a/internal/models/running_output_test.go
+++ b/internal/models/running_output_test.go
@@ -31,9 +31,7 @@ var next5 = []telegraf.Metric{
 // Benchmark adding metrics.
 func BenchmarkRunningOutputAddWrite(b *testing.B) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &perfOutput{}
@@ -49,9 +47,7 @@ func BenchmarkRunningOutputAddWrite(b *testing.B) {
 // Benchmark adding metrics.
 func BenchmarkRunningOutputAddWriteEvery100(b *testing.B) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &perfOutput{}
@@ -69,9 +65,7 @@ func BenchmarkRunningOutputAddWriteEvery100(b *testing.B) {
 // Benchmark adding metrics.
 func BenchmarkRunningOutputAddFailWrites(b *testing.B) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &perfOutput{}
@@ -88,11 +82,10 @@ func BenchmarkRunningOutputAddFailWrites(b *testing.B) {
 func TestRunningOutput_DropFilter(t *testing.T) {
 	conf := &OutputConfig{
 		Filter: Filter{
-			IsActive: true,
 			NameDrop: []string{"metric1", "metric2"},
 		},
 	}
-	assert.NoError(t, conf.Filter.CompileFilter())
+	assert.NoError(t, conf.Filter.Compile())
 
 	m := &mockOutput{}
 	ro := NewRunningOutput("test", m, conf, 1000, 10000)
@@ -114,11 +107,10 @@ func TestRunningOutput_DropFilter(t *testing.T) {
 func TestRunningOutput_PassFilter(t *testing.T) {
 	conf := &OutputConfig{
 		Filter: Filter{
-			IsActive: true,
 			NameDrop: []string{"metric1000", "foo*"},
 		},
 	}
-	assert.NoError(t, conf.Filter.CompileFilter())
+	assert.NoError(t, conf.Filter.Compile())
 
 	m := &mockOutput{}
 	ro := NewRunningOutput("test", m, conf, 1000, 10000)
@@ -140,11 +132,11 @@ func TestRunningOutput_PassFilter(t *testing.T) {
 func TestRunningOutput_TagIncludeNoMatch(t *testing.T) {
 	conf := &OutputConfig{
 		Filter: Filter{
-			IsActive:   true,
+
 			TagInclude: []string{"nothing*"},
 		},
 	}
-	assert.NoError(t, conf.Filter.CompileFilter())
+	assert.NoError(t, conf.Filter.Compile())
 
 	m := &mockOutput{}
 	ro := NewRunningOutput("test", m, conf, 1000, 10000)
@@ -162,11 +154,11 @@ func TestRunningOutput_TagIncludeNoMatch(t *testing.T) {
 func TestRunningOutput_TagExcludeMatch(t *testing.T) {
 	conf := &OutputConfig{
 		Filter: Filter{
-			IsActive:   true,
+
 			TagExclude: []string{"tag*"},
 		},
 	}
-	assert.NoError(t, conf.Filter.CompileFilter())
+	assert.NoError(t, conf.Filter.Compile())
 
 	m := &mockOutput{}
 	ro := NewRunningOutput("test", m, conf, 1000, 10000)
@@ -184,11 +176,11 @@ func TestRunningOutput_TagExcludeMatch(t *testing.T) {
 func TestRunningOutput_TagExcludeNoMatch(t *testing.T) {
 	conf := &OutputConfig{
 		Filter: Filter{
-			IsActive:   true,
+
 			TagExclude: []string{"nothing*"},
 		},
 	}
-	assert.NoError(t, conf.Filter.CompileFilter())
+	assert.NoError(t, conf.Filter.Compile())
 
 	m := &mockOutput{}
 	ro := NewRunningOutput("test", m, conf, 1000, 10000)
@@ -206,11 +198,11 @@ func TestRunningOutput_TagExcludeNoMatch(t *testing.T) {
 func TestRunningOutput_TagIncludeMatch(t *testing.T) {
 	conf := &OutputConfig{
 		Filter: Filter{
-			IsActive:   true,
+
 			TagInclude: []string{"tag*"},
 		},
 	}
-	assert.NoError(t, conf.Filter.CompileFilter())
+	assert.NoError(t, conf.Filter.Compile())
 
 	m := &mockOutput{}
 	ro := NewRunningOutput("test", m, conf, 1000, 10000)
@@ -227,9 +219,7 @@ func TestRunningOutput_TagIncludeMatch(t *testing.T) {
 // Test that we can write metrics with simple default setup.
 func TestRunningOutputDefault(t *testing.T) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &mockOutput{}
@@ -252,9 +242,7 @@ func TestRunningOutputDefault(t *testing.T) {
 // FlushBufferWhenFull is set.
 func TestRunningOutputFlushWhenFull(t *testing.T) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &mockOutput{}
@@ -283,9 +271,7 @@ func TestRunningOutputFlushWhenFull(t *testing.T) {
 // FlushBufferWhenFull is set, twice.
 func TestRunningOutputMultiFlushWhenFull(t *testing.T) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &mockOutput{}
@@ -304,9 +290,7 @@ func TestRunningOutputMultiFlushWhenFull(t *testing.T) {
 
 func TestRunningOutputWriteFail(t *testing.T) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &mockOutput{}
@@ -339,9 +323,7 @@ func TestRunningOutputWriteFail(t *testing.T) {
 // Verify that the order of points is preserved during a write failure.
 func TestRunningOutputWriteFailOrder(t *testing.T) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &mockOutput{}
@@ -379,9 +361,7 @@ func TestRunningOutputWriteFailOrder(t *testing.T) {
 // Verify that the order of points is preserved during many write failures.
 func TestRunningOutputWriteFailOrder2(t *testing.T) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &mockOutput{}
@@ -452,9 +432,7 @@ func TestRunningOutputWriteFailOrder2(t *testing.T) {
 //
 func TestRunningOutputWriteFailOrder3(t *testing.T) {
 	conf := &OutputConfig{
-		Filter: Filter{
-			IsActive: false,
-		},
+		Filter: Filter{},
 	}
 
 	m := &mockOutput{}
diff --git a/plugins/inputs/varnish/varnish.go b/plugins/inputs/varnish/varnish.go
index 2b0e84514d1ff38c4bff64203353422bb2ea5717..229a5e8b0db7157c6428a86c3b4116bb4336e6e3 100644
--- a/plugins/inputs/varnish/varnish.go
+++ b/plugins/inputs/varnish/varnish.go
@@ -77,13 +77,13 @@ func (s *Varnish) Gather(acc telegraf.Accumulator) error {
 	if s.filter == nil {
 		var err error
 		if len(s.Stats) == 0 {
-			s.filter, err = filter.CompileFilter(defaultStats)
+			s.filter, err = filter.Compile(defaultStats)
 		} else {
 			// legacy support, change "all" -> "*":
 			if s.Stats[0] == "all" {
 				s.Stats[0] = "*"
 			}
-			s.filter, err = filter.CompileFilter(s.Stats)
+			s.filter, err = filter.Compile(s.Stats)
 		}
 		if err != nil {
 			return err