Skip to content

Commit 141b737

Browse files
support ndjson for http sink
1 parent 6b13ce2 commit 141b737

File tree

6 files changed

+114
-13
lines changed

6 files changed

+114
-13
lines changed

api/observability/v1/output_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,12 @@ type HTTP struct {
636636
// +kubebuilder:validation:Optional
637637
// +kubebuilder:validation:XValidation:rule="self == '' || isURL(self)", message="invalid URL"
638638
ProxyURL string `json:"proxyURL,omitempty"`
639+
640+
// LinePerEvent uses NDJSON instead of JSON to send data to remote destination.
641+
//
642+
// +kubebuilder:validation:Optional
643+
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Line Per Event"
644+
LinePerEvent bool `json:"line_per_event,omitempty"`
639645
}
640646

641647
type KafkaTuningSpec struct {

config/crd/bases/observability.openshift.io_clusterlogforwarders.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2391,6 +2391,10 @@ spec:
23912391
description: Headers specify optional headers to be sent
23922392
with the request
23932393
type: object
2394+
line_per_event:
2395+
description: LinePerEvent uses NDJSON instead of JSON to
2396+
send data to remote destination.
2397+
type: boolean
23942398
method:
23952399
description: Method specifies the HTTP method to be used
23962400
for sending logs. If not set, 'POST' is used.

docs/reference/operator/api_observability_v1.adoc

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,7 +1276,7 @@ NOTE2: If this filter is used in a pipeline with GoogleCloudLogging, `.hostname`
12761276

12771277
=== .spec.filters[].prune.in[]
12781278

1279-
FieldPath represents a path to find a value for a given field. The format must a value that can be converted to a
1279+
FieldPath represents a path to find a value for a given field. The format must be a value that can be converted to a
12801280
valid collector configuration. It is a dot delimited path to a field in the log record. It must start with a `.`.
12811281
The path can contain alphanumeric characters and underscores (a-zA-Z0-9_).
12821282
If segments contain characters outside of this range, the segment must be quoted.
@@ -1286,7 +1286,7 @@ Type:: array
12861286

12871287
=== .spec.filters[].prune.notIn[]
12881288

1289-
FieldPath represents a path to find a value for a given field. The format must a value that can be converted to a
1289+
FieldPath represents a path to find a value for a given field. The format must be a value that can be converted to a
12901290
valid collector configuration. It is a dot delimited path to a field in the log record. It must start with a `.`.
12911291
The path can contain alphanumeric characters and underscores (a-zA-Z0-9_).
12921292
If segments contain characters outside of this range, the segment must be quoted.
@@ -2260,6 +2260,8 @@ The 'username@password' part of `url` is ignored.
22602260

22612261
|headers|object| Headers specify optional headers to be sent with the request
22622262

2263+
|line_per_event|bool| LinePerEvent uses NDJSON instead of JSON to send data to remote destination.
2264+
22632265
|method|string| Method specifies the HTTP method to be used for sending logs. If not set, 'POST' is used.
22642266

22652267
|proxyURL|string| ProxyURL URL of a HTTP or HTTPS proxy to be used instead of direct connection.
@@ -3147,6 +3149,39 @@ Example:
31473149

31483150
3. foo.{.bar.baz||.qux.quux.corge||.grault||"nil"}-waldo.fred{.plugh||"none"}
31493151

3152+
|indexedFields|array| IndexedFields are the list of fields to be indexed by Splunk, increase storage usage, they should be used sparingly and only for high-value fields that provide significant search benefits.
3153+
Nested fields are flattened into top-level fields.
3154+
Field paths are joined using dot notation, and unsupported characters are replaced with underscores (_).
3155+
Non-string values are automatically converted to strings (e.g., 3 → "3", true → "true").
3156+
Object values serialized as JSON strings (e.g., { status: 200 } → "{\"status\":200}").
3157+
3158+
Examples: [`.kubernetes`, `.log_type`, '.kubernetes.labels.foobar', `.kubernetes.labels."foo-bar/baz"`]
3159+
|payloadKey|string| PayloadKey specifies record field to use as payload.
3160+
The PayloadKey must be a single field path.
3161+
3162+
Field paths must only contain alphanumeric and underscores. Any field with other characters must be quoted.
3163+
3164+
By default, payloadKey is not set, which means the complete log record is forwarded as the payload.
3165+
Use payloadKey carefully. Selecting a single field as the payload may cause other important information in the
3166+
log to be dropped, potentially leading to inconsistent or incomplete log events.
3167+
3168+
Examples: `.kubernetes`, `.log_type`, '.kubernetes.labels.foobar', `.kubernetes.labels."foo-bar/baz"`
3169+
3170+
|source|string| Source identifies the origin of a log event.
3171+
The Source can be a combination of static and dynamic values consisting of field paths followed by `||` followed by another field path or a static value.
3172+
A dynamic value is encased in single curly brackets `{}` and MUST end with a static fallback value separated with `||`.
3173+
Static values can only contain alphanumeric characters along with dashes, underscores, dots and forward slashes.
3174+
If not specified will be detected according to .log_source and .log_type value.
3175+
Details see in: docs/features/logforwarding/outputs/splunk-forwarding.adoc
3176+
3177+
Example:
3178+
3179+
1. foo-{.bar||"none"}
3180+
3181+
2. {.foo||.bar||"missing"}
3182+
3183+
3. foo.{.bar.baz||.qux.quux.corge||.grault||"nil"}-waldo.fred{.plugh||"none"}
3184+
31503185
|tuning|object| Tuning specs tuning for the output
31513186

31523187
|======================
@@ -3181,6 +3216,16 @@ Type:: object
31813216

31823217
|======================
31833218

3219+
=== .spec.outputs[].splunk.indexedFields[]
3220+
3221+
FieldPath represents a path to find a value for a given field. The format must be a value that can be converted to a
3222+
valid collector configuration. It is a dot delimited path to a field in the log record. It must start with a `.`.
3223+
The path can contain alphanumeric characters and underscores (a-zA-Z0-9_).
3224+
If segments contain characters outside of this range, the segment must be quoted.
3225+
Examples: `.kubernetes.namespace_name`, `.log_type`, '.kubernetes.labels.foobar', `.kubernetes.labels."foo-bar/baz"`
3226+
3227+
Type:: array
3228+
31843229
=== .spec.outputs[].splunk.tuning
31853230

31863231
Type:: object
@@ -3255,6 +3300,15 @@ uucp cron authpriv ftp ntp security console solaris-cron
32553300

32563301
local0 local1 local2 local3 local4 local5 local6 local7
32573302

3303+
The Facility can be a combination of static and dynamic values consisting of field paths followed by `||` followed by another field path or a static value.
3304+
A dynamic value is encased in single curly brackets `{}` and MUST end with a static fallback value separated with `||`.
3305+
3306+
Static values can only contain alphanumeric characters along with dashes, underscores, dots and forward slashes.
3307+
3308+
Example:
3309+
3310+
1. {.foo||"user"}
3311+
32583312
|msgId|string| MsgId is MSGID part of the syslog-msg header. This supports template syntax to allow dynamic per-event values.
32593313

32603314
The MsgId can be a combination of static and dynamic values consisting of field paths followed by `||` followed by another field path or a static value.
@@ -3321,6 +3375,15 @@ The value can be a decimal integer or one of these case-insensitive keywords:
33213375

33223376
Emergency Alert Critical Error Warning Notice Informational Debug
33233377

3378+
The Severity can be a combination of static and dynamic values consisting of field paths followed by `||` followed by another field path or a static value.
3379+
A dynamic value is encased in single curly brackets `{}` and MUST end with a static fallback value separated with `||`.
3380+
3381+
Static values can only contain alphanumeric characters along with dashes, underscores, dots and forward slashes.
3382+
3383+
Example:
3384+
3385+
1. {.foo||"Error"}
3386+
33243387
|tuning|object| Tuning specs tuning for the output
33253388

33263389
|url|string| An absolute URL, with a scheme. Valid schemes are: `tcp`, `tls`, `udp`

internal/generator/vector/output/http/http.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ import (
1414
)
1515

1616
type Http struct {
17-
ComponentID string
18-
Inputs string
19-
URI string
20-
Method string
21-
Proxy string
17+
ComponentID string
18+
Inputs string
19+
URI string
20+
Method string
21+
Proxy string
22+
LinePerEvent bool
2223
common.RootMixin
2324
}
2425

@@ -33,6 +34,9 @@ type = "http"
3334
inputs = {{.Inputs}}
3435
uri = "{{.URI}}"
3536
method = "{{.Method}}"
37+
{{with .LinePerEvent}}
38+
framing.method = "newline_delimited"
39+
{{end}}
3640
{{with .Proxy -}}
3741
proxy.enabled = true
3842
proxy.http = "{{.}}"
@@ -76,12 +80,13 @@ func New(id string, o obs.OutputSpec, inputs []string, secrets observability.Sec
7680

7781
func Output(id string, o obs.OutputSpec, inputs []string, secrets observability.Secrets, op Options) *Http {
7882
return &Http{
79-
ComponentID: id,
80-
Inputs: vectorhelpers.MakeInputs(inputs...),
81-
URI: o.HTTP.URL,
82-
Method: Method(o.HTTP),
83-
Proxy: o.HTTP.ProxyURL,
84-
RootMixin: common.NewRootMixin(nil),
83+
ComponentID: id,
84+
Inputs: vectorhelpers.MakeInputs(inputs...),
85+
URI: o.HTTP.URL,
86+
Method: Method(o.HTTP),
87+
Proxy: o.HTTP.ProxyURL,
88+
LinePerEvent: o.HTTP.LinePerEvent,
89+
RootMixin: common.NewRootMixin(nil),
8590
}
8691
}
8792

internal/generator/vector/output/http/http_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ var _ = Describe("Generate vector config", func() {
143143
BaseOutputTuningSpec: *baseTune,
144144
}
145145
}, secrets, true, framework.NoOptions, "http_with_tuning.toml"),
146+
Entry("with ndjson", func(spec *obs.OutputSpec) {
147+
spec.HTTP.LinePerEvent = true
148+
}, secrets, true, framework.NoOptions, "http_with_ndjson.toml"),
146149
Entry("with proxy", func(spec *obs.OutputSpec) {
147150
spec.HTTP.ProxyURL = "http://somewhere.org/proxy"
148151
spec.HTTP.Headers = nil
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[sinks.http_receiver]
2+
type = "http"
3+
inputs = ["application"]
4+
uri = "https://my-logstore.com"
5+
method = "post"
6+
framing.method = "newline_delimited"
7+
8+
[sinks.http_receiver.encoding]
9+
codec = "json"
10+
except_fields = ["_internal"]
11+
12+
[sinks.http_receiver.request]
13+
headers = {"h1"="v1","h2"="v2"}
14+
15+
[sinks.http_receiver.auth]
16+
strategy = "basic"
17+
user = "SECRET[kubernetes_secret.http-receiver/username]"
18+
password = "SECRET[kubernetes_secret.http-receiver/password]"
19+
20+

0 commit comments

Comments
 (0)