SumoLogic/sumologic-kubernetes-tools

[update-collection-v3] Multiline strings are serialized as single line string

Closed this issue · 0 comments

Version: v2.16.0

Multiline strings are migrated as single-line strings.

The output values file still seems to work with Helm, but the multiline properties are impossible to read.

Steps to reproduce

  1. Download input values file from https://raw.githubusercontent.com/SumoLogic/sumologic-kubernetes-collection/v2.19.1/examples/multiline_logs/values_containerd.yaml

  2. Run the migration tool ./update-collection-v3 -in values_containerd.yaml -out v3.yaml

Expected output

The migrated file v3.yaml should contain multiline entries in e.g. fluent-bit.config.inputs etc.

fluent-bit:
  luaScripts:
    parse_logs.lua: |
      local function split(s, delimiter)
        result = {};
        for match in (s..delimiter):gmatch('(.-)'..delimiter) do
            table.insert(result, match);
        end
        return result;
      end

      function get_log_content(line)
        -- remove elements specific containerd log format and get log as single string
        table.remove(line, 1) -- remove date
        table.remove(line, 1) -- remove stream
        table.remove(line, 1) -- remove log tag
        return table.concat(line, " ")
      end

      function adjust_first_line(record, first_line)
        -- adjust the first line to containerd format, it comes without date, stream and logtag
        -- 'fake-date' string at the beginning is used only to have proper log format
        first_line = 'fake-date' .. ' ' .. record['stream'] .. ' ' .. record['logtag'] .. ' ' .. first_line
        return first_line
      end

      function parse_log(tag, timestamp, record)
        if record['log'] == nil or record['stream'] == nil or record['logtag'] == nil then
          -- log does not contain required attributes ('log', 'stream', 'logtag') to be processed by parse_log function
          -- the record will not be modified
          return 0, timestamp, record
        end

        log_lines = split(record['log'], '\n')
        log_lines[1] = adjust_first_line(record, log_lines[1])

        new_lines = {}
        buffer = ''

        for k, v in pairs(log_lines) do
          line = split(v, ' ')
          log_tag = line[3]
          buffer = buffer .. get_log_content(line)

          if log_tag == 'F' then
              table.insert(new_lines, buffer)
              buffer = ""
          end
        end

        new_log = table.concat(new_lines, "\n")
        record['log'] = new_log
        return 2, timestamp, record
      end
  config:
    service: |
      [SERVICE]
          Flush        1
          Daemon       Off
          Log_Level    info
          Parsers_File parsers.conf
          Parsers_File custom_parsers.conf
          HTTP_Server  On
          HTTP_Listen  0.0.0.0
          HTTP_Port    2020
    inputs: |
      [INPUT]
          Name                tail
          Path                /var/log/containers/*.log
          Read_from_head      true
          Multiline           On
          Parser_Firstline    containerd_multiline_pattern
          Multiline_Flush     60
          Tag                 containers.*
          Refresh_Interval    1
          Rotate_Wait         60
          Mem_Buf_Limit       30MB
          Skip_Long_Lines     On
          DB                  /tail-db/tail-containers-state-sumo.db
          DB.Sync             Normal
      [INPUT]
          Name            systemd
          Tag             host.*
          DB              /tail-db/systemd-state-sumo.db
          Systemd_Filter  _SYSTEMD_UNIT=addon-config.service
          Systemd_Filter  _SYSTEMD_UNIT=addon-run.service
          Systemd_Filter  _SYSTEMD_UNIT=cfn-etcd-environment.service
          Systemd_Filter  _SYSTEMD_UNIT=cfn-signal.service
          Systemd_Filter  _SYSTEMD_UNIT=clean-ca-certificates.service
          Systemd_Filter  _SYSTEMD_UNIT=containerd.service
          Systemd_Filter  _SYSTEMD_UNIT=coreos-metadata.service
          Systemd_Filter  _SYSTEMD_UNIT=coreos-setup-environment.service
          Systemd_Filter  _SYSTEMD_UNIT=coreos-tmpfiles.service
          Systemd_Filter  _SYSTEMD_UNIT=dbus.service
          Systemd_Filter  _SYSTEMD_UNIT=docker.service
          Systemd_Filter  _SYSTEMD_UNIT=efs.service
          Systemd_Filter  _SYSTEMD_UNIT=etcd-member.service
          Systemd_Filter  _SYSTEMD_UNIT=etcd.service
          Systemd_Filter  _SYSTEMD_UNIT=etcd2.service
          Systemd_Filter  _SYSTEMD_UNIT=etcd3.service
          Systemd_Filter  _SYSTEMD_UNIT=etcdadm-check.service
          Systemd_Filter  _SYSTEMD_UNIT=etcdadm-reconfigure.service
          Systemd_Filter  _SYSTEMD_UNIT=etcdadm-save.service
          Systemd_Filter  _SYSTEMD_UNIT=etcdadm-update-status.service
          Systemd_Filter  _SYSTEMD_UNIT=flanneld.service
          Systemd_Filter  _SYSTEMD_UNIT=format-etcd2-volume.service
          Systemd_Filter  _SYSTEMD_UNIT=kube-node-taint-and-uncordon.service
          Systemd_Filter  _SYSTEMD_UNIT=kubelet.service
          Systemd_Filter  _SYSTEMD_UNIT=ldconfig.service
          Systemd_Filter  _SYSTEMD_UNIT=locksmithd.service
          Systemd_Filter  _SYSTEMD_UNIT=logrotate.service
          Systemd_Filter  _SYSTEMD_UNIT=lvm2-monitor.service
          Systemd_Filter  _SYSTEMD_UNIT=mdmon.service
          Systemd_Filter  _SYSTEMD_UNIT=nfs-idmapd.service
          Systemd_Filter  _SYSTEMD_UNIT=nfs-mountd.service
          Systemd_Filter  _SYSTEMD_UNIT=nfs-server.service
          Systemd_Filter  _SYSTEMD_UNIT=nfs-utils.service
          Systemd_Filter  _SYSTEMD_UNIT=node-problem-detector.service
          Systemd_Filter  _SYSTEMD_UNIT=ntp.service
          Systemd_Filter  _SYSTEMD_UNIT=oem-cloudinit.service
          Systemd_Filter  _SYSTEMD_UNIT=rkt-gc.service
          Systemd_Filter  _SYSTEMD_UNIT=rkt-metadata.service
          Systemd_Filter  _SYSTEMD_UNIT=rpc-idmapd.service
          Systemd_Filter  _SYSTEMD_UNIT=rpc-mountd.service
          Systemd_Filter  _SYSTEMD_UNIT=rpc-statd.service
          Systemd_Filter  _SYSTEMD_UNIT=rpcbind.service
          Systemd_Filter  _SYSTEMD_UNIT=set-aws-environment.service
          Systemd_Filter  _SYSTEMD_UNIT=system-cloudinit.service
          Systemd_Filter  _SYSTEMD_UNIT=systemd-timesyncd.service
          Systemd_Filter  _SYSTEMD_UNIT=update-ca-certificates.service
          Systemd_Filter  _SYSTEMD_UNIT=user-cloudinit.service
          Systemd_Filter  _SYSTEMD_UNIT=var-lib-etcd2.service
          Max_Entries     1000
          Read_From_Tail  true
    outputs: |
      [OUTPUT]
          Name          forward
          Match         *
          Host          ${FLUENTD_LOGS_SVC}.${NAMESPACE}.svc.cluster.local.
          Port          24321
          Retry_Limit   False
          tls           off
          tls.verify    on
          tls.debug     1
          # Disable keepalive for better load balancing
          net.keepalive off
    customParsers: |
      [PARSER]
          Name         containerd_multiline_pattern
          Format       regex
          Regex        (?<time>^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[.]\d+Z) (?<stream>stdout|stderr) (?<logtag>[P|F]) (?<log>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.* .*)
          Time_Key     time
          Time_Format  %Y-%m-%dT%H:%M:%S.%LZ
    filters: |
      [FILTER]
          Name    lua
          Match   containers.*
          script  /fluent-bit/scripts/parse_logs.lua
          call    parse_log
fluentd:
  logs:
    containers:
      multiline:
        enabled: false

Actual output

The properties in migrated file v3.yaml contain multiline strings rendered as single line with newlines encoded as \n.

fluent-bit:
  luaScripts:
    parse_logs.lua: "local function split(s, delimiter)\n  result = {};\n  for match in (s..delimiter):gmatch('(.-)'..delimiter) do\n      table.insert(result, match);\n  end\n  return result;\nend\n\nfunction get_log_content(line)\n  -- remove elements specific containerd log format and get log as single string\n  table.remove(line, 1) -- remove date\n  table.remove(line, 1) -- remove stream\n  table.remove(line, 1) -- remove log tag\n  return table.concat(line, \" \")\nend\n\nfunction adjust_first_line(record, first_line)\n  -- adjust the first line to containerd format, it comes without date, stream and logtag\n  -- 'fake-date' string at the beginning is used only to have proper log format\n  first_line = 'fake-date' .. ' ' .. record['stream'] .. ' ' .. record['logtag'] .. ' ' .. first_line\n  return first_line\nend\n\nfunction parse_log(tag, timestamp, record)\n  if record['log'] == nil or record['stream'] == nil or record['logtag'] == nil then\n    -- log does not contain required attributes ('log', 'stream', 'logtag') to be processed by parse_log function\n    -- the record will not be modified\n    return 0, timestamp, record\n  end\n\n  log_lines = split(record['log'], '\\n')\n  log_lines[1] = adjust_first_line(record, log_lines[1])\n\n  new_lines = {}\n  buffer = ''\n\n  for k, v in pairs(log_lines) do\n    line = split(v, ' ')\n    log_tag = line[3]\n    buffer = buffer .. get_log_content(line)\n\n    if log_tag == 'F' then\n        table.insert(new_lines, buffer)\n        buffer = \"\"\n    end\n  end\n\n  new_log = table.concat(new_lines, \"\\n\")\n  record['log'] = new_log\n  return 2, timestamp, record\nend\n"
  config:
    service: "[SERVICE]\n    Flush        1\n    Daemon       Off\n    Log_Level    info\n    Parsers_File parsers.conf\n    Parsers_File custom_parsers.conf\n    HTTP_Server  On\n    HTTP_Listen  0.0.0.0\n    HTTP_Port    2020\n"
    inputs: "[INPUT]\n    Name                tail\n    Path                /var/log/containers/*.log\n    Read_from_head      true\n    Multiline           On\n    Parser_Firstline    containerd_multiline_pattern\n    Multiline_Flush     60\n    Tag                 containers.*\n    Refresh_Interval    1\n    Rotate_Wait         60\n    Mem_Buf_Limit       30MB\n    Skip_Long_Lines     On\n    DB                  /tail-db/tail-containers-state-sumo.db\n    DB.Sync             Normal\n[INPUT]\n    Name            systemd\n    Tag             host.*\n    DB              /tail-db/systemd-state-sumo.db\n    Systemd_Filter  _SYSTEMD_UNIT=addon-config.service\n    Systemd_Filter  _SYSTEMD_UNIT=addon-run.service\n    Systemd_Filter  _SYSTEMD_UNIT=cfn-etcd-environment.service\n    Systemd_Filter  _SYSTEMD_UNIT=cfn-signal.service\n    Systemd_Filter  _SYSTEMD_UNIT=clean-ca-certificates.service\n    Systemd_Filter  _SYSTEMD_UNIT=containerd.service\n    Systemd_Filter  _SYSTEMD_UNIT=coreos-metadata.service\n    Systemd_Filter  _SYSTEMD_UNIT=coreos-setup-environment.service\n    Systemd_Filter  _SYSTEMD_UNIT=coreos-tmpfiles.service\n    Systemd_Filter  _SYSTEMD_UNIT=dbus.service\n    Systemd_Filter  _SYSTEMD_UNIT=docker.service\n    Systemd_Filter  _SYSTEMD_UNIT=efs.service\n    Systemd_Filter  _SYSTEMD_UNIT=etcd-member.service\n    Systemd_Filter  _SYSTEMD_UNIT=etcd.service\n    Systemd_Filter  _SYSTEMD_UNIT=etcd2.service\n    Systemd_Filter  _SYSTEMD_UNIT=etcd3.service\n    Systemd_Filter  _SYSTEMD_UNIT=etcdadm-check.service\n    Systemd_Filter  _SYSTEMD_UNIT=etcdadm-reconfigure.service\n    Systemd_Filter  _SYSTEMD_UNIT=etcdadm-save.service\n    Systemd_Filter  _SYSTEMD_UNIT=etcdadm-update-status.service\n    Systemd_Filter  _SYSTEMD_UNIT=flanneld.service\n    Systemd_Filter  _SYSTEMD_UNIT=format-etcd2-volume.service\n    Systemd_Filter  _SYSTEMD_UNIT=kube-node-taint-and-uncordon.service\n    Systemd_Filter  _SYSTEMD_UNIT=kubelet.service\n    Systemd_Filter  _SYSTEMD_UNIT=ldconfig.service\n    Systemd_Filter  _SYSTEMD_UNIT=locksmithd.service\n    Systemd_Filter  _SYSTEMD_UNIT=logrotate.service\n    Systemd_Filter  _SYSTEMD_UNIT=lvm2-monitor.service\n    Systemd_Filter  _SYSTEMD_UNIT=mdmon.service\n    Systemd_Filter  _SYSTEMD_UNIT=nfs-idmapd.service\n    Systemd_Filter  _SYSTEMD_UNIT=nfs-mountd.service\n    Systemd_Filter  _SYSTEMD_UNIT=nfs-server.service\n    Systemd_Filter  _SYSTEMD_UNIT=nfs-utils.service\n    Systemd_Filter  _SYSTEMD_UNIT=node-problem-detector.service\n    Systemd_Filter  _SYSTEMD_UNIT=ntp.service\n    Systemd_Filter  _SYSTEMD_UNIT=oem-cloudinit.service\n    Systemd_Filter  _SYSTEMD_UNIT=rkt-gc.service\n    Systemd_Filter  _SYSTEMD_UNIT=rkt-metadata.service\n    Systemd_Filter  _SYSTEMD_UNIT=rpc-idmapd.service\n    Systemd_Filter  _SYSTEMD_UNIT=rpc-mountd.service\n    Systemd_Filter  _SYSTEMD_UNIT=rpc-statd.service\n    Systemd_Filter  _SYSTEMD_UNIT=rpcbind.service\n    Systemd_Filter  _SYSTEMD_UNIT=set-aws-environment.service\n    Systemd_Filter  _SYSTEMD_UNIT=system-cloudinit.service\n    Systemd_Filter  _SYSTEMD_UNIT=systemd-timesyncd.service\n    Systemd_Filter  _SYSTEMD_UNIT=update-ca-certificates.service\n    Systemd_Filter  _SYSTEMD_UNIT=user-cloudinit.service\n    Systemd_Filter  _SYSTEMD_UNIT=var-lib-etcd2.service\n    Max_Entries     1000\n    Read_From_Tail  true\n"
    outputs: "[OUTPUT]\n    Name          forward\n    Match         *\n    Host          ${FLUENTD_LOGS_SVC}.${NAMESPACE}.svc.cluster.local.\n    Port          24321\n    Retry_Limit   False\n    tls           off\n    tls.verify    on\n    tls.debug     1\n    # Disable keepalive for better load balancing\n    net.keepalive off\n"
    customParsers: "[PARSER]\n    Name         containerd_multiline_pattern\n    Format       regex\n    Regex        (?<time>^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[.]\\d+Z) (?<stream>stdout|stderr) (?<logtag>[P|F]) (?<log>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.* .*)\n    Time_Key     time\n    Time_Format  %Y-%m-%dT%H:%M:%S.%LZ\n"
    filters: "[FILTER]\n    Name    lua\n    Match   containers.*\n    script  /fluent-bit/scripts/parse_logs.lua\n    call    parse_log\n"
fluentd:
  logs:
    containers:
      multiline:
        enabled: false