diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0024c783..2e4fcd4a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,8 @@ jobs: splunk-version: - "8.1" - "8.2" - - "latest" + - "9.0.8" + - "9.1.3" fail-fast: false steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e2137a6..0e423e67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,5 @@ # Splunk Enterprise SDK for Python Changelog -## Version 2.0.2 - -### Minor changes -* Added six.py file back - - -## Version 2.0.1 - -### Bug fixes -* [#567](https://github.com/splunk/splunk-sdk-python/issues/567) Moved "deprecation" dependency - - ## Version 2.0.0 ### Feature updates diff --git a/README.md b/README.md index e7481836..2bbae4de 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # The Splunk Enterprise Software Development Kit for Python -#### Version 2.0.2 +#### Version 2.0.0 The Splunk Enterprise Software Development Kit (SDK) for Python contains library code designed to enable developers to build applications using the Splunk platform. diff --git a/splunklib/__init__.py b/splunklib/__init__.py index f999a52a..2613d284 100644 --- a/splunklib/__init__.py +++ b/splunklib/__init__.py @@ -30,5 +30,5 @@ def setup_logging(level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE datefmt=date_format) -__version_info__ = (2, 0, 2) +__version_info__ = (2, 0, 0) __version__ = ".".join(map(str, __version_info__)) diff --git a/splunklib/modularinput/event_writer.py b/splunklib/modularinput/event_writer.py index c048a5b5..7be3845a 100644 --- a/splunklib/modularinput/event_writer.py +++ b/splunklib/modularinput/event_writer.py @@ -13,6 +13,7 @@ # under the License. import sys +import traceback from splunklib.utils import ensure_str from .event import ET @@ -66,6 +67,25 @@ def log(self, severity, message): self._err.write(f"{severity} {message}\n") self._err.flush() + def log_exception(self, message, exception=None, severity=None): + """Logs messages about the exception thrown by this modular input to Splunk. + These messages will show up in Splunk's internal logs. + + :param message: ``string``, message to log. + :param exception: ``Exception``, exception thrown by this modular input; if none, sys.exc_info() is used + :param severity: ``string``, severity of message, see severities defined as class constants. Default severity: ERROR + """ + if exception is not None: + tb_str = traceback.format_exception(type(exception), exception, exception.__traceback__) + else: + tb_str = traceback.format_exc() + + if severity is None: + severity = EventWriter.ERROR + + self._err.write(("%s %s - %s" % (severity, message, tb_str)).replace("\n", " ")) + self._err.flush() + def write_xml_document(self, document): """Writes a string representation of an ``ElementTree`` object to the output stream. diff --git a/splunklib/modularinput/script.py b/splunklib/modularinput/script.py index e912d737..a6c96193 100644 --- a/splunklib/modularinput/script.py +++ b/splunklib/modularinput/script.py @@ -91,13 +91,12 @@ def run_script(self, args, event_writer, input_stream): event_writer.write_xml_document(root) return 1 - err_string = "ERROR Invalid arguments to modular input script:" + ' '.join( - args) - event_writer._err.write(err_string) + event_writer.log(EventWriter.ERROR, "Invalid arguments to modular input script:" + ' '.join( + args)) return 1 except Exception as e: - event_writer.log(EventWriter.ERROR, str(e)) + event_writer.log_exception(str(e)) return 1 @property diff --git a/tests/modularinput/test_event.py b/tests/modularinput/test_event.py index 4039d8e2..35e9c09c 100644 --- a/tests/modularinput/test_event.py +++ b/tests/modularinput/test_event.py @@ -15,7 +15,9 @@ # under the License. +import re import sys +from io import StringIO import pytest @@ -150,3 +152,29 @@ def test_write_xml_is_sane(capsys): found_xml = ET.fromstring(captured.out) assert xml_compare(expected_xml, found_xml) + + +def test_log_exception(): + out, err = StringIO(), StringIO() + ew = EventWriter(out, err) + + exc = Exception("Something happened!") + + try: + raise exc + except Exception: + ew.log_exception("ex1") + + assert out.getvalue() == "" + + # Remove paths and line + err = re.sub(r'File "[^"]+', 'File "...', err.getvalue()) + err = re.sub(r'line \d+', 'line 123', err) + + # One line + assert err == ( + 'ERROR ex1 - Traceback (most recent call last): ' + ' File "...", line 123, in test_log_exception ' + ' raise exc ' + 'Exception: Something happened! ' + ) diff --git a/tests/modularinput/test_script.py b/tests/modularinput/test_script.py index 48be8826..bb471710 100644 --- a/tests/modularinput/test_script.py +++ b/tests/modularinput/test_script.py @@ -1,6 +1,7 @@ import sys import io +import re import xml.etree.ElementTree as ET from splunklib.client import Service from splunklib.modularinput import Script, EventWriter, Scheme, Argument, Event @@ -228,3 +229,37 @@ def stream_events(self, inputs, ew): assert output.err == "" assert isinstance(script.service, Service) assert script.service.authority == script.authority_uri + + +def test_log_script_exception(monkeypatch): + out, err = io.StringIO(), io.StringIO() + + # Override abstract methods + class NewScript(Script): + def get_scheme(self): + return None + + def stream_events(self, inputs, ew): + raise RuntimeError("Some error") + + script = NewScript() + input_configuration = data_open("data/conf_with_2_inputs.xml") + + ew = EventWriter(out, err) + + assert script.run_script([TEST_SCRIPT_PATH], ew, input_configuration) == 1 + + # Remove paths and line numbers + err = re.sub(r'File "[^"]+', 'File "...', err.getvalue()) + err = re.sub(r'line \d+', 'line 123', err) + + assert out.getvalue() == "" + assert err == ( + 'ERROR Some error - ' + 'Traceback (most recent call last): ' + ' File "...", line 123, in run_script ' + ' self.stream_events(self._input_definition, event_writer) ' + ' File "...", line 123, in stream_events ' + ' raise RuntimeError("Some error") ' + 'RuntimeError: Some error ' + )