Skip to content

honeyhive.tracer.processing.context

Context management and baggage operations for HoneyHive tracers.

This module handles OpenTelemetry context propagation, baggage management, and span enrichment functionality. It provides dynamic baggage discovery and context-aware operations following the multi-instance architecture.

SAFE_PROPAGATION_KEYS module-attribute

SAFE_PROPAGATION_KEYS = frozenset(
    {
        "run_id",
        "dataset_id",
        "datapoint_id",
        "honeyhive_tracer_id",
    }
)

setup_baggage_context

setup_baggage_context(
    tracer_instance: HoneyHiveTracer,
) -> None

Set up baggage with session context for OpenInference integration.

This function dynamically discovers and sets up baggage items from the tracer configuration and environment. It supports experiment harness integration and evaluation context propagation.

:param tracer_instance: The tracer instance to setup baggage for :type tracer_instance: HoneyHiveTracer

Example:

.. code-block:: python

tracer = HoneyHiveTracer(api_key="key", project="project")
setup_baggage_context(tracer)
# Baggage is now available to all spans created by this tracer

Note:

This function uses dynamic discovery to find all relevant context information and automatically sets up baggage for downstream spans. It gracefully handles missing or invalid configuration.

Source code in src/honeyhive/tracer/processing/context.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def setup_baggage_context(tracer_instance: "HoneyHiveTracer") -> None:
    """Set up baggage with session context for OpenInference integration.

    This function dynamically discovers and sets up baggage items from the
    tracer configuration and environment. It supports experiment harness
    integration and evaluation context propagation.

    :param tracer_instance: The tracer instance to setup baggage for
    :type tracer_instance: HoneyHiveTracer

    **Example:**

    .. code-block:: python

        tracer = HoneyHiveTracer(api_key="key", project="project")
        setup_baggage_context(tracer)
        # Baggage is now available to all spans created by this tracer

    **Note:**

    This function uses dynamic discovery to find all relevant context
    information and automatically sets up baggage for downstream spans.
    It gracefully handles missing or invalid configuration.
    """
    try:
        # Dynamically discover baggage items
        baggage_items = _discover_baggage_items(tracer_instance)

        # Set up baggage context
        _apply_baggage_context(baggage_items, tracer_instance)

        safe_log(
            tracer_instance,
            "debug",
            "Baggage context set up successfully",
            honeyhive_data={
                "baggage_items": list(baggage_items.keys()),
                "item_count": len(baggage_items),
            },
        )

    except Exception as e:
        safe_log(
            tracer_instance,
            "warning",
            "Failed to set up baggage context",
            honeyhive_data={"error": str(e)},
        )

enrich_span_context

enrich_span_context(
    event_name: str,
    *,
    attributes: Optional[Dict[str, Any]] = None,
    inputs: Optional[Dict[str, Any]] = None,
    outputs: Optional[Dict[str, Any]] = None,
    metadata: Optional[Dict[str, Any]] = None,
    metrics: Optional[Dict[str, Any]] = None,
    feedback: Optional[Dict[str, Any]] = None,
    config: Optional[Dict[str, Any]] = None,
    user_properties: Optional[Dict[str, Any]] = None,
    error: Optional[str] = None,
    event_id: Optional[str] = None,
    session_id: Optional[str] = None,
    project: Optional[str] = None,
    source: Optional[str] = None,
    tracer_instance: Any = None
) -> Iterator[Any]

Create an enriched span with HoneyHive-specific attributes.

Note: Multiple positional arguments are required to maintain backward compatibility with existing API usage patterns and provide flexibility for span enrichment configuration.

This context manager creates a span with automatic HoneyHive attribute enrichment, including session context, experiment information, and dynamic attribute discovery. It supports all reserved parameters from enrich_span() for consistent API usage.

