Skip to content

Commit 114e673

Browse files
committed
revert Python SDK related changes
1 parent 26b1cd3 commit 114e673

File tree

2 files changed

+50
-76
lines changed

2 files changed

+50
-76
lines changed

python/src/otel/otel_sdk/otel-instrument

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,18 +146,21 @@ else
146146
export OTEL_RESOURCE_ATTRIBUTES="$LAMBDA_RESOURCE_ATTRIBUTES,$OTEL_RESOURCE_ATTRIBUTES";
147147
fi
148148

149-
# Redirect Lambda to load the `otel_wrapper.py` wrapper script instead of the
150-
# user's handler. The wrapper initializes OpenTelemetry instrumentation at module
151-
# load time and then delegates to the original handler.
149+
150+
# - Uses the default `OTEL_PROPAGATORS` which is set to `tracecontext,baggage`
151+
152+
# - Use a wrapper because AWS Lambda's `python3 /var/runtime/bootstrap.py` will
153+
# use `imp.load_module` to load the function from the `_HANDLER` environment
154+
# variable. This RELOADS the module and REMOVES any instrumentation patching
155+
# done earlier. So we delay instrumentation until `bootstrap.py` imports
156+
# `otel_wrapper.py` at which we know the patching will be picked up.
152157
#
153-
# _HANDLER is a reserved environment variable used by AWS Lambda containing the
154-
# application handler path (e.g., "mymodule.handler"). We save it to ORIG_HANDLER
155-
# to allow the `otel_wrapper.py` wrapper script to retrieve it later so it
156-
# can delegate to the original handler.
157-
# see: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime
158+
# See more:
159+
# https://docs.python.org/3/library/imp.html#imp.load_module
160+
158161
export ORIG_HANDLER=$_HANDLER;
159162
export _HANDLER="otel_wrapper.lambda_handler";
160163

161-
# Handoff to the Lambda bootstrap process with _HANDLER set to
162-
# `otel_wrapper.lambda_handler`
163-
exec "$@"
164+
# - Call the upstream auto instrumentation script
165+
166+
exec python3 $LAMBDA_LAYER_PKGS_DIR/bin/opentelemetry-instrument "$@"

python/src/otel/otel_sdk/otel_wrapper.py

Lines changed: 36 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,83 +13,54 @@
1313
# limitations under the License.
1414

1515
"""
16-
OpenTelemetry Lambda Handler Wrapper
17-
18-
This module wraps the user's Lambda function to enable automatic OpenTelemetry
19-
instrumentation. It acts as wrapper script that instruments the Lambda function
20-
before loading the module containing the user's handler.
21-
22-
The instrumentation process works as follows:
23-
------------
24-
1. The `otel-instrument` shell script sets _HANDLER to point to this file's
25-
`lambda_handler`, saving the original handler path to ORIG_HANDLER.
26-
27-
2. When AWS Lambda imports this module, `auto_instrumentation.initialize()` runs
28-
immediately, instrumenting the application before any user code executes.
29-
30-
3. The module containing the user's handler is loaded by this script and the
31-
`lambda_handler` variable is bound to the user's original handler function,
32-
allowing Lambda invocations to be transparently forwarded to the original handler.
33-
34-
Details on why the `opentelemetry-instrument` CLI wrapper is insufficient:
35-
------------------------------------------------
36-
The `opentelemetry-instrument` CLI wrapper only instruments the initial Python process.
37-
AWS Lambda may spawn fresh Python processes for new invocations (e.g. as is the case with
38-
lambda managed instances), which would bypass CLI based instrumentation. By
39-
calling `auto_instrumentation.initialize()` at module import time, we ensure every
40-
Lambda execution context is instrumented.
41-
42-
Environment Variables
43-
---------------------
44-
ORIG_HANDLER : str
45-
The original Lambda handler path (e.g., "mymodule.handler"). Set by
46-
`otel-instrument` before this module is loaded.
47-
"""
16+
`otel_wrapper.py`
17+
18+
This file serves as a wrapper over the user's Lambda function.
19+
20+
Usage
21+
-----
22+
Patch the reserved `_HANDLER` Lambda environment variable to point to this
23+
file's `otel_wrapper.lambda_handler` property. Do this having saved the original
24+
`_HANDLER` in the `ORIG_HANDLER` environment variable. Doing this makes it so
25+
that **on import of this file, the handler is instrumented**.
26+
27+
Instrumenting any earlier will cause the instrumentation to be lost because the
28+
AWS Service uses `imp.load_module` to import the handler which RELOADS the
29+
module. This is why AwsLambdaInstrumentor cannot be instrumented with the
30+
`opentelemetry-instrument` script.
4831
32+
See more:
33+
https://docs.python.org/3/library/imp.html#imp.load_module
34+
35+
"""
4936

5037
import os
5138
from importlib import import_module
5239

53-
from opentelemetry.instrumentation import auto_instrumentation
54-
55-
# Initialize OpenTelemetry instrumentation immediately on module import.
56-
# This must happen before the user's handler module is loaded (below) to ensure
57-
# all library patches are applied before any user code runs.
58-
auto_instrumentation.initialize()
40+
from opentelemetry.instrumentation.aws_lambda import AwsLambdaInstrumentor
5941

6042

61-
def _get_orig_handler():
62-
"""
63-
Resolve and return the user's original Lambda handler function.
43+
def modify_module_name(module_name):
44+
"""Returns a valid modified module to get imported"""
45+
return ".".join(module_name.split("/"))
6446

65-
Reads the handler path from the ORIG_HANDLER environment variable,
66-
dynamically imports the handler's module and returns the handler
67-
function.
68-
"""
6947

70-
handler_path = os.environ.get("ORIG_HANDLER")
48+
class HandlerError(Exception):
49+
pass
7150

72-
if handler_path is None:
73-
raise RuntimeError(
74-
"ORIG_HANDLER is not defined."
75-
)
7651

77-
# Split "module/path.handler_name" into module path and function name.
78-
# The handler path uses the last "." as the separator between module and function.
79-
try:
80-
module_path, handler_name = handler_path.rsplit(".", 1)
81-
except ValueError as e:
82-
raise RuntimeError(
83-
f"Invalid ORIG_HANDLER format '{handler_path}': expected "
84-
f"'module.handler_name' or 'path/to/module.handler_name'. Error: {e}"
85-
)
52+
AwsLambdaInstrumentor().instrument()
8653

87-
# Convert path separators to Python module notation
88-
module_name = ".".join(module_path.split("/"))
54+
path = os.environ.get("ORIG_HANDLER")
8955

90-
handler_module = import_module(module_name)
91-
return getattr(handler_module, handler_name)
56+
if path is None:
57+
raise HandlerError("ORIG_HANDLER is not defined.")
9258

59+
try:
60+
(mod_name, handler_name) = path.rsplit(".", 1)
61+
except ValueError as e:
62+
raise HandlerError("Bad path '{}' for ORIG_HANDLER: {}".format(path, str(e)))
9363

94-
# Resolve to the user's handler at module load time.
95-
lambda_handler = _get_orig_handler()
64+
modified_mod_name = modify_module_name(mod_name)
65+
handler_module = import_module(modified_mod_name)
66+
lambda_handler = getattr(handler_module, handler_name)

0 commit comments

Comments
 (0)