Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e520901
Add log type include filter
jorbaum Jan 5, 2026
6084cff
Support exclude and include
jorbaum Jan 5, 2026
13eb748
Error when using both include and exclude types
jorbaum Jan 5, 2026
0e81cde
Introduce logging to binding config
jorbaum Jan 8, 2026
a7855b6
Add some more unit tests
jorbaum Jan 8, 2026
23e92ed
Fix wrong word
jorbaum Jan 12, 2026
553cd46
Create LogType as string, move to package
jorbaum Jan 12, 2026
27cf2be
Prefix log type constants with LOG_
jorbaum Jan 12, 2026
a05a630
Rename value to sourceType
jorbaum Jan 13, 2026
100a721
Only emit one warning on wrong drain config
jorbaum Jan 13, 2026
d009752
Rename DrainParameterParser in test
jorbaum Jan 13, 2026
4553280
Add tests for the include and exclude filters
jorbaum Jan 13, 2026
c832950
Move new log filter test to table test
jorbaum Jan 19, 2026
9b7d458
Move next test to table
jorbaum Jan 19, 2026
ba2b7ab
Move drain data for old parameter test to table
jorbaum Jan 19, 2026
f5aa7bc
Move log filter validation to binding_fetcher
jorbaum Feb 3, 2026
da09b15
Dereference pointer to make an actual copy
jorbaum Feb 2, 2026
9d442da
Rename log type to source type where possible
jorbaum Feb 11, 2026
e733792
Only exclude filter shall pass unknown source type
jorbaum Feb 11, 2026
a1ec8d1
Add SourceType PROXY, HEALTH, SYS and STATS
jorbaum Feb 11, 2026
dd42633
Make missing source_type tests clearer
jorbaum Feb 20, 2026
9f7a5df
Add tests for no filtering value configured
jorbaum Feb 20, 2026
a520a28
Rename source to log source where sensible
jorbaum Feb 20, 2026
7ba8c60
Remove unused method
jorbaum Feb 20, 2026
4184c3c
Add -log to uri query param
jorbaum Feb 20, 2026
f1bb2a0
No longer accept URI drains with space in values
jorbaum Feb 20, 2026
6385a74
Move log source type parsing into common function
jorbaum Feb 20, 2026
ee5b2b8
Group tests where source_type is present
jorbaum Mar 17, 2026
efb81ed
Use new package name in test
jorbaum Mar 26, 2026
6ff68b3
Add filtering validation to poller
jorbaum Mar 26, 2026
97231b5
Fix cyclomatic complexity by refactoring poller
jorbaum Mar 26, 2026
c90ecf3
Also move filtering tests to poller
jorbaum Mar 26, 2026
86b5879
Follow refactoring of FilterebBindingFetcher
jorbaum Jul 10, 2026
af1453a
Support all
jorbaum Jul 17, 2026
2143322
Filter envelopes by source type just like logs
jorbaum Jul 17, 2026
a014672
Clarify event forwarding via drain-type param
jorbaum Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/pkg/binding/poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import (
"net"
"net/http"
"net/url"
"strings"
"time"

metrics "code.cloudfoundry.org/go-metric-registry"
"code.cloudfoundry.org/loggregator-agent-release/src/pkg/egress/syslog"
v2 "code.cloudfoundry.org/loggregator-agent-release/src/pkg/ingress/v2"
"code.cloudfoundry.org/loggregator-agent-release/src/pkg/simplecache"
)
Expand Down Expand Up @@ -236,6 +238,17 @@ func (bc *bindingChecker) checkBindings(bindings []Binding) []Binding {
continue
}

if invalidLogFilter(u) {
bc.rejectBinding(b.Credentials, fmt.Sprintf("include-log-source-types and exclude-log-source-types cannot be used at the same time in syslog drain url %s", anonymousUrl.String()), true)
continue
}

sourceTypes := getUnknownSourceTypes(u.Query())
if sourceTypes != nil {
bc.rejectBinding(b.Credentials, fmt.Sprintf("Unknown source types '%s' in source type filter in syslog drain url %s", strings.Join(sourceTypes, ", "), anonymousUrl.String()), true)
continue
}