:param event_name: Human-readable name for the operation being traced :type event_name: str :param attributes: Initial attributes to set on the span (direct span attributes) :type attributes: Optional[Dict[str, Any]] :param inputs: Inputs namespace (automatically prefixed with 'honeyhive_inputs.') :type inputs: Optional[Dict[str, Any]] :param outputs: Outputs namespace (automatically prefixed with 'honeyhive_outputs.') :type outputs: Optional[Dict[str, Any]] :param metadata: Metadata namespace (prefixed: 'honeyhive_metadata.') :type metadata: Optional[Dict[str, Any]] :param metrics: Metrics namespace (prefixed: 'honeyhive_metrics.') :type metrics: Optional[Dict[str, Any]] :param feedback: Feedback namespace (prefixed: 'honeyhive_feedback.') :type feedback: Optional[Dict[str, Any]] :param config: Config namespace (prefixed: 'honeyhive_config.') :type config: Optional[Dict[str, Any]] :param user_properties: User properties namespace (prefixed: 'honeyhive_user_properties.') :type user_properties: Optional[Dict[str, Any]] :param error: Error message (stored as 'honeyhive_error', non-namespaced) :type error: Optional[str] :param event_id: Event ID (stored as 'honeyhive_event_id', non-namespaced) :type event_id: Optional[str] :param session_id: Optional session ID for the span :type session_id: Optional[str] :param project: Optional project name for the span :type project: Optional[str] :param source: Optional source environment for the span :type source: Optional[str] :param tracer_instance: Optional tracer instance :type tracer_instance: Any :return: Context manager yielding the enriched span :rtype: Iterator[Any]

Example:

.. code-block:: python

with enrich_span_context("user_lookup",
                       attributes={"user.id": "12345"},
                       inputs={"user_id": "12345"}) as span:
    user = get_user_by_id("12345")
    span.set_attribute("user.found", user is not None)

Note:

This function automatically adds HoneyHive-specific attributes and experiment context to the span. Reserved parameters (inputs, outputs, metadata, etc.) are handled via enrich_span_core() for consistent namespacing and backend recognition.

