Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ if .log_source == "container" {
}
if .log_type == "audit" {
._syslog.app_name = .log_source
._syslog.severity = "informational"
._syslog.severity = "info"
._syslog.facility = "security"
}
._syslog.proc_id = to_string!(._syslog.proc_id || "")
if exists(._syslog.proc_id) && is_empty(strip_whitespace(string!(._syslog.proc_id))) { del(._syslog.proc_id) }
._syslog.proc_id = to_string(._syslog.proc_id)
if is_empty(strip_whitespace(._syslog.proc_id)) { del(._syslog.proc_id) }
._syslog.app_name = to_string!(._syslog.app_name || "")

# try to convert syslog code to the facility, severity names (e.g. 4 -> "warning", "4" -> "warning" )
Expand All @@ -51,6 +51,11 @@ if exists(._syslog.facility) && !is_null(._syslog.facility) {
}

if exists(._syslog.severity) && !is_null(._syslog.severity) {
if is_string(._syslog.severity) {
._syslog.severity = downcase(._syslog.severity) ?? ._syslog.severity
if ._syslog.severity == "emergency" { ._syslog.severity = "emerg" } else if ._syslog.severity == "critical" { ._syslog.severity = "crit" } else if ._syslog.severity == "informational" { ._syslog.severity = "info" }
}

_, err = to_syslog_severity(._syslog.severity)
if err != null {
# Field is not a valid name — try treating it as a code (int or string int)
Expand Down
56 changes: 49 additions & 7 deletions internal/generator/vector/output/syslog/syslog.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const (
vrlKeySyslogAppName = "._syslog.app_name"
vrlKeySyslogMsgID = "._syslog.msg_id"

defProcIdRFC3164 = `to_string!(._syslog.proc_id || "")
if exists(._syslog.proc_id) && is_empty(strip_whitespace(string!(._syslog.proc_id))) { del(._syslog.proc_id) }
defProcIdRFC3164 = `to_string(._syslog.proc_id)
if is_empty(strip_whitespace(._syslog.proc_id)) { del(._syslog.proc_id) }
`
defAppNameRFC3164 = `to_string!(._syslog.app_name || "")`

Expand Down Expand Up @@ -63,7 +63,7 @@ if err != null {

// Default values for Syslog fields for audit logType
auditAppName = `._syslog.app_name = .log_source`
auditSeverity = `._syslog.severity = "informational"`
auditSeverity = `._syslog.severity = "info"`
auditFacility = `._syslog.facility = "security"`
auditProcId = `._syslog.proc_id = to_string!(.auditID || "-")`

Expand All @@ -74,7 +74,7 @@ if err != null {
isContainerLogCond = `.log_source == "container"`
isAuditLogCond = `.log_type == "audit"`

facilitySeverityConversionVRL = `
facilityConversionVRL = `
# try to convert syslog code to the facility, severity names (e.g. 4 -> "warning", "4" -> "warning" )
if exists(._syslog.facility) && !is_null(._syslog.facility) {
_, err = to_syslog_facility_code(._syslog.facility)
Expand All @@ -93,9 +93,23 @@ if exists(._syslog.facility) && !is_null(._syslog.facility) {
}
}
# else: already a valid name, leave it as-is
}
}`

// severityNormalizationInfallibleVRL is used when VRL already knows ._syslog.severity is a string (dynamic severity with field references).
severityNormalizationInfallibleVRL = `
._syslog.severity = downcase(._syslog.severity)
if ._syslog.severity == "emergency" { ._syslog.severity = "emerg" } else if ._syslog.severity == "critical" { ._syslog.severity = "crit" } else if ._syslog.severity == "informational" { ._syslog.severity = "info" }`

// severityNormalizationFallibleVRL is used when ._syslog.severity type is unknown (empty/default severity).
severityNormalizationFallibleVRL = `
if is_string(._syslog.severity) {
._syslog.severity = downcase(._syslog.severity) ?? ._syslog.severity
if ._syslog.severity == "emergency" { ._syslog.severity = "emerg" } else if ._syslog.severity == "critical" { ._syslog.severity = "crit" } else if ._syslog.severity == "informational" { ._syslog.severity = "info" }
}`

severityValidationVRL = `
if exists(._syslog.severity) && !is_null(._syslog.severity) {
%s
_, err = to_syslog_severity(._syslog.severity)
if err != null {
# Field is not a valid name — try treating it as a code (int or string int)
Expand Down Expand Up @@ -181,6 +195,20 @@ func New(id string, o *adapters.Output, inputs []string, secrets observability.S
return id, sink, tfs
}

func normalizeSeverity(s string) string {
lower := strings.ToLower(s)
switch lower {
case "emergency":
return "emerg"
case "critical":
return "crit"
case "informational":
return "info"
default:
return lower
}
}

func socketMode(urlScheme string) sinks.SocketMode {
switch strings.ToLower(urlScheme) {
case TCP, TLS:
Expand Down Expand Up @@ -225,7 +253,13 @@ func parseEncoding(inputs []string, o *obs.Syslog) types.Transform {
}

appendField(vrlKeySyslogFacility, o.Facility, "")
appendField(vrlKeySyslogSeverity, o.Severity, "")

severity := o.Severity
isDynamicSeverity := strings.Contains(severity, "{")
if !isDynamicSeverity && severity != "" {
severity = normalizeSeverity(severity)
}
appendField(vrlKeySyslogSeverity, severity, "")

if o.RFC == obs.SyslogRFC3164 {
appendField(vrlKeySyslogProcID, o.ProcId, defProcIdRFC3164)
Expand All @@ -236,7 +270,15 @@ func parseEncoding(inputs []string, o *obs.Syslog) types.Transform {
appendField(vrlKeySyslogMsgID, o.MsgId, "")
}

vrls = append(vrls, facilitySeverityConversionVRL)
vrls = append(vrls, facilityConversionVRL)

normVRL := ""
if isDynamicSeverity {
normVRL = severityNormalizationInfallibleVRL
} else if severity == "" {
normVRL = severityNormalizationFallibleVRL
}
vrls = append(vrls, fmt.Sprintf(severityValidationVRL, normVRL))

if key := PayloadKey(o.PayloadKey); key != "" {
vrls = append(vrls, fmt.Sprintf(payloadKeyConfiguredVRL, key))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ if .log_source == "container" {
if .log_type == "audit" {
._syslog.app_name = .log_source
._syslog.proc_id = to_string!(.auditID || "-")
._syslog.severity = "informational"
._syslog.severity = "info"
._syslog.facility = "security"
}

Expand All @@ -47,6 +47,11 @@ if exists(._syslog.facility) && !is_null(._syslog.facility) {
}

if exists(._syslog.severity) && !is_null(._syslog.severity) {
if is_string(._syslog.severity) {
._syslog.severity = downcase(._syslog.severity) ?? ._syslog.severity
if ._syslog.severity == "emergency" { ._syslog.severity = "emerg" } else if ._syslog.severity == "critical" { ._syslog.severity = "crit" } else if ._syslog.severity == "informational" { ._syslog.severity = "info" }
}

_, err = to_syslog_severity(._syslog.severity)
if err != null {
# Field is not a valid name — try treating it as a code (int or string int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ if .log_source == "container" {
if .log_type == "audit" {
._syslog.app_name = .log_source
._syslog.proc_id = to_string!(.auditID || "-")
._syslog.severity = "informational"
._syslog.severity = "info"
._syslog.facility = "security"
}

Expand All @@ -47,6 +47,11 @@ if exists(._syslog.facility) && !is_null(._syslog.facility) {
}

if exists(._syslog.severity) && !is_null(._syslog.severity) {
if is_string(._syslog.severity) {
._syslog.severity = downcase(._syslog.severity) ?? ._syslog.severity
if ._syslog.severity == "emergency" { ._syslog.severity = "emerg" } else if ._syslog.severity == "critical" { ._syslog.severity = "crit" } else if ._syslog.severity == "informational" { ._syslog.severity = "info" }
}

_, err = to_syslog_severity(._syslog.severity)
if err != null {
# Field is not a valid name — try treating it as a code (int or string int)
Expand Down
7 changes: 6 additions & 1 deletion internal/generator/vector/output/syslog/tcp_with_tuning.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if .log_source == "container" {
if .log_type == "audit" {
._syslog.app_name = .log_source
._syslog.proc_id = to_string!(.auditID || "-")
._syslog.severity = "informational"
._syslog.severity = "info"
._syslog.facility = "security"
}

Expand All @@ -48,6 +48,11 @@ if exists(._syslog.facility) && !is_null(._syslog.facility) {
}

if exists(._syslog.severity) && !is_null(._syslog.severity) {
if is_string(._syslog.severity) {
._syslog.severity = downcase(._syslog.severity) ?? ._syslog.severity
if ._syslog.severity == "emergency" { ._syslog.severity = "emerg" } else if ._syslog.severity == "critical" { ._syslog.severity = "crit" } else if ._syslog.severity == "informational" { ._syslog.severity = "info" }
}

_, err = to_syslog_severity(._syslog.severity)
if err != null {
# Field is not a valid name — try treating it as a code (int or string int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ if exists(._syslog.facility) && !is_null(._syslog.facility) {
}

if exists(._syslog.severity) && !is_null(._syslog.severity) {
._syslog.severity = downcase(._syslog.severity)
if ._syslog.severity == "emergency" { ._syslog.severity = "emerg" } else if ._syslog.severity == "critical" { ._syslog.severity = "crit" } else if ._syslog.severity == "informational" { ._syslog.severity = "info" }

_, err = to_syslog_severity(._syslog.severity)
if err != null {
# Field is not a valid name — try treating it as a code (int or string int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ source = '''

._syslog = {}
._syslog.facility = "kern"
._syslog.severity = "critical"
._syslog.severity = "crit"
._syslog.proc_id = "procID"
._syslog.app_name = "appName"

Expand All @@ -30,6 +30,7 @@ if exists(._syslog.facility) && !is_null(._syslog.facility) {
}

if exists(._syslog.severity) && !is_null(._syslog.severity) {

_, err = to_syslog_severity(._syslog.severity)
if err != null {
# Field is not a valid name — try treating it as a code (int or string int)
Expand Down
39 changes: 39 additions & 0 deletions test/functional/outputs/syslog/rfc3164_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,48 @@ var _ = Describe("[Functional][Outputs][Syslog] RFC3164 tests", func() {
// 134 = Facility(local0/16)*8 + Severity(Informational/6)
expectedPriority := "<134>"
Expect(outputlogs[0]).To(MatchRegexp(expectedPriority), "Exp to find tag in received message")

collectorLogs, err := framework.ReadCollectorLogs()
Expect(err).To(BeNil())
Expect(collectorLogs).ToNot(ContainSubstring("VRL compilation warning"), "Expected no VRL compilation warnings in collector logs")
})
})

DescribeTable("should correctly encode severity keywords in all forms", func(severity string, expectedPriority string) {
obstestruntime.NewClusterLogForwarderBuilder(framework.Forwarder).
FromInput(obs.InputTypeApplication).
ToSyslogOutput(obs.SyslogRFC3164, func(spec *obs.OutputSpec) {
spec.Syslog.Facility = "user"
spec.Syslog.Severity = severity
})
Expect(framework.Deploy()).To(BeNil())

crioMessage := functional.NewFullCRIOLogMessage(functional.CRIOTime(time.Now()), `{"index":1}`)
Expect(framework.WriteMessagesToApplicationLog(crioMessage, 1)).To(BeNil())

outputlogs, err := framework.ReadRawApplicationLogsFrom(string(obs.OutputTypeSyslog))
Expect(err).To(BeNil(), "Expected no errors reading the logs")
Expect(outputlogs).To(HaveLen(1), "Expected the receiver to receive the message")
Expect(outputlogs[0]).To(HavePrefix(expectedPriority), "Expected priority to match facility(user/1)*8 + severity")

collectorLogs, err := framework.ReadCollectorLogs()
Expect(err).To(BeNil())
Expect(collectorLogs).ToNot(ContainSubstring("VRL compilation warning"), "Expected no VRL compilation warnings in collector logs")
Expect(collectorLogs).ToNot(ContainSubstring("Invalid syslog severity"), "Expected no invalid severity warnings in collector logs")
},
// facility "user" = 1, priority = 1*8 + severity_code
// full-form keywords (RFC 5424)
Entry("full-form: critical", "critical", "<10>"), // 8 + 2
Entry("full-form: emergency", "emergency", "<8>"), // 8 + 0
Entry("full-form: error", "error", "<11>"), // 8 + 3
Entry("full-form: informational", "informational", "<14>"), // 8 + 6
Entry("full-form: warning", "warning", "<12>"), // 8 + 4
// capitalized keywords (as documented in oc explain)
Entry("capitalized: Critical", "Critical", "<10>"), // 8 + 2
Entry("capitalized: Emergency", "Emergency", "<8>"), // 8 + 0
Entry("capitalized: Informational", "Informational", "<14>"), // 8 + 6
)

DescribeTable("should enrich logs based upon the enrichment type", func(source obs.InputType, enrichment obs.EnrichmentType) {
obstestruntime.NewClusterLogForwarderBuilder(framework.Forwarder).
FromInput(source).
Expand Down
38 changes: 29 additions & 9 deletions test/functional/outputs/syslog/rfc5424_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ var _ = Describe("[Functional][Outputs][Syslog] RFC5424 tests", func() {
// The 1 after <134> is version, which is always set to 1
expectedPriority := "<134>1 "
Expect(outputlogs[0]).To(MatchRegexp(expectedPriority), "Exp to find tag in received message")

collectorLogs, err := framework.ReadCollectorLogs()
Expect(err).To(BeNil())
Expect(collectorLogs).ToNot(ContainSubstring("VRL compilation warning"), "Expected no VRL compilation warnings in collector logs")
})

It("should use numeric value", func() {
Expand All @@ -164,7 +168,7 @@ var _ = Describe("[Functional][Outputs][Syslog] RFC5424 tests", func() {
})
})

DescribeTable("should correctly encode short-form severity keywords", func(severity string, expectedPriority string) {
DescribeTable("should correctly encode severity keywords in all forms", func(severity string, expectedPriority string) {
obstestruntime.NewClusterLogForwarderBuilder(framework.Forwarder).
FromInput(obs.InputTypeApplication).
ToSyslogOutput(obs.SyslogRFC5424, func(spec *obs.OutputSpec) {
Expand All @@ -180,16 +184,32 @@ var _ = Describe("[Functional][Outputs][Syslog] RFC5424 tests", func() {
Expect(err).To(BeNil(), "Expected no errors reading the logs")
Expect(outputlogs).To(HaveLen(1), "Expected the receiver to receive the message")
Expect(outputlogs[0]).To(HavePrefix(expectedPriority), "Expected priority to match facility(user/1)*8 + severity")

collectorLogs, err := framework.ReadCollectorLogs()
Expect(err).To(BeNil())
Expect(collectorLogs).ToNot(ContainSubstring("VRL compilation warning"), "Expected no VRL compilation warnings in collector logs")
Expect(collectorLogs).ToNot(ContainSubstring("Invalid syslog severity"), "Expected no invalid severity warnings in collector logs")
},
// facility "user" = 1, priority = 1*8 + severity_code
Entry("crit", "crit", "<10>1 "), // 8 + 2
Entry("emerg", "emerg", "<8>1 "), // 8 + 0
Entry("err", "err", "<11>1 "), // 8 + 3
Entry("info", "info", "<14>1 "), // 8 + 6
Entry("warn", "warn", "<12>1 "), // 8 + 4
Entry("debug", "debug", "<15>1 "), // 8 + 7
Entry("notice", "notice", "<13>1 "), // 8 + 5
Entry("alert", "alert", "<9>1 "), // 8 + 1
// full-form keywords (RFC 5424)
Entry("full-form: critical", "critical", "<10>1 "), // 8 + 2
Entry("full-form: emergency", "emergency", "<8>1 "), // 8 + 0
Entry("full-form: error", "error", "<11>1 "), // 8 + 3
Entry("full-form: informational", "informational", "<14>1 "), // 8 + 6
Entry("full-form: warning", "warning", "<12>1 "), // 8 + 4
Entry("full-form: debug", "debug", "<15>1 "), // 8 + 7
Entry("full-form: notice", "notice", "<13>1 "), // 8 + 5
Entry("full-form: alert", "alert", "<9>1 "), // 8 + 1
// short-form keywords (VRL to_syslog_level output)
Entry("short-form: crit", "crit", "<10>1 "), // 8 + 2
Entry("short-form: emerg", "emerg", "<8>1 "), // 8 + 0
Entry("short-form: err", "err", "<11>1 "), // 8 + 3
Entry("short-form: info", "info", "<14>1 "), // 8 + 6
Entry("short-form: warn", "warn", "<12>1 "), // 8 + 4
// capitalized keywords (as documented in oc explain)
Entry("capitalized: Critical", "Critical", "<10>1 "), // 8 + 2
Entry("capitalized: Emergency", "Emergency", "<8>1 "), // 8 + 0
Entry("capitalized: Informational", "Informational", "<14>1 "), // 8 + 6
)

It("should be able to send a large payload", func() {
Expand Down
Loading