_, exists := bc.failedHostsCache.Get(u.Host)
if exists {
bc.rejectBinding(b.Credentials, fmt.Sprintf("Skipped resolve ip address for syslog drain with url %s due to prior failure", anonymousUrl.String()), false)
Expand Down Expand Up @@ -308,6 +321,34 @@ func invalidScheme(scheme string) bool {
return true
}

// invalidLogFilter checks if both include-log-source-types and exclude-log-source-types
func invalidLogFilter(u *url.URL) bool {
includeSourceTypes := u.Query().Get("include-log-source-types")
excludeSourceTypes := u.Query().Get("exclude-log-source-types")
if excludeSourceTypes != "" && includeSourceTypes != "" {
return true
}
return false
}

// assumes only one of include-log-source-types or exclude-log-source-types is set
func getUnknownSourceTypes(u url.Values) []string {
var sourceTypeList string
includeSourceTypes := u.Get("include-log-source-types")
excludeSourceTypes := u.Get("exclude-log-source-types")

if includeSourceTypes != "" {
sourceTypeList = includeSourceTypes
} else if excludeSourceTypes != "" {
sourceTypeList = excludeSourceTypes
} else {
return nil
}

_, unknownTypes := syslog.ParseSourceTypeList(sourceTypeList)
return unknownTypes
}

func CalculateBindingCount(bindings []Binding) int {
apps := make(map[string]bool)
for _, b := range bindings {
Expand Down
119 changes: 119 additions & 0 deletions src/pkg/binding/poller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,125 @@ var _ = Describe("Poller", func() {
Expect(bndChecker.invalidDrains).To(Equal(float64(0)))
Expect(bndChecker.blacklistedDrains).To(Equal(float64(0)))
})

Context("when both include-log-source-types and exclude-log-source-types are specified", func() {
It("ignores the drain and counts as invalid", func() {
bindings := []Binding{
{
Url: "https://test.org/drain?include-log-source-types=app&exclude-log-source-types=rtr",
Credentials: []Credentials{
{
Apps: []App{{Hostname: "app-hostname0", AppID: "app-id-0"}},
},
},
},
}

filteredBindings := bndChecker.checkBindings(bindings)

Expect(filteredBindings).To(BeEmpty())
Expect(logClient.Message()).To(ContainElement(MatchRegexp("include-log-source-types and exclude-log-source-types cannot be used at the same time")))
Expect(bndChecker.invalidDrains).To(BeNumerically("==", 1))
Expect(bndChecker.blacklistedDrains).To(BeNumerically("==", 0))
})

It("doesn't log the conflicting filters warning when warn is false", func() {
bndChecker.warn = false
bindings := []Binding{
{
Url: "https://test.org/drain?include-log-source-types=app&exclude-log-source-types=rtr",
Credentials: []Credentials{
{
Apps: []App{{Hostname: "app-hostname0", AppID: "app-id-0"}},
},
},
},
}
bndChecker.checkBindings(bindings)

for _, msg := range logClient.Message() {
Expect(msg).ToNot(MatchRegexp("include-log-source-types and exclude-log-source-types cannot be used at the same time"))
}
})
})

Context("when unknown source types are provided", func() {
It("logs a warning and ignores the drain in include mode", func() {
bindings := []Binding{
{
Url: "https://test.org/drain?include-log-source-types=app,unknown,invalid,rtr",
Credentials: []Credentials{
{
Apps: []App{{Hostname: "app-hostname0", AppID: "app-id-0"}},
},
},
},
}
filteredBindings := bndChecker.checkBindings(bindings)

Expect(filteredBindings).To(BeEmpty())
Expect(logClient.Message()).To(ContainElement(MatchRegexp("Unknown source types")))
Expect(logClient.Message()).To(ContainElement(MatchRegexp("unknown")))
Expect(logClient.Message()).To(ContainElement(MatchRegexp("invalid")))
Expect(bndChecker.invalidDrains).To(BeNumerically("==", 1))
})

It("logs a warning and ignores the drain in exclude mode", func() {
bindings := []Binding{
{
Url: "https://test.org/drain?exclude-log-source-types=rtr,unknown",
Credentials: []Credentials{
{
Apps: []App{{Hostname: "app-hostname0", AppID: "app-id-0"}},
},
},
},
}
filteredBindings := bndChecker.checkBindings(bindings)

Expect(filteredBindings).To(BeEmpty())
Expect(logClient.Message()).To(ContainElement(MatchRegexp("Unknown source types")))
Expect(logClient.Message()).To(ContainElement(MatchRegexp("unknown")))
Expect(bndChecker.invalidDrains).To(BeNumerically("==", 1))
})

It("logs a warning and ignores the drain when source types have spaces", func() {
bindings := []Binding{
{
Url: "https://test.org/drain?include-log-source-types=app, rtr",
Credentials: []Credentials{
{
Apps: []App{{Hostname: "app-hostname0", AppID: "app-id-0"}},
},
},
},
}
filteredBindings := bndChecker.checkBindings(bindings)

Expect(filteredBindings).To(BeEmpty())
Expect(logClient.Message()).To(ContainElement(MatchRegexp("Unknown source types")))
Expect(bndChecker.invalidDrains).To(BeNumerically("==", 1))
})

It("doesn't log the warning when warn is false", func() {
bndChecker.warn = false
bindings := []Binding{
{
Url: "https://test.org/drain?include-log-source-types=app,unknown,rtr",
Credentials: []Credentials{
{
Apps: []App{{Hostname: "app-hostname0", AppID: "app-id-0"}},
},
},
},
}
bndChecker.checkBindings(bindings)

for _, msg := range logClient.Message() {
Expect(msg).ToNot(MatchRegexp("Unknown source types"))
}
})
})
})
})