Source code in src/honeyhive/tracer/processing/context.py
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
@contextmanager
def enrich_span_context(  # pylint: disable=too-many-arguments
    event_name: str,
    *,
    attributes: Optional[Dict[str, Any]] = None,
    inputs: Optional[Dict[str, Any]] = None,
    outputs: Optional[Dict[str, Any]] = None,
    metadata: Optional[Dict[str, Any]] = None,
    metrics: Optional[Dict[str, Any]] = None,
    feedback: Optional[Dict[str, Any]] = None,
    config: Optional[Dict[str, Any]] = None,
    user_properties: Optional[Dict[str, Any]] = None,
    error: Optional[str] = None,
    event_id: Optional[str] = None,
    session_id: Optional[str] = None,
    project: Optional[str] = None,
    source: Optional[str] = None,
    tracer_instance: Any = None,
) -> Iterator[Any]:
    """Create an enriched span with HoneyHive-specific attributes.

    Note: Multiple positional arguments are required to maintain backward
    compatibility with existing API usage patterns and provide flexibility
    for span enrichment configuration.

    This context manager creates a span with automatic HoneyHive attribute
    enrichment, including session context, experiment information, and
    dynamic attribute discovery. It supports all reserved parameters from
    enrich_span() for consistent API usage.

    :param event_name: Human-readable name for the operation being traced
    :type event_name: str
    :param attributes: Initial attributes to set on the span (direct span attributes)
    :type attributes: Optional[Dict[str, Any]]
    :param inputs: Inputs namespace (automatically prefixed with 'honeyhive_inputs.')
    :type inputs: Optional[Dict[str, Any]]
    :param outputs: Outputs namespace (automatically prefixed with 'honeyhive_outputs.')
    :type outputs: Optional[Dict[str, Any]]
    :param metadata: Metadata namespace (prefixed: 'honeyhive_metadata.')
    :type metadata: Optional[Dict[str, Any]]
    :param metrics: Metrics namespace (prefixed: 'honeyhive_metrics.')
    :type metrics: Optional[Dict[str, Any]]
    :param feedback: Feedback namespace (prefixed: 'honeyhive_feedback.')
    :type feedback: Optional[Dict[str, Any]]
    :param config: Config namespace (prefixed: 'honeyhive_config.')
    :type config: Optional[Dict[str, Any]]
    :param user_properties: User properties namespace
        (prefixed: 'honeyhive_user_properties.')
    :type user_properties: Optional[Dict[str, Any]]
    :param error: Error message (stored as 'honeyhive_error', non-namespaced)
    :type error: Optional[str]
    :param event_id: Event ID (stored as 'honeyhive_event_id', non-namespaced)
    :type event_id: Optional[str]
    :param session_id: Optional session ID for the span
    :type session_id: Optional[str]
    :param project: Optional project name for the span
    :type project: Optional[str]
    :param source: Optional source environment for the span
    :type source: Optional[str]
    :param tracer_instance: Optional tracer instance
    :type tracer_instance: Any
    :return: Context manager yielding the enriched span
    :rtype: Iterator[Any]

    **Example:**

    .. code-block:: python

        with enrich_span_context("user_lookup",
                               attributes={"user.id": "12345"},
                               inputs={"user_id": "12345"}) as span:
            user = get_user_by_id("12345")
            span.set_attribute("user.found", user is not None)

    **Note:**

    This function automatically adds HoneyHive-specific attributes and
    experiment context to the span. Reserved parameters (inputs, outputs,
    metadata, etc.) are handled via enrich_span_core() for consistent
    namespacing and backend recognition.
    """
    # Import here to avoid circular dependency
    from ..instrumentation.enrichment import (  # pylint: disable=import-outside-toplevel
        enrich_span_core,
    )

    # Get tracer from tracer instance if available, otherwise use global fallback
    if (
        tracer_instance
        and hasattr(tracer_instance, "tracer")
        and tracer_instance.tracer
    ):
        tracer = tracer_instance.tracer
    else:
        # Fallback for cases where no tracer instance is provided
        tracer = trace.get_tracer("honeyhive.fallback")

    # Prepare enriched attributes (HoneyHive core: session_id, project, source)
    enriched_attributes = _prepare_enriched_attributes(
        attributes, session_id, project, source, tracer_instance
    )

    # Create span using tracer.start_span() for proper lifecycle management
    # Also use trace.use_span() to make it current for enrich_span_core()
    with tracer.start_span(event_name, attributes=enriched_attributes) as span:
        # Make this span current in OpenTelemetry context using use_span()
        # Ensures enrich_span_core() gets correct span via get_current_span()
        # end_on_exit=False as tracer.start_span() handles finalization
        with trace.use_span(  # pylint: disable=not-context-manager
            span, end_on_exit=False
        ):
            try:
                # Span is now the current span in OpenTelemetry context
                # Use enrich_span_core() to set reserved params with namespacing
                # This reuses all existing logic without duplication

                # Debug logging: Check if we have reserved parameters to set
                has_reserved_params = any(
                    [
                        inputs,
                        outputs,
                        metadata,
                        metrics,
                        feedback,
                        config,
                        user_properties,
                        error,
                        event_id,
                    ]
                )

                if has_reserved_params:
                    safe_log(
                        tracer_instance,
                        "debug",
                        f"Enriching span '{event_name}' with reserved parameters",
                        honeyhive_data={
                            "event_name": event_name,
                            "has_inputs": bool(inputs),
                            "has_outputs": bool(outputs),
                            "has_metadata": bool(metadata),
                            "has_metrics": bool(metrics),
                            "has_feedback": bool(feedback),
                            "has_config": bool(config),
                            "has_user_properties": bool(user_properties),
                            "has_error": bool(error),
                            "has_event_id": bool(event_id),
                        },
                    )

                enrich_span_core(
                    inputs=inputs,
                    outputs=outputs,
                    metadata=metadata,
                    metrics=metrics,
                    feedback=feedback,
                    config=config,
                    user_properties=user_properties,
                    error=error,
                    event_id=event_id,
                    tracer_instance=tracer_instance,
                    verbose=(
                        getattr(tracer_instance, "verbose", False)
                        if tracer_instance
                        else False
                    ),
                    # attributes handled via enriched_attributes above
                )

                # Debug logging: Verify span attributes were set
                if has_reserved_params and hasattr(span, "attributes"):
                    # Try to get span attributes for debugging
                    span_attrs = getattr(span, "attributes", {})
                    safe_log(
                        tracer_instance,
                        "debug",
                        f"Span '{event_name}' enrichment completed",
                        honeyhive_data={
                            "event_name": event_name,
                            "span_has_attributes": bool(span_attrs),
                            "span_is_recording": (
                                span.is_recording()
                                if hasattr(span, "is_recording")
                                else None
                            ),
                        },
                    )

                yield span
            except Exception as e:
                # Record exception and re-raise
                if hasattr(span, "record_exception"):
                    span.record_exception(e)
                if hasattr(span, "set_status"):
                    span.set_status(Status(StatusCode.ERROR, str(e)))
                raise

