Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 31 additions & 25 deletions src/instana/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
import importlib
import os
import sys
from importlib import util as importlib_util
from typing import Tuple

from instana.collector.helpers.runtime import (
is_autowrapt_instrumented,
is_webhook_instrumented,
)
from instana.util.config import is_truthy
from instana.version import VERSION

__author__ = "Instana Inc."
Expand Down Expand Up @@ -70,7 +72,7 @@ def load(_: object) -> None:
def apply_gevent_monkey_patch() -> None:
from gevent import monkey

if os.environ.get("INSTANA_GEVENT_MONKEY_OPTIONS"):
if provided_options := os.environ.get("INSTANA_GEVENT_MONKEY_OPTIONS"):

def short_key(k: str) -> str:
return k[3:] if k.startswith("no-") else k
Expand All @@ -81,12 +83,8 @@ def key_to_bool(k: str) -> bool:
import inspect

all_accepted_patch_all_args = inspect.getfullargspec(monkey.patch_all)[0]
provided_options = (
os.environ.get("INSTANA_GEVENT_MONKEY_OPTIONS")
.replace(" ", "")
.replace("--", "")
.split(",")
)
provided_options.replace(" ", "").replace("--", "").split(",")

provided_options = [
k for k in provided_options if short_key(k) in all_accepted_patch_all_args
]
Expand Down Expand Up @@ -115,9 +113,7 @@ def get_aws_lambda_handler() -> Tuple[str, str]:
handler_function = "lambda_handler"

try:
handler = os.environ.get("LAMBDA_HANDLER", False)

if handler:
if handler := os.environ.get("LAMBDA_HANDLER", None):
parts = handler.split(".")
handler_function = parts.pop().strip()
handler_module = ".".join(parts).strip()
Expand Down Expand Up @@ -159,21 +155,17 @@ def boot_agent() -> None:

import instana.singletons # noqa: F401

# Instrumentation
# Import & initialize instrumentation
if "INSTANA_DISABLE_AUTO_INSTR" not in os.environ:
# TODO: remove the following entries as the migration of the
# instrumentation codes are finalised.

# Import & initialize instrumentation
from instana.instrumentation import (
aio_pika, # noqa: F401
aioamqp, # noqa: F401
asyncio, # noqa: F401
cassandra, # noqa: F401
celery, # noqa: F401
couchbase, # noqa: F401
fastapi, # noqa: F401
flask, # noqa: F401
# gevent_inst, # noqa: F401
grpcio, # noqa: F401
httpx, # noqa: F401
logging, # noqa: F401
Expand All @@ -186,11 +178,10 @@ def boot_agent() -> None:
pyramid, # noqa: F401
redis, # noqa: F401
sanic, # noqa: F401
spyne, # noqa: F401
sqlalchemy, # noqa: F401
starlette, # noqa: F401
urllib3, # noqa: F401
spyne, # noqa: F401
aio_pika, # noqa: F401
)
from instana.instrumentation.aiohttp import (
client as aiohttp_client, # noqa: F401
Expand Down Expand Up @@ -218,14 +209,32 @@ def boot_agent() -> None:
server as tornado_server, # noqa: F401
)

# from instana.instrumentation import gevent_inst # noqa: F401

# Hooks
from instana.hooks import (
hook_gunicorn, # noqa: F401
hook_uwsgi, # noqa: F401
)


if "INSTANA_DISABLE" not in os.environ:
def _start_profiler() -> None:
"""Start the Instana Auto Profile."""
from instana.singletons import get_profiler

if profiler := get_profiler():
profiler.start()


if "INSTANA_DISABLE" in os.environ: # pragma: no cover
import warnings

message = "Instana: The INSTANA_DISABLE environment variable is deprecated. Please use INSTANA_TRACING_DISABLE=True instead."
warnings.simplefilter("always")
warnings.warn(message, DeprecationWarning)


