diff --git a/README.md b/README.md index 6967272c..475cc712 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,19 @@ Programmatic Errors indicating that a non-fatal error occurred, such as an appli #### Creating a Programmatic Error -Programmatic errors are created by adding [span events](https://opentelemetry.io/docs/instrumentation/python/manual/#adding-events) with a custom attribute being set with the key name `lumigo.type`. +Programmatic errors can now be created easily using the `create_programmatic_error` function provided by the `lumigo_opentelemetry` package. This allows you to capture and report errors with minimal setup, without requiring direct interaction with the OpenTelemetry trace SDK. + +For example, you can add a programmatic error as follows: + +```python +from lumigo_opentelemetry import create_programmatic_error + +create_programmatic_error("The customer 123 was not found", "CustomerNotExist") +``` + +The first argument, "Error message", is a descriptive message for the error, while the second argument, "ErrorType", represents the type of the error. + +Alternately, programmatic errors can also be created by adding [span events](https://opentelemetry.io/docs/instrumentation/python/manual/#adding-events) with a custom attribute being set with the key name `lumigo.type`. For example, you could add a programmatic error as follows: diff --git a/src/lumigo_opentelemetry/__init__.py b/src/lumigo_opentelemetry/__init__.py index 8dfce10c..71e3c36c 100644 --- a/src/lumigo_opentelemetry/__init__.py +++ b/src/lumigo_opentelemetry/__init__.py @@ -296,6 +296,12 @@ def wrapper(*args: List[Any], **kwargs: Dict[Any, Any]) -> T: return wrapper +def create_programmatic_error(error_message: str, error_type: str) -> None: + from opentelemetry.trace import get_current_span + + get_current_span().add_event(error_message, {"lumigo.type": error_type}) + + # Load the package on import init_data = init() @@ -309,4 +315,5 @@ def wrapper(*args: List[Any], **kwargs: Dict[Any, Any]) -> T: "logger", "tracer_provider", "logger_provider", + "create_programmatic_error", ] diff --git a/src/test/unit/test_tracer.py b/src/test/unit/test_tracer.py index 5d7998c3..fea1fd5e 100644 --- a/src/test/unit/test_tracer.py +++ b/src/test/unit/test_tracer.py @@ -151,6 +151,38 @@ def test_python_version_too_new(self): ) +class TestCreateProgrammaticError(unittest.TestCase): + @httpretty.activate(allow_net_connect=False) + def test_create_event_with_correct_attributes(self): + from lumigo_opentelemetry import create_programmatic_error, tracer_provider + + self.assertIsNotNone(create_programmatic_error) + + span_processor = Mock(SpanProcessor) + tracer_provider.add_span_processor(span_processor) + + tracer = tracer_provider.get_tracer(__name__) + with tracer.start_as_current_span("Root") as span: + create_programmatic_error("Error message", "ErrorType") + + # Verify `on_start` was called + span_processor.on_start.assert_called() + + # Capture the span passed to `on_start` + span = span_processor.on_start.call_args[0][ + 0 + ] # Extract the `span` argument from the first call + + events = span.events + # Verify the event was added + self.assertEqual(len(events), 1) + + event = events[0] + # Verify the event has the correct attributes + self.assertEqual(event.name, "Error message") + self.assertEqual(event.attributes["lumigo.type"], "ErrorType") + + class TestLumigoWrapped(unittest.TestCase): @httpretty.activate(allow_net_connect=False) def test_access_lumigo_wrapped(self):