get_current_baggage

get_current_baggage() -> Dict[str, str]

Get all baggage items from the current OpenTelemetry context.

This function dynamically discovers and returns all baggage items from the current context, providing a way to inspect what context information is available to spans.

:return: Dictionary of all baggage key-value pairs :rtype: Dict[str, str]

Example:

.. code-block:: python

current_baggage = get_current_baggage()
print(f"Session ID: {current_baggage.get('session_id')}")
print(f"Project: {current_baggage.get('project')}")

Note:

This function provides read-only access to the current baggage. To modify baggage, use the tracer's baggage management methods.

Source code in src/honeyhive/tracer/processing/context.py
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
def get_current_baggage() -> Dict[str, str]:
    """Get all baggage items from the current OpenTelemetry context.

    This function dynamically discovers and returns all baggage items
    from the current context, providing a way to inspect what context
    information is available to spans.

    :return: Dictionary of all baggage key-value pairs
    :rtype: Dict[str, str]

    **Example:**

    .. code-block:: python

        current_baggage = get_current_baggage()
        print(f"Session ID: {current_baggage.get('session_id')}")
        print(f"Project: {current_baggage.get('project')}")

    **Note:**

    This function provides read-only access to the current baggage.
    To modify baggage, use the tracer's baggage management methods.
    """
    try:
        current_baggage = {}
        ctx = context.get_current()

        # Get all baggage items dynamically
        baggage_dict = baggage.get_all(ctx)

        for key, value in baggage_dict.items():
            current_baggage[key] = str(value)

        # Baggage retrieved successfully (removed logging from utility function)

        return current_baggage

    except Exception:
        # Error getting baggage (removed logging from utility function)
        return {}

inject_context_into_carrier

inject_context_into_carrier(
    carrier: Dict[str, str],
    tracer_instance: HoneyHiveTracer,
) -> None

Inject OpenTelemetry context into a carrier dictionary.

This function injects the current OpenTelemetry context (including trace context and baggage) into a carrier dictionary for cross-service or cross-process propagation.

:param carrier: Dictionary to inject context into :type carrier: Dict[str, str] :param tracer_instance: The tracer instance for propagator access :type tracer_instance: HoneyHiveTracer

Example:

.. code-block:: python

headers = {}
inject_context_into_carrier(headers, tracer)
# headers now contains trace context and baggage

# Use headers in HTTP request
response = requests.get(url, headers=headers)

Note:

The carrier dictionary will be modified in-place with context information. This is typically used for HTTP headers or message metadata in distributed systems.

Source code in src/honeyhive/tracer/processing/context.py
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
def inject_context_into_carrier(
    carrier: Dict[str, str], tracer_instance: "HoneyHiveTracer"
) -> None:
    """Inject OpenTelemetry context into a carrier dictionary.

    This function injects the current OpenTelemetry context (including
    trace context and baggage) into a carrier dictionary for cross-service
    or cross-process propagation.

    :param carrier: Dictionary to inject context into
    :type carrier: Dict[str, str]
    :param tracer_instance: The tracer instance for propagator access
    :type tracer_instance: HoneyHiveTracer

    **Example:**

    .. code-block:: python

        headers = {}
        inject_context_into_carrier(headers, tracer)
        # headers now contains trace context and baggage

        # Use headers in HTTP request
        response = requests.get(url, headers=headers)

    **Note:**

    The carrier dictionary will be modified in-place with context
    information. This is typically used for HTTP headers or message
    metadata in distributed systems.
    """

    try:
        if not tracer_instance.propagator:
            safe_log(
                tracer_instance,
                "warning",
                "No propagator available for context injection",
            )
            return

        # Inject current context into carrier
        tracer_instance.propagator.inject(carrier)

        safe_log(
            tracer_instance,
            "debug",
            "Context injected into carrier",
            honeyhive_data={
                "carrier_keys": list(carrier.keys()),
                "injected_items": len(carrier),
            },
        )

    except Exception as e:
        safe_log(
            tracer_instance,
            "error",
            "Failed to inject context into carrier: %s",
            e,
            honeyhive_data={"carrier_keys": list(carrier.keys())},
        )