if not is_truthy(os.environ.get("INSTANA_TRACING_DISABLE", None)):
# There are cases when sys.argv may not be defined at load time. Seems to happen in embedded Python,
# and some Pipenv installs. If this is the case, it's best effort.
if (
Expand All @@ -243,15 +252,12 @@ def boot_agent() -> None:
if (
(is_autowrapt_instrumented() or is_webhook_instrumented())
and "INSTANA_DISABLE_AUTO_INSTR" not in os.environ
and importlib.util.find_spec("gevent")
and importlib_util.find_spec("gevent")
):
apply_gevent_monkey_patch()

# AutoProfile
if "INSTANA_AUTOPROFILE" in os.environ:
from instana.singletons import get_profiler

profiler = get_profiler()
if profiler:
profiler.start()
_start_profiler()

boot_agent()
13 changes: 10 additions & 3 deletions src/instana/instrumentation/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import wrapt

from instana.log import logger
from instana.singletons import agent
from instana.util.runtime import get_runtime_env_info
from instana.util.traceutils import get_tracer_tuple, tracing_is_off

Expand All @@ -27,12 +28,18 @@ def log_with_instana(

# We take into consideration if `stacklevel` is already present in `kwargs`.
# This prevents the error `_log() got multiple values for keyword argument 'stacklevel'`
stacklevel_in = kwargs.pop("stacklevel", 1 if get_runtime_env_info()[0] not in ["ppc64le", "s390x"] else 2)
stacklevel_in = kwargs.pop(
"stacklevel", 1 if get_runtime_env_info()[0] not in ["ppc64le", "s390x"] else 2
)
stacklevel = stacklevel_in + 1 + (sys.version_info >= (3, 14))

try:
# Only needed if we're tracing and serious log
if tracing_is_off() or argv[0] < logging.WARN:
# Only needed if we're tracing and serious log and logging spans are not disabled
if (
tracing_is_off()
or argv[0] < logging.WARN
or agent.options.is_span_disabled(category="logging")
):
return wrapped(*argv, **kwargs, stacklevel=stacklevel)

tracer, parent_span, _ = get_tracer_tuple()
Expand Down
103 changes: 97 additions & 6 deletions src/instana/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@

import logging
import os
from typing import Any, Dict
from typing import Any, Dict, Sequence

from instana.configurator import config
from instana.log import logger
from instana.util.config import (is_truthy, parse_ignored_endpoints,
parse_ignored_endpoints_from_yaml)
from instana.util.config import (
SPAN_TYPE_TO_CATEGORY,
get_disable_trace_configurations_from_env,
get_disable_trace_configurations_from_local,
get_disable_trace_configurations_from_yaml,
is_truthy,
parse_ignored_endpoints,
parse_ignored_endpoints_from_yaml,
parse_span_disabling,
)
from instana.util.runtime import determine_service_name


Expand All @@ -37,6 +45,11 @@ def __init__(self, **kwds: Dict[str, Any]) -> None:
self.ignore_endpoints = []
self.kafka_trace_correlation = True

# disabled_spans lists all categories and types that should be disabled
self.disabled_spans = []
# enabled_spans lists all categories and types that should be enabled, preceding disabled_spans
self.enabled_spans = []

self.set_trace_configurations()

# Defaults
Expand Down Expand Up @@ -75,8 +88,9 @@ def set_trace_configurations(self) -> None:
)

# Check if either of the environment variables is truthy
if is_truthy(os.environ.get("INSTANA_ALLOW_EXIT_AS_ROOT", None)) or \
is_truthy(os.environ.get("INSTANA_ALLOW_ROOT_EXIT_SPAN", None)):
if is_truthy(os.environ.get("INSTANA_ALLOW_EXIT_AS_ROOT", None)) or is_truthy(
os.environ.get("INSTANA_ALLOW_ROOT_EXIT_SPAN", None)
):
self.allow_exit_as_root = True

# The priority is as follows:
Expand All @@ -99,12 +113,69 @@ def set_trace_configurations(self) -> None:
)

if "INSTANA_KAFKA_TRACE_CORRELATION" in os.environ:
self.kafka_trace_correlation = is_truthy(os.environ["INSTANA_KAFKA_TRACE_CORRELATION"])
self.kafka_trace_correlation = is_truthy(
os.environ["INSTANA_KAFKA_TRACE_CORRELATION"]
)
elif isinstance(config.get("tracing"), dict) and "kafka" in config["tracing"]:
self.kafka_trace_correlation = config["tracing"]["kafka"].get(
"trace_correlation", True
)

self.set_disable_trace_configurations()

def set_disable_trace_configurations(self) -> None:
disabled_spans = []
enabled_spans = []

# The precedence is as follows:
# environment variables > in-code (local) config > agent config (configuration.yaml)
# For the env vars: INSTANA_TRACING_DISABLE > INSTANA_CONFIG_PATH
if "INSTANA_TRACING_DISABLE" in os.environ:
disabled_spans, enabled_spans = get_disable_trace_configurations_from_env()
elif "INSTANA_CONFIG_PATH" in os.environ:
disabled_spans, enabled_spans = get_disable_trace_configurations_from_yaml()
else:
# In-code (local) config
# The agent config (configuration.yaml) is handled in StandardOptions.set_disable_tracing()
disabled_spans, enabled_spans = (
get_disable_trace_configurations_from_local()
)

self.disabled_spans.extend(disabled_spans)
self.enabled_spans.extend(enabled_spans)

def is_span_disabled(self, category=None, span_type=None) -> bool:
"""
Check if a span is disabled based on its category and type.

Args:
category (str): The span category (e.g., "logging", "databases")
span_type (str): The span type (e.g., "redis", "kafka")

Returns:
bool: True if the span is disabled, False otherwise
"""
# If span_type is provided, check if it's disabled
if span_type and span_type in self.disabled_spans:
return True

# If category is provided directly, check if it's disabled
if category and category in self.disabled_spans:
return True

# If span_type is provided but not explicitly configured,
# check if its parent category is disabled. Also check for the precedence rules
if span_type and span_type in SPAN_TYPE_TO_CATEGORY:
parent_category = SPAN_TYPE_TO_CATEGORY[span_type]
if (
parent_category in self.disabled_spans
and span_type not in self.enabled_spans
):
return True

# Default: not disabled
return False


class StandardOptions(BaseOptions):
"""The options class used when running directly on a host/node with an Instana agent"""
Expand Down Expand Up @@ -177,6 +248,26 @@ def set_tracing(self, tracing: Dict[str, Any]) -> None:
if "extra-http-headers" in tracing:
self.extra_http_headers = tracing["extra-http-headers"]

# Handle span disabling configuration
if "disable" in tracing:
self.set_disable_tracing(tracing["disable"])

def set_disable_tracing(self, tracing_config: Sequence[Dict[str, Any]]) -> None:
# The precedence is as follows:
# environment variables > in-code (local) config > agent config (configuration.yaml)
if (
"INSTANA_TRACING_DISABLE" not in os.environ
and "INSTANA_CONFIG_PATH" not in os.environ
and not (
isinstance(config.get("tracing"), dict)
and "disable" in config["tracing"]
)
):
# agent config (configuration.yaml)
disabled_spans, enabled_spans = parse_span_disabling(tracing_config)
self.disabled_spans.extend(disabled_spans)
self.enabled_spans.extend(enabled_spans)

def set_from(self, res_data: Dict[str, Any]) -> None:
"""
Set the source identifiers given to use by the Instana Host agent.
Expand Down
Loading
Loading