Expand Down
39 changes: 17 additions & 22 deletions src/pkg/egress/syslog/filtering_drain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,18 @@ func NewFilteringDrainWriter(binding Binding, writer egress.Writer) (*FilteringD
}

func (w *FilteringDrainWriter) Write(env *loggregator_v2.Envelope) error {
if w.binding.DrainData == ALL {
return w.writer.Write(env)
}

if env.GetTimer() != nil {
if w.binding.DrainData == TRACES {
if w.binding.DrainData == TRACES || w.binding.DrainData == ALL {
return w.writer.Write(env)
}
}
if env.GetEvent() != nil {
if w.binding.DrainData == LOGS {
if sendsEvents(w.binding.DrainData, w.binding.LogFilter, env.GetTags()["source_type"]) {
return w.writer.Write(env)
}
}
if env.GetLog() != nil {
if sendsLogs(w.binding.DrainData) {
if sendsLogs(w.binding.DrainData, w.binding.LogFilter, env.GetTags()["source_type"]) {
return w.writer.Write(env)
}
}
Expand All @@ -63,26 +59,25 @@ func (w *FilteringDrainWriter) Write(env *loggregator_v2.Envelope) error {
return nil
}

func sendsLogs(drainData DrainData) bool {
switch drainData {
case LOGS:
return true
case LOGS_AND_METRICS:
return true
case LOGS_NO_EVENTS:
return true
default:
func sendsEvents(drainData DrainData, logFilter *LogFilter, sourceTypeTag string) bool {
if drainData != LOGS && drainData != ALL {
return false
}

return logFilter.ShouldInclude(sourceTypeTag)
}

func sendsLogs(drainData DrainData, logFilter *LogFilter, sourceTypeTag string) bool {
if drainData == TRACES || drainData == METRICS {
return false
}

return logFilter.ShouldInclude(sourceTypeTag)
}

func sendsMetrics(drainData DrainData) bool {
switch drainData {
case LOGS_AND_METRICS:
return true
case METRICS:
return true
default:
if drainData != LOGS_AND_METRICS && drainData != METRICS && drainData != ALL {
return false
}
return true
}
Loading
Loading