From b72e8a48be07a1cebce8b2237d7344220678c2ec Mon Sep 17 00:00:00 2001 From: Noriko Hosoi Date: Fri, 16 Oct 2020 08:15:11 -0700 Subject: [PATCH 5/7] Logging - support property-based filters in the files and forwards outputs Adding property-based filter options to files, forwards and remote_files output. A test case is added to tests_basics_files2.yml. In addition, fixing a bug caused by a left over file from the previous tests. Issue - https://github.com/linux-system-roles/logging/issues/179 (cherry picked from commit 6ac8f9ff680a4b0230446062f5927f5921829f80) --- README.md | 68 ++++++++++++------- roles/rsyslog/templates/output_files.j2 | 4 +- roles/rsyslog/templates/output_forwards.j2 | 4 +- .../rsyslog/templates/output_remote_files.j2 | 4 +- tests/tests_basics_files2.yml | 40 +++++++++-- tests/tests_basics_forwards_cert.yml | 8 +++ tests/tests_basics_forwards_cert_missing.yml | 4 ++ tests/tests_server_conflict.yml | 8 +++ 8 files changed, 108 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index db29dc5..4352ee7 100644 --- a/README.md +++ b/README.md @@ -180,11 +180,16 @@ This is a schematic logging configuration to show log messages from input_nameA - `files` type - `files` output supports storing logs in the local files usually in /var/log.
**available options** - - `facility`: Facility; default to `*`. - - `severity`: Severity; default to `*`. - - `exclude`: Exclude list; default to none. + - `facility`: Facility in selector; default to `*`. + - `severity`: Severity in selector; default to `*`. + - `exclude`: Exclude list used in selector; default to none. + - `property`: Property in property-based filter; no default + - `prop_op`: Operation in property-based filter; In case of not `!`, put the `prop_op` value in quotes; default to `contains` + - `prop_value`: Value in property-based filter; default to `error` - `path`: Path to the output file. + Selector options and property-based filter options are exclusive. If Property-based filter options are defined, selector options will be ignored. + Unless the above options are given, these local file outputs are configured. ``` kern.* /dev/console @@ -199,8 +204,12 @@ This is a schematic logging configuration to show log messages from input_nameA - `forwards` type - `forwards` output sends logs to the remote logging system over the network. This is for the client rsyslog.
**available options** - - `facility`: Facility; default to `*`. - - `severity`: Severity; default to `*`. + - `facility`: Facility in selector; default to `*`. + - `severity`: Severity in selector; default to `*`. + - `exclude`: Exclude list used in selector; default to none. + - `property`: Property in property-based filter; no default + - `prop_op`: Operation in property-based filter; In case of not `!`, put the `prop_op` value in quotes; default to `contains` + - `prop_value`: Value in property-based filter; default to `error` - `target`: Target host (fqdn). **Required**. - `udp_port`: UDP port number. Default to `514`. - `tcp_port`: TCP port number. Default to `514`. @@ -208,11 +217,16 @@ This is a schematic logging configuration to show log messages from input_nameA - `pki_authmode`: Specifying the default network driver authentication mode. `x509/name`, `x509/fingerprint`, `anon` is accepted. Default to `x509/name`. - `permitted_server`: Hostname, IP address, fingerprint(sha1) or wildcard DNS domain of the server which this client will be allowed to connect and send logs over TLS. Default to `*.{{ logging_domain }}` + Selector options and property-based filter options are exclusive. If Property-based filter options are defined, selector options will be ignored. + - `remote_files` type - `remote_files` output stores logs to the local files per remote host and program name originated the logs.
**available options** - - `facility`: Facility; default to `*`. - - `severity`: Severity; default to `*`. - - `exclude`: Exclude list; default to none. + - `facility`: Facility in selector; default to `*`. + - `severity`: Severity in selector; default to `*`. + - `exclude`: Exclude list used in selector; default to none. + - `property`: Property in property-based filter; no default + - `prop_op`: Operation in property-based filter; In case of not `!`, put the `prop_op` value in quotes; default to `contains` + - `prop_value`: Value in property-based filter; default to `error` - `async_writing`: If set to `true`, the files are written asynchronously. Allowed value is `true` or `false`. Default to `false`. - `client_count`: Count of client logging system supported this rsyslog server. Default to `10`. - `io_buffer_size`: Buffer size used to write output data. Default to `65536` bytes. @@ -221,6 +235,8 @@ This is a schematic logging configuration to show log messages from input_nameA `/path/to/output/dir/%HOSTNAME%/%PROGRAMNAME:::secpath-replace%.log` - `remote_sub_path`: Relative path to logging_system_log_dir to store the filtered logs. + Selector options and property-based filter options are exclusive. If Property-based filter options are defined, selector options will be ignored. + if both `remote_log_path` and `remote_sub_path` are _not_ specified, the remote_file output configured with the following settings. ``` template( @@ -446,32 +462,38 @@ The following playbook generates the same logging configuration files. outputs: [files_output0, files_output1] ``` -5. Deploying `files input` reading logs from a local file and `elasticsearch output` to store the logs. Assuming the ca_cert, cert and key to connect to Elasticsearch are prepared. +5. Deploying `files input` reading logs from local files and `files output` to write to the local files based on the property-based filters. ```yaml --- -- name: Deploying basic input and elasticsearch output +- name: Deploying files input and configured files output hosts: all roles: - linux-system-roles.logging vars: logging_inputs: - - name: files_input + - name: files_input0 type: files - input_log_path: /var/log/containers/*.log + input_log_path: /var/log/containerA/*.log + - name: files_input1 + type: files + input_log_path: /var/log/containerB/*.log logging_outputs: - - name: elasticsearch_output - type: elasticsearch - server_host: your_target_host - server_port: 9200 - index_prefix: project. - input_type: ovirt - ca_cert_src: /local/path/to/ca_cert - cert_src: /local/path/to/cert - private_key_src: /local/path/to/key + - name: files_output0 + type: files + property: msg + prop_op: contains + prop_value: error + path: /var/log/errors.log + - name: files_output1 + type: files + property: msg + prop_op: "!contains" + prop_value: error + path: /var/log/others.log logging_flows: - name: flow0 - inputs: [files_input] - outputs: [elasticsearch_output] + inputs: [files_input0, files_input1] + outputs: [files_output0, files_output1] ``` ### Client configuration diff --git a/roles/rsyslog/templates/output_files.j2 b/roles/rsyslog/templates/output_files.j2 index d994414..e15e4cd 100644 --- a/roles/rsyslog/templates/output_files.j2 +++ b/roles/rsyslog/templates/output_files.j2 @@ -1,6 +1,8 @@ {% if item.path is defined %} ruleset(name="{{ item.name }}") { -{% if item.exclude | d([]) %} +{% if item.property | d() %} + :{{ item.property }}, {{ item.prop_op | d('contains') }}, "{{ item.prop_value | d('error') }}" {{ item.path }} +{% elif item.exclude | d([]) %} {{ item.facility | d('*') }}.{{ item.severity | d('*') }};{{ item.exclude | join(';') }} {{ item.path }} {% else %} {{ item.facility | d('*') }}.{{ item.severity | d('*') }} {{ item.path }} diff --git a/roles/rsyslog/templates/output_forwards.j2 b/roles/rsyslog/templates/output_forwards.j2 index 61254ee..35030b4 100644 --- a/roles/rsyslog/templates/output_forwards.j2 +++ b/roles/rsyslog/templates/output_forwards.j2 @@ -9,7 +9,9 @@ {% set __forwards_protocol = '' %} {% endif %} ruleset(name="{{ item.name }}") { -{% if item.exclude | d([]) %} +{% if item.property | d() %} + :{{ item.property }}, {{ item.prop_op | d('contains') }}, "{{ item.prop_value | d('error') }}" action(name="{{ item.name }}" +{% elif item.exclude | d([]) %} {{ item.facility | d('*') }}.{{ item.severity | d('*') }};{{ item.exclude | join(';') }} action(name="{{ item.name }}" {% else %} {{ item.facility | d('*') }}.{{ item.severity | d('*') }} action(name="{{ item.name }}" diff --git a/roles/rsyslog/templates/output_remote_files.j2 b/roles/rsyslog/templates/output_remote_files.j2 index 3c9339f..aaf547e 100644 --- a/roles/rsyslog/templates/output_remote_files.j2 +++ b/roles/rsyslog/templates/output_remote_files.j2 @@ -17,7 +17,9 @@ ruleset(name="{{ item.name }}" queue.size="{{ logging_server_queue_size }}" queue.workerThreads="{{ logging_server_threads }}") { # Store remote logs in separate logfiles -{% if item.exclude | d([]) %} +{% if item.property | d() %} + :{{ item.property }}, {{ item.prop_op | d('contains') }}, "{{ item.prop_value | d('error') }}" action(name="{{ item.name }}" type="omfile" DynaFile="{{ item.name }}_template" DynaFileCacheSize="{{ item.client_count | d(10) }}" ioBufferSize="{{ item.io_buffer_size | d('65536') }}" asyncWriting="{{ 'on' if item.async_writing | d(false) | bool else 'off' }}") +{% elif item.exclude | d([]) %} {{ item.facility | d('*') }}.{{ item.severity | d('*') }};{{ item.exclude | join(';') }} action(name="{{ item.name }}" type="omfile" DynaFile="{{ item.name }}_template" DynaFileCacheSize="{{ item.client_count | d(10) }}" ioBufferSize="{{ item.io_buffer_size | d('65536') }}" asyncWriting="{{ 'on' if item.async_writing | d(false) | bool else 'off' }}") {% else %} {{ item.facility | d('*') }}.{{ item.severity | d('*') }} action(name="{{ item.name }}" type="omfile" DynaFile="{{ item.name }}_template" DynaFileCacheSize="{{ item.client_count | d(10) }}" ioBufferSize="{{ item.io_buffer_size | d('65536') }}" asyncWriting="{{ 'on' if item.async_writing | d(false) | bool else 'off' }}") diff --git a/tests/tests_basics_files2.yml b/tests/tests_basics_files2.yml index 094b125..b1a0f62 100644 --- a/tests/tests_basics_files2.yml +++ b/tests/tests_basics_files2.yml @@ -10,9 +10,9 @@ # If logging role is executed, the file size is about 100 bytes. # Thus, assert the size is less than 1000. # 2. Check file count in /etc/rsyslog.d. -# If logging role is executed, 8 config files are generated. +# If logging role is executed, 9 config files are generated. # By setting logging_purge_confs, pre-existing config files are deleted. -# Thus, assert the the count is equal to 8. +# Thus, assert the the count is equal to 9. # 3. Check systemctl status of rsyslog as well as error or specific message in the output. # 4. To verify the generated filename is correct, check the config file of files output exists. # 4.1 Check the config file contains the expected filter and the output file as configured. @@ -24,6 +24,8 @@ vars: __test_files_conf: /etc/rsyslog.d/30-output-files-files_output1.conf __default_system_log: /var/log/messages + __prop_based_log0: /var/log/property_based_filter_in.log + __prop_based_log1: /var/log/property_based_filter_out.log tasks: - name: deploy config to output into local files @@ -49,15 +51,23 @@ path: :omusrmsg:* - name: files_output3 type: files - facility: local7 - path: /var/log/boot.log + property: msg + prop_op: contains + prop_value: property_based_filter_test + path: "{{ __prop_based_log0 }}" + - name: files_output4 + type: files + property: msg + prop_op: "!contains" + prop_value: property_based_filter_test + path: "{{ __prop_based_log1 }}" logging_inputs: - name: basic_input type: basics logging_flows: - name: flow_0 inputs: [basic_input] - outputs: [files_output0, files_output1, files_output2, files_output3] + outputs: [files_output0, files_output1, files_output2, files_output3, files_output4] include_role: name: linux-system-roles.logging @@ -74,7 +84,7 @@ - name: Check file counts in rsyslog.d assert: - that: rsyslog_d_file_count.matched == 8 + that: rsyslog_d_file_count.matched == 9 # Checking 'error' in stdout from systemctl status is for detecting the case in which rsyslog is running, # but some functionality is disabled due to some error, e.g., error: 'tls.cacert' file couldn't be accessed. @@ -104,3 +114,21 @@ retries: 5 delay: 1 changed_when: false + + - name: Run logger to generate a test log message containing property_based_filter_test + command: /bin/logger -i -p local6.info -t testTag1 property_based_filter_test + changed_when: false + + - name: Check the test log message in {{ __prop_based_log0 }} + command: /bin/grep property_based_filter_test "{{ __prop_based_log0 }}" + register: __result + until: __result is success + retries: 5 + delay: 1 + changed_when: false + + - name: Check the test log message not in {{ __prop_based_log1 }} + command: /bin/grep property_based_filter_test "{{ __prop_based_log1 }}" + register: __result + changed_when: false + failed_when: "__result is not failed" diff --git a/tests/tests_basics_forwards_cert.yml b/tests/tests_basics_forwards_cert.yml index e27e016..48263ae 100644 --- a/tests/tests_basics_forwards_cert.yml +++ b/tests/tests_basics_forwards_cert.yml @@ -139,3 +139,11 @@ - /etc/pki/tls/certs/{{ __test_ca_cert_name }} - /etc/pki/tls/certs/{{ __test_cert_name }} - /etc/pki/tls/private/{{ __test_key_name }} + + - name: clean up test files + file: path="{{ item }}" state=absent + loop: + - "{{ __test_ca_cert }}" + - "{{ __test_cert }}" + - "{{ __test_key }}" + delegate_to: localhost diff --git a/tests/tests_basics_forwards_cert_missing.yml b/tests/tests_basics_forwards_cert_missing.yml index 3e82856..0ad0569 100644 --- a/tests/tests_basics_forwards_cert_missing.yml +++ b/tests/tests_basics_forwards_cert_missing.yml @@ -63,6 +63,10 @@ assert: that: "'{{ ansible_failed_result.results.0.msg }}' is match('{{ __expected_error }}')" + - name: clean up test files + file: path="{{ __test_key }}" state=absent + delegate_to: localhost + - name: default run for cleanup vars: logging_inputs: diff --git a/tests/tests_server_conflict.yml b/tests/tests_server_conflict.yml index 36eeeb7..8c182f6 100644 --- a/tests/tests_server_conflict.yml +++ b/tests/tests_server_conflict.yml @@ -76,3 +76,11 @@ - assert: that: item.msg is not defined or item.msg is defined and item.msg == __expected_error loop: "{{ ansible_failed_result.results }}" + + - name: clean up test files + file: path="{{ item }}" state=absent + loop: + - "{{ __test_ca_cert }}" + - "{{ __test_cert }}" + - "{{ __test_key }}" + delegate_to: localhost -- 2.26.2