with_distributed_trace_context

with_distributed_trace_context(
    carrier: Dict[str, str],
    tracer_instance: HoneyHiveTracer,
    *,
    session_id: Optional[str] = None
) -> Iterator[Context]

Context manager for distributed tracing that extracts and sets up context.

This function extracts OpenTelemetry context from a carrier (e.g., HTTP headers), extracts session_id from baggage if available, and attaches the context with session_id in baggage. This is the recommended way to handle distributed tracing on the server side.

:param carrier: Dictionary containing trace context (e.g., HTTP headers) :type carrier: Dict[str, str] :param tracer_instance: The tracer instance for propagator access :type tracer_instance: HoneyHiveTracer :param session_id: Optional explicit session_id to use (overrides baggage) :type session_id: Optional[str] :return: Context manager that yields the extracted context

Example:

.. code-block:: python

@app.route("/api/endpoint", methods=["POST"])
def my_endpoint():
    with with_distributed_trace_context(dict(request.headers), tracer) as ctx:
        # All spans created here will use the propagated session_id
        with tracer.start_span("operation"):
            pass

Note for async functions:

If you need to use this with asyncio.run(), you'll need to re-attach the context inside the async function since asyncio.run() creates a new event loop:

.. code-block:: python

with with_distributed_trace_context(dict(request.headers), tracer) as ctx:
    async def my_async_function():
        # Re-attach context in new event loop
        token = context.attach(ctx)
        try:
            # Your async code here
            pass
        finally:
            context.detach(token)

    asyncio.run(my_async_function())
Source code in src/honeyhive/tracer/processing/context.py
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
@contextmanager
def with_distributed_trace_context(
    carrier: Dict[str, str],
    tracer_instance: "HoneyHiveTracer",
    *,
    session_id: Optional[str] = None,
) -> Iterator["Context"]:
    """Context manager for distributed tracing that extracts and sets up context.

    This function extracts OpenTelemetry context from a carrier (e.g., HTTP headers),
    extracts session_id from baggage if available, and attaches the context with
    session_id in baggage. This is the recommended way to handle distributed tracing
    on the server side.

    :param carrier: Dictionary containing trace context (e.g., HTTP headers)
    :type carrier: Dict[str, str]
    :param tracer_instance: The tracer instance for propagator access
    :type tracer_instance: HoneyHiveTracer
    :param session_id: Optional explicit session_id to use (overrides baggage)
    :type session_id: Optional[str]
    :return: Context manager that yields the extracted context

    **Example:**

    .. code-block:: python

        @app.route("/api/endpoint", methods=["POST"])
        def my_endpoint():
            with with_distributed_trace_context(dict(request.headers), tracer) as ctx:
                # All spans created here will use the propagated session_id
                with tracer.start_span("operation"):
                    pass

    **Note for async functions:**

    If you need to use this with `asyncio.run()`, you'll need to re-attach the context
    inside the async function since `asyncio.run()` creates a new event loop:

    .. code-block:: python

        with with_distributed_trace_context(dict(request.headers), tracer) as ctx:
            async def my_async_function():
                # Re-attach context in new event loop
                token = context.attach(ctx)
                try:
                    # Your async code here
                    pass
                finally:
                    context.detach(token)

            asyncio.run(my_async_function())
    """
    # Extract trace context from carrier
    incoming_context = extract_context_from_carrier(carrier, tracer_instance)

    # Extract session_id, project, source from baggage header if not explicit
    propagated_session_id = session_id
    propagated_project = None
    propagated_source = None

    if not propagated_session_id:
        baggage_header = carrier.get("baggage") or carrier.get("Baggage")
        if baggage_header:
            # Parse baggage manually (fallback if extract doesn't populate)
            for item in baggage_header.split(","):
                if "=" in item:
                    key, value = item.split("=", 1)
                    key = key.strip()
                    value = value.strip()

                    # Extract session_id
                    if key in (
                        "session_id",
                        "honeyhive_session_id",
                        "honeyhive.session_id",
                    ):
                        propagated_session_id = value
                    # Extract project
                    elif key in ("project", "honeyhive_project", "honeyhive.project"):
                        propagated_project = value
                    # Extract source
                    elif key in ("source", "honeyhive_source", "honeyhive.source"):
                        propagated_source = value

    # Set up context with session_id, project, and source in baggage
    context_to_use = incoming_context if incoming_context else context.get_current()
    if propagated_session_id:
        context_to_use = baggage.set_baggage(
            "session_id", propagated_session_id, context_to_use
        )
    if propagated_project:
        context_to_use = baggage.set_baggage(
            "project", propagated_project, context_to_use
        )
    if propagated_source:
        context_to_use = baggage.set_baggage(
            "source", propagated_source, context_to_use
        )

    # Attach context
    token = context.attach(context_to_use)
    try:
        yield context_to_use
    finally:
        context.detach(token)

