Skip to content

Commit 05e798a

Browse files
committed
enhancement: refactoring project structure and pep8 format
Signed-off-by: Cagri Yonca <[email protected]>
1 parent 088148a commit 05e798a

File tree

11 files changed

+231
-267
lines changed

11 files changed

+231
-267
lines changed

docker-compose.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ services:
1313
ports:
1414
- 9042:9042
1515

16-
1716
couchbase:
1817
image: public.ecr.aws/docker/library/couchbase:community
1918
ports:

src/instana/__init__.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,10 @@ def boot_agent() -> None:
168168
from instana.instrumentation import (
169169
aioamqp, # noqa: F401
170170
asyncio, # noqa: F401
171-
boto3_inst, # noqa: F401
172-
cassandra_inst, # noqa: F401
171+
cassandra, # noqa: F401
173172
celery, # noqa: F401
174-
couchbase_inst, # noqa: F401
175-
fastapi_inst, # noqa: F401
173+
couchbase, # noqa: F401
174+
fastapi, # noqa: F401
176175
flask, # noqa: F401
177176
# gevent_inst, # noqa: F401
178177
grpcio, # noqa: F401
@@ -185,9 +184,9 @@ def boot_agent() -> None:
185184
pymysql, # noqa: F401
186185
pyramid, # noqa: F401
187186
redis, # noqa: F401
188-
sanic_inst, # noqa: F401
187+
sanic, # noqa: F401
189188
sqlalchemy, # noqa: F401
190-
starlette_inst, # noqa: F401
189+
starlette, # noqa: F401
191190
urllib3, # noqa: F401
192191
)
193192
from instana.instrumentation.aiohttp import (
@@ -196,7 +195,10 @@ def boot_agent() -> None:
196195
from instana.instrumentation.aiohttp import (
197196
server as aiohttp_server, # noqa: F401
198197
)
199-
from instana.instrumentation.aws import lambda_inst # noqa: F401
198+
from instana.instrumentation.aws import (
199+
boto3, # noqa: F401
200+
lambda_inst, # noqa: F401
201+
)
200202
from instana.instrumentation.django import middleware # noqa: F401
201203
from instana.instrumentation.google.cloud import (
202204
pubsub, # noqa: F401
@@ -209,12 +211,14 @@ def boot_agent() -> None:
209211
client as tornado_client, # noqa: F401
210212
)
211213
from instana.instrumentation.tornado import (
212-
client as tornado_client, # noqa: F401
213214
server as tornado_server, # noqa: F401
214215
)
215216

216217
# Hooks
217-
from instana.hooks import hook_gunicorn, hook_uwsgi # noqa: F401
218+
from instana.hooks import (
219+
hook_gunicorn, # noqa: F401
220+
hook_uwsgi, # noqa: F401
221+
)
218222

219223

220224
if "INSTANA_DISABLE" not in os.environ:
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# (c) Copyright IBM Corp. 2025
2+
3+
from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Tuple, Type
4+
5+
from opentelemetry.semconv.trace import SpanAttributes
6+
7+
if TYPE_CHECKING:
8+
from botocore.auth import SigV4Auth
9+
from botocore.client import BaseClient
10+
11+
from instana.span.span import InstanaSpan
12+
13+
try:
14+
import json
15+
16+
import wrapt
17+
18+
from instana.log import logger
19+
from instana.propagators.format import Format
20+
from instana.singletons import tracer
21+
from instana.span.span import get_current_span
22+
from instana.util.traceutils import (
23+
extract_custom_headers,
24+
get_tracer_tuple,
25+
tracing_is_off,
26+
)
27+
28+
def lambda_inject_context(payload: Dict[str, Any], span: "InstanaSpan") -> None:
29+
"""
30+
When boto3 lambda client 'Invoke' is called, we want to inject the tracing context.
31+
boto3/botocore has specific requirements:
32+
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lambda.html#Lambda.Client.invoke
33+
"""
34+
try:
35+
invoke_payload = payload.get("Payload", {})
36+
37+
if not isinstance(invoke_payload, dict):
38+
invoke_payload = json.loads(invoke_payload)
39+
40+
tracer.inject(span.context, Format.HTTP_HEADERS, invoke_payload)
41+
payload["Payload"] = json.dumps(invoke_payload)
42+
except Exception:
43+
logger.debug("non-fatal lambda_inject_context: ", exc_info=True)
44+
45+
@wrapt.patch_function_wrapper("botocore.auth", "SigV4Auth.add_auth")
46+
def emit_add_auth_with_instana(
47+
wrapped: Callable[..., None],
48+
instance: "SigV4Auth",
49+
args: Tuple[object],
50+
kwargs: Dict[str, Any],
51+
) -> Callable[..., None]:
52+
current_span = get_current_span()
53+
if not tracing_is_off() and current_span and current_span.is_recording():
54+
extract_custom_headers(current_span, args[0].headers)
55+
return wrapped(*args, **kwargs)
56+
57+
@wrapt.patch_function_wrapper("botocore.client", "BaseClient._make_api_call")
58+
def make_api_call_with_instana(
59+
wrapped: Callable[..., Dict[str, Any]],
60+
instance: Type["BaseClient"],
61+
args: Sequence[Dict[str, Any]],
62+
kwargs: Dict[str, Any],
63+
) -> Dict[str, Any]:
64+
# If we're not tracing, just return
65+
if tracing_is_off():
66+
return wrapped(*args, **kwargs)
67+
68+
tracer, parent_span, _ = get_tracer_tuple()
69+
70+
parent_context = parent_span.get_span_context() if parent_span else None
71+
72+
try:
73+
with tracer.start_as_current_span(
74+
"boto3", span_context=parent_context
75+
) as span:
76+
try:
77+
operation = args[0]
78+
payload = args[1]
79+
80+
span.set_attribute("op", operation)
81+
span.set_attribute("ep", instance._endpoint.host)
82+
span.set_attribute("reg", instance._client_config.region_name)
83+
84+
span.set_attribute(
85+
SpanAttributes.HTTP_URL,
86+
instance._endpoint.host + ":443/" + args[0],
87+
)
88+
span.set_attribute(SpanAttributes.HTTP_METHOD, "POST")
89+
90+
# Don't collect payload for SecretsManager
91+
if not hasattr(instance, "get_secret_value"):
92+
span.set_attribute("payload", payload)
93+
94+
# Inject context when invoking lambdas
95+
if "lambda" in instance._endpoint.host and operation == "Invoke":
96+
lambda_inject_context(payload, span)
97+
98+
except Exception:
99+
logger.debug(
100+
"make_api_call_with_instana: collect error",
101+
exc_info=True,
102+
)
103+
104+
try:
105+
result = wrapped(*args, **kwargs)
106+
107+
if isinstance(result, dict):
108+
http_dict = result.get("ResponseMetadata")
109+
if isinstance(http_dict, dict):
110+
status = http_dict.get("HTTPStatusCode")
111+
if status is not None:
112+
span.set_attribute("http.status_code", status)
113+
headers = http_dict.get("HTTPHeaders")
114+
extract_custom_headers(span, headers)
115+
116+
return result
117+
except Exception as exc:
118+
span.mark_as_errored({"error": exc})
119+
raise
120+
except Exception:
121+
logger.debug("make_api_call_with_instana: collect error", exc_info=True)
122+
else:
123+
return wrapped(*args, **kwargs)
124+
125+
except ImportError:
126+
pass

src/instana/instrumentation/aws/lambda_inst.py

Lines changed: 75 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,89 +5,93 @@
55
Instrumentation for AWS Lambda functions
66
"""
77

8-
import sys
9-
import traceback
108
from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple
119

12-
import wrapt
13-
from opentelemetry.semconv.trace import SpanAttributes
14-
15-
from instana import get_aws_lambda_handler
16-
from instana.instrumentation.aws.triggers import enrich_lambda_span, get_context
17-
from instana.log import logger
18-
from instana.singletons import env_is_aws_lambda, get_agent, get_tracer
19-
from instana.util.ids import define_server_timing
20-
2110
if TYPE_CHECKING:
2211
from instana.agent.aws_lambda import AWSLambdaAgent
2312

13+
try:
14+
import sys
15+
import traceback
2416

25-
def lambda_handler_with_instana(
26-
wrapped: Callable[..., object],
27-
instance: object,
28-
args: Tuple[object, ...],
29-
kwargs: Dict[str, Any],
30-
) -> object:
31-
event = args[0]
32-
agent: "AWSLambdaAgent" = get_agent()
33-
tracer = get_tracer()
17+
import wrapt
18+
from opentelemetry.semconv.trace import SpanAttributes
3419

35-
agent.collector.collect_snapshot(*args)
36-
incoming_ctx = get_context(tracer, event)
20+
from instana import get_aws_lambda_handler
21+
from instana.instrumentation.aws.triggers import enrich_lambda_span, get_context
22+
from instana.log import logger
23+
from instana.singletons import env_is_aws_lambda, get_agent, get_tracer
24+
from instana.util.ids import define_server_timing
3725

38-
result = None
39-
with tracer.start_as_current_span(
40-
"aws.lambda.entry", span_context=incoming_ctx
41-
) as span:
42-
enrich_lambda_span(agent, span, *args)
43-
try:
44-
result = wrapped(*args, **kwargs)
26+
def lambda_handler_with_instana(
27+
wrapped: Callable[..., object],
28+
instance: object,
29+
args: Tuple[object, ...],
30+
kwargs: Dict[str, Any],
31+
) -> object:
32+
event = args[0]
33+
agent: "AWSLambdaAgent" = get_agent()
34+
tracer = get_tracer()
4535

46-
if isinstance(result, dict):
47-
server_timing_value = define_server_timing(span.context.trace_id)
48-
if "headers" in result:
49-
result["headers"]["Server-Timing"] = server_timing_value
50-
elif "multiValueHeaders" in result:
51-
result["multiValueHeaders"]["Server-Timing"] = [server_timing_value]
52-
if "statusCode" in result and result.get("statusCode"):
53-
status_code = int(result["statusCode"])
54-
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)
55-
if 500 <= status_code:
56-
span.record_exception(f"HTTP status {status_code}")
57-
except Exception as exc:
58-
logger.debug(f"AWS Lambda lambda_handler_with_instana error: {exc}")
59-
if span:
60-
exc = traceback.format_exc()
61-
span.record_exception(exc)
62-
raise
63-
finally:
64-
agent.collector.shutdown()
36+
agent.collector.collect_snapshot(*args)
37+
incoming_ctx = get_context(tracer, event)
38+
39+
result = None
40+
with tracer.start_as_current_span(
41+
"aws.lambda.entry", span_context=incoming_ctx
42+
) as span:
43+
enrich_lambda_span(agent, span, *args)
44+
try:
45+
result = wrapped(*args, **kwargs)
6546

66-
if agent.collector.started:
67-
agent.collector.shutdown()
68-
69-
return result
47+
if isinstance(result, dict):
48+
server_timing_value = define_server_timing(span.context.trace_id)
49+
if "headers" in result:
50+
result["headers"]["Server-Timing"] = server_timing_value
51+
elif "multiValueHeaders" in result:
52+
result["multiValueHeaders"]["Server-Timing"] = [
53+
server_timing_value
54+
]
55+
if "statusCode" in result and result.get("statusCode"):
56+
status_code = int(result["statusCode"])
57+
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)
58+
if 500 <= status_code:
59+
span.record_exception(f"HTTP status {status_code}")
60+
except Exception as exc:
61+
logger.debug(f"AWS Lambda lambda_handler_with_instana error: {exc}")
62+
if span:
63+
exc = traceback.format_exc()
64+
span.record_exception(exc)
65+
raise
66+
finally:
67+
agent.collector.shutdown()
7068

69+
if agent.collector.started:
70+
agent.collector.shutdown()
7171

72-
if env_is_aws_lambda:
73-
handler_module, handler_function = get_aws_lambda_handler()
72+
return result
7473

75-
if handler_module and handler_function:
76-
try:
77-
logger.debug(
78-
f"Instrumenting AWS Lambda handler ({handler_module}.{handler_function})"
79-
)
80-
sys.path.insert(0, "/var/runtime")
81-
sys.path.insert(0, "/var/task")
82-
wrapt.wrap_function_wrapper(
83-
handler_module, handler_function, lambda_handler_with_instana
84-
)
85-
except (ModuleNotFoundError, ImportError) as exc:
86-
logger.debug(f"AWS Lambda error: {exc}")
74+
if env_is_aws_lambda:
75+
handler_module, handler_function = get_aws_lambda_handler()
76+
77+
if handler_module and handler_function:
78+
try:
79+
logger.debug(
80+
f"Instrumenting AWS Lambda handler ({handler_module}.{handler_function})"
81+
)
82+
sys.path.insert(0, "/var/runtime")
83+
sys.path.insert(0, "/var/task")
84+
wrapt.wrap_function_wrapper(
85+
handler_module, handler_function, lambda_handler_with_instana
86+
)
87+
except (ModuleNotFoundError, ImportError) as exc:
88+
logger.debug(f"AWS Lambda error: {exc}")
89+
logger.warning(
90+
"Instana: Couldn't instrument AWS Lambda handler. Not monitoring."
91+
)
92+
else:
8793
logger.warning(
88-
"Instana: Couldn't instrument AWS Lambda handler. Not monitoring."
94+
"Instana: Couldn't determine AWS Lambda Handler. Not monitoring."
8995
)
90-
else:
91-
logger.warning(
92-
"Instana: Couldn't determine AWS Lambda Handler. Not monitoring."
93-
)
96+
except ImportError:
97+
pass

0 commit comments

Comments
 (0)