Skip to content

Commit

Permalink
feat: do not export empty sqs polling spans
Browse files Browse the repository at this point in the history
  • Loading branch information
sagivoululumigo committed Sep 7, 2023
1 parent a960fc4 commit a063aa7
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 2 deletions.
5 changes: 3 additions & 2 deletions src/lumigo_opentelemetry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ def init() -> Dict[str, Any]:
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import SpanLimits, TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

from lumigo_opentelemetry.resources.span_processor import LumigoSpanProcessor

DEFAULT_LUMIGO_ENDPOINT = (
"https://ga-otlp.lumigo-tracer-edge.golumigo.com/v1/traces"
Expand Down Expand Up @@ -125,7 +126,7 @@ def init() -> Dict[str, Any]:

if lumigo_token:
tracer_provider.add_span_processor(
BatchSpanProcessor(
LumigoSpanProcessor(
OTLPSpanExporter(
endpoint=lumigo_endpoint,
headers={"Authorization": f"LumigoToken {lumigo_token}"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
extract_region_from_arn,
get_resource_fullname,
)
from lumigo_opentelemetry.resources.span_processor import set_span_no_export
from lumigo_opentelemetry import logger
from lumigo_opentelemetry.libs.general_utils import get_boolean_env_var
from lumigo_opentelemetry.libs.environment_variables import AUTO_FILTER_EMPTY_SQS


class AwsParser:
Expand Down Expand Up @@ -133,6 +137,15 @@ def parse_request(
}
span.set_attributes(attributes)

@staticmethod
def _should_skip_empty_sqs_polling_response(operation_name: str, result: Dict[Any, Any]) -> bool:
"""
checks the sqs response & returns true if the request receive messages from SQS but no messages were returned
"""

empty_sqs_poll = operation_name == 'ReceiveMessage' and 'Messages' not in result
return empty_sqs_poll and get_boolean_env_var(AUTO_FILTER_EMPTY_SQS, True)

@staticmethod
def parse_response(
span: Span, service_name: str, operation_name: str, result: Dict[Any, Any]
Expand All @@ -145,6 +158,12 @@ def parse_response(
{"lumigoData": json.dumps({"trigger": trigger_details})}
)

# Filter out sqs polls with empty response
if SqsParser._should_skip_empty_sqs_polling_response(operation_name, result):
logger.debug('Not tracing empty SQS polling requests '
f'(override by setting the {AUTO_FILTER_EMPTY_SQS} env var to false)')
set_span_no_export(span)


class LambdaParser(AwsParser):
@staticmethod
Expand Down
1 change: 1 addition & 0 deletions src/lumigo_opentelemetry/libs/environment_variables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
AUTO_FILTER_EMPTY_SQS = 'AUTO_FILTER_EMPTY_SQS'
13 changes: 13 additions & 0 deletions src/lumigo_opentelemetry/libs/general_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,16 @@ def get_max_size() -> int:
os.environ.get(OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, DEFAULT_MAX_ENTRY_SIZE),
)
)


def get_boolean_env_var(env_var_name: str, default: bool = False) -> bool:
"""
This function return the boolean value of the given environment variable.
If this values doesn't exist, return default.
@param env_var_name: The env var to get (case-sensitive)
@param default: Default value if env var is not set
@return: The boolean value of the env var
"""

return os.environ.get(env_var_name, str(default)).lower() == "true"
37 changes: 37 additions & 0 deletions src/lumigo_opentelemetry/resources/span_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from opentelemetry.trace import Span
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import BatchSpanProcessor

from lumigo_opentelemetry import logger


class LumigoSpanProcessor(BatchSpanProcessor):

def on_end(self, span: ReadableSpan) -> None:
if should_not_export_span(span):
logger.debug('Not exporting span because it has NO_EXPORT=True attribute')
return

return super().on_end(span)


def should_not_export_span(span: ReadableSpan) -> bool:
"""
Given a span, returns an answer if the span should be exported or not.
@param span: A readable span to check
@return: True if the span should not be exported, False otherwise
"""
return span.attributes.get('NO_EXPORT') is True


def set_span_no_export(span: Span, no_export: bool = True):
"""
marks the span as a span not intended for export (for example in spans that create a lot of noise and customers
do not want to trace)
@param span: The span to mark (The span is altered in place)
@param no_export: Should the span be exported or not (default is True, the span will not be exported)
@return:
"""

span.set_attributes({'NO_EXPORT': no_export})

0 comments on commit a063aa7

Please sign in to comment.