extract_context_from_carrier

extract_context_from_carrier(
    carrier: Dict[str, str],
    tracer_instance: HoneyHiveTracer,
) -> Optional[Context]

Extract OpenTelemetry context from a carrier dictionary.

This function extracts OpenTelemetry context (including trace context and baggage) from a carrier dictionary, typically received from another service or process.

:param carrier: Dictionary containing context information :type carrier: Dict[str, str] :param tracer_instance: The tracer instance for propagator access :type tracer_instance: HoneyHiveTracer :return: Extracted OpenTelemetry context or None if extraction fails :rtype: Optional[Context]

Example:

.. code-block:: python

# Extract context from HTTP headers
extracted_context = extract_context_from_carrier(request.headers, tracer)

# Use extracted context as parent for new spans
with tracer.start_span("operation", context=extracted_context) as span:
    # This span will be a child of the remote span
    pass

Note:

This function is typically used in service endpoints to continue distributed traces from upstream services. The extracted context can be used as a parent context for new spans.

Source code in src/honeyhive/tracer/processing/context.py
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
def extract_context_from_carrier(
    carrier: Dict[str, str], tracer_instance: "HoneyHiveTracer"
) -> Optional["Context"]:
    """Extract OpenTelemetry context from a carrier dictionary.

    This function extracts OpenTelemetry context (including trace context
    and baggage) from a carrier dictionary, typically received from another
    service or process.

    :param carrier: Dictionary containing context information
    :type carrier: Dict[str, str]
    :param tracer_instance: The tracer instance for propagator access
    :type tracer_instance: HoneyHiveTracer
    :return: Extracted OpenTelemetry context or None if extraction fails
    :rtype: Optional[Context]

    **Example:**

    .. code-block:: python

        # Extract context from HTTP headers
        extracted_context = extract_context_from_carrier(request.headers, tracer)

        # Use extracted context as parent for new spans
        with tracer.start_span("operation", context=extracted_context) as span:
            # This span will be a child of the remote span
            pass

    **Note:**

    This function is typically used in service endpoints to continue
    distributed traces from upstream services. The extracted context
    can be used as a parent context for new spans.
    """

    try:
        if not tracer_instance.propagator:
            safe_log(
                tracer_instance,
                "warning",
                "No propagator available for context extraction",
            )
            return None

        # Extract context from carrier
        extracted_context: Optional["Context"] = tracer_instance.propagator.extract(
            carrier
        )

        safe_log(
            tracer_instance,
            "debug",
            "Context extracted from carrier",
            honeyhive_data={
                "carrier_keys": list(carrier.keys()),
                "has_context": extracted_context is not None,
            },
        )

        return extracted_context

    except Exception as e:
        safe_log(
            tracer_instance,
            "error",
            "Failed to extract context from carrier: %s",
            e,
            honeyhive_data={"carrier_keys": list(carrier.keys())},
        )
        return None