@@ -168,65 +168,72 @@ func (oc *OperatorContext) getFluentBitData() map[string]string {
168168
169169pipeline:
170170 inputs:`
171-
172171 // Add INPUT sections based on enabled log types
173- if oc .MarklogicGroup .Spec .LogCollection .Files .ErrorLogs {
174- fluentBitData ["fluent-bit.yaml" ] += `
175- - name: tail
172+ if strings .TrimSpace (oc .MarklogicGroup .Spec .LogCollection .Inputs ) != "" {
173+ fluentBitData ["fluent-bit.yaml" ] += "\n " + normalizeYAMLIndentation (oc .MarklogicGroup .Spec .LogCollection .Inputs , 4 , 6 )
174+ } else {
175+ if oc .MarklogicGroup .Spec .LogCollection .Files .ErrorLogs {
176+ fluentBitData ["fluent-bit.yaml" ] += `
177+ - name: tail
176178 path: /var/opt/MarkLogic/Logs/*ErrorLog.txt
177179 read_from_head: true
178180 tag: kube.marklogic.logs.error
179181 path_key: path
180182 parser: error_parser
181183 mem_buf_limit: 4MB`
182- }
184+ }
183185
184- if oc .MarklogicGroup .Spec .LogCollection .Files .AccessLogs {
185- fluentBitData ["fluent-bit.yaml" ] += `
186+ if oc .MarklogicGroup .Spec .LogCollection .Files .AccessLogs {
187+ fluentBitData ["fluent-bit.yaml" ] += `
186188 - name: tail
187189 path: /var/opt/MarkLogic/Logs/*AccessLog.txt
188190 read_from_head: true
189191 tag: kube.marklogic.logs.access
190192 path_key: path
191193 parser: access_parser
192194 mem_buf_limit: 4MB`
193- }
195+ }
194196
195- if oc .MarklogicGroup .Spec .LogCollection .Files .RequestLogs {
196- fluentBitData ["fluent-bit.yaml" ] += `
197+ if oc .MarklogicGroup .Spec .LogCollection .Files .RequestLogs {
198+ fluentBitData ["fluent-bit.yaml" ] += `
197199 - name: tail
198200 path: /var/opt/MarkLogic/Logs/*RequestLog.txt
199201 read_from_head: true
200202 tag: kube.marklogic.logs.request
201203 path_key: path
202204 parser: json_parser
203205 mem_buf_limit: 4MB`
204- }
206+ }
205207
206- if oc .MarklogicGroup .Spec .LogCollection .Files .CrashLogs {
207- fluentBitData ["fluent-bit.yaml" ] += `
208+ if oc .MarklogicGroup .Spec .LogCollection .Files .CrashLogs {
209+ fluentBitData ["fluent-bit.yaml" ] += `
208210 - name: tail
209211 path: /var/opt/MarkLogic/Logs/CrashLog.txt
210212 read_from_head: true
211213 tag: kube.marklogic.logs.crash
212214 path_key: path
213215 mem_buf_limit: 4MB`
214- }
216+ }
215217
216- if oc .MarklogicGroup .Spec .LogCollection .Files .AuditLogs {
217- fluentBitData ["fluent-bit.yaml" ] += `
218+ if oc .MarklogicGroup .Spec .LogCollection .Files .AuditLogs {
219+ fluentBitData ["fluent-bit.yaml" ] += `
218220 - name: tail
219221 path: /var/opt/MarkLogic/Logs/AuditLog.txt
220222 read_from_head: true
221223 tag: kube.marklogic.logs.audit
222224 path_key: path
223225 mem_buf_limit: 4MB`
226+ }
224227 }
225228
226229 // Add FILTER sections
227230 fluentBitData ["fluent-bit.yaml" ] += `
228231
229- filters:
232+ filters:`
233+ if strings .TrimSpace (oc .MarklogicGroup .Spec .LogCollection .Filters ) != "" {
234+ fluentBitData ["fluent-bit.yaml" ] += "\n " + normalizeYAMLIndentation (oc .MarklogicGroup .Spec .LogCollection .Filters , 4 , 6 )
235+ } else {
236+ fluentBitData ["fluent-bit.yaml" ] += `
230237 - name: modify
231238 match: "*"
232239 add: pod ${POD_NAME}
@@ -246,43 +253,16 @@ pipeline:
246253 - name: modify
247254 match: kube.marklogic.logs.crash
248255 add: tag kube.marklogic.logs.crash
256+ `
257+ }
249258
250- outputs:`
259+ // Add OUTPUT sections
260+ fluentBitData ["fluent-bit.yaml" ] += `
251261
262+ outputs:`
252263 // Handle user-defined outputs from LogCollection.Outputs
253- if oc .MarklogicGroup .Spec .LogCollection .Outputs != "" {
254- // Process user-defined outputs, adjusting indentation to match pipeline level
255- outputs := oc .MarklogicGroup .Spec .LogCollection .Outputs
256- // Split into lines and process indentation
257- lines := strings .Split (outputs , "\n " )
258- processedLines := make ([]string , 0 , len (lines ))
259- for _ , line := range lines {
260- if strings .TrimSpace (line ) == "" {
261- continue // Skip empty lines
262- }
263- // Count leading spaces in original line
264- leadingSpaces := len (line ) - len (strings .TrimLeft (line , " " ))
265- content := strings .TrimLeft (line , " " )
266- if content != "" {
267- // For YAML list items starting with "-", use 4 spaces base indentation
268- // For properties under list items, use 6 spaces base indentation
269- var newIndent int
270- const yamlListItemIndent = 4
271- const yamlPropertyIndent = 6
272- if strings .HasPrefix (content , "- " ) {
273- newIndent = yamlListItemIndent // List items at pipeline outputs level
274- } else {
275- newIndent = yamlPropertyIndent // Properties under list items
276- }
277- // Add any additional relative indentation beyond the first level
278- const baseIndentOffset = 2
279- if leadingSpaces > baseIndentOffset {
280- newIndent += leadingSpaces - baseIndentOffset
281- }
282- processedLines = append (processedLines , strings .Repeat (" " , newIndent )+ content )
283- }
284- }
285- fluentBitData ["fluent-bit.yaml" ] += "\n " + strings .Join (processedLines , "\n " )
264+ if strings .TrimSpace (oc .MarklogicGroup .Spec .LogCollection .Outputs ) != "" {
265+ fluentBitData ["fluent-bit.yaml" ] += "\n " + normalizeYAMLIndentation (oc .MarklogicGroup .Spec .LogCollection .Outputs , 4 , 6 )
286266 } else {
287267 // Default stdout output if none specified
288268 fluentBitData ["fluent-bit.yaml" ] += `
@@ -292,7 +272,11 @@ pipeline:
292272 }
293273
294274 // Parsers in YAML format
295- fluentBitData ["parsers.yaml" ] = `parsers:
275+ fluentBitData ["parsers.yaml" ] = `parsers:`
276+ if strings .TrimSpace (oc .MarklogicGroup .Spec .LogCollection .Parsers ) != "" {
277+ fluentBitData ["parsers.yaml" ] += "\n " + normalizeYAMLIndentation (oc .MarklogicGroup .Spec .LogCollection .Parsers , 2 , 4 )
278+ } else {
279+ fluentBitData ["parsers.yaml" ] += `
296280 - name: error_parser
297281 format: regex
298282 regex: ^(?<time>(.+?)(?=[a-zA-Z]))(?<log_level>(.+?)(?=:))(.+?)(?=[a-zA-Z])(?<log>.*)
@@ -309,6 +293,62 @@ pipeline:
309293 format: json
310294 time_key: time
311295 time_format: "%Y-%m-%dT%H:%M:%S%z"`
296+ }
312297
313298 return fluentBitData
314299}
300+
301+ // normalizeYAMLIndentation processes user-provided YAML content and adjusts indentation
302+ // to match the target YAML structure. This is useful when embedding user YAML into templates.
303+ //
304+ // Parameters:
305+ // - yamlContent: The raw YAML content string to process
306+ // - listItemIndent: Number of spaces for YAML list items (lines starting with "- ")
307+ // - propertyIndent: Number of spaces for properties under list items
308+ //
309+ // Returns: A string with normalized indentation, ready to embed in a larger YAML structure
310+ //
311+ // Example:
312+ //
313+ // input := "- name: loki\n host: loki.svc\n port: 3100"
314+ // output := normalizeYAMLIndentation(input, 4, 6)
315+ func normalizeYAMLIndentation (yamlContent string , listItemIndent , propertyIndent int ) string {
316+ if yamlContent == "" {
317+ return ""
318+ }
319+
320+ lines := strings .Split (yamlContent , "\n " )
321+ processedLines := make ([]string , 0 , len (lines ))
322+
323+ for _ , line := range lines {
324+ if strings .TrimSpace (line ) == "" {
325+ continue // Skip empty lines
326+ }
327+
328+ // Count leading spaces in original line
329+ leadingSpaces := len (line ) - len (strings .TrimLeft (line , " " ))
330+ content := strings .TrimLeft (line , " " )
331+
332+ if content != "" {
333+ // Determine base indentation level
334+ var newIndent int
335+ if strings .HasPrefix (content , "- " ) {
336+ // YAML list items (e.g., "- name: loki")
337+ newIndent = listItemIndent
338+ } else {
339+ // Properties under list items (e.g., "host: loki.svc")
340+ newIndent = propertyIndent
341+ }
342+
343+ // Preserve relative indentation for nested structures
344+ const baseIndentOffset = 2
345+ if leadingSpaces > baseIndentOffset {
346+ newIndent += leadingSpaces - baseIndentOffset
347+ }
348+
349+ processedLines = append (processedLines , strings .Repeat (" " , newIndent )+ content )
350+ }
351+ }
352+
353+ return strings .Join (processedLines , "\n " )
354+ }
0 commit comments