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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ newrelic-lambda layers install \
| `--nr-env-delimite` | No | Set `NR_ENV_DELIMITER` environment variable for your Lambda Function |
| `--nr-tags` | No | Set `NR_TAGS` environment variable for your Lambda Function |
| `--java_handler_method` or `-j` | No | For java runtimes only to specify an aws implementation method. Defaults to RequestHandler. Optional inputs are: handleRequest, handleStreamsRequest `--java_handler_method handleStreamsRequest`. |
| `--java-agent` | No | For Java runtimes only (`java17`, `java21`). Attaches the New Relic Java Agent layer (`NewRelicAgentJava`) instead of the default OpenTracing layer. Sets `AWS_LAMBDA_EXEC_WRAPPER=/opt/newrelic-java-handler` and leaves the function handler unchanged. Use `--java-agent true` to enable. |
| `--esm` | No | For Node.js functions using ES Modules (ESM), enable the specific ESM wrapper during installation (e.g., using the --esm flag). This sets the Lambda handler to `/opt/nodejs/node_modules/newrelic-esm-lambda-wrapper/index.handler`. |
| `--extension-logs-enabled` | No | Set `NEW_RELIC_EXTENSION_LOGS_ENABLED=true` to enable `[NR_EXT]` extension log output in CloudWatch. This is the default extension behaviour.|
| `--extension-logs-disabled` | No | Set `NEW_RELIC_EXTENSION_LOGS_ENABLED=false` to suppress `[NR_EXT]` extension log output in CloudWatch. |
Expand Down
7 changes: 7 additions & 0 deletions newrelic_lambda_cli/cli/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@ def register(group):
"log output in CloudWatch, reducing CloudWatch log volume without affecting "
"telemetry delivery to New Relic",
)
@click.option(
"--java-agent",
"java_agent",
default=False,
type=bool,
help="Java runtimes only - Use New Relic Java Agent layer (sets AWS_LAMBDA_EXEC_WRAPPER, keeps original handler)",
)
@click.pass_context
def install(ctx, **kwargs):
"""Install New Relic AWS Lambda Layers"""
Expand Down
90 changes: 67 additions & 23 deletions newrelic_lambda_cli/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@


NEW_RELIC_ENV_VARS = (
"AWS_LAMBDA_EXEC_WRAPPER",
"NEW_RELIC_ACCOUNT_ID",
"NEW_RELIC_EXTENSION_LOGS_ENABLED",
"NEW_RELIC_EXTENSION_SEND_EXTENSION_LOGS",
Expand Down Expand Up @@ -57,10 +58,20 @@ def layer_selection(
existing_layer_arn=None,
slim=False,
):
layer_options = [
layer["LatestMatchingVersion"]["LayerVersionArn"] for layer in available_layers
]

if slim:
for arn in layer_options:
if "-slim:" in arn:
success("Layer %s selected (slim)" % arn)
return arn

if upgrade and existing_layer_arn:
base_arn = existing_layer_arn.rsplit(":", 1)[0]

for i, layer in enumerate(available_layers):
for layer in available_layers:
candidate_arn = layer["LatestMatchingVersion"]["LayerVersionArn"]
candidate_base_arn = candidate_arn.rsplit(":", 1)[0]
if candidate_base_arn == base_arn:
Expand All @@ -69,14 +80,6 @@ def layer_selection(
if len(available_layers) == 1:
return available_layers[0]["LatestMatchingVersion"]["LayerVersionArn"]

layer_options = [
layer["LatestMatchingVersion"]["LayerVersionArn"] for layer in available_layers
]
if slim:
for arn in layer_options:
if "-slim:" in arn:
success("Layer %s selected (slim)" % arn)
return arn
if sys.stdout.isatty():
output = "\n".join(
[
Expand Down Expand Up @@ -126,9 +129,14 @@ def _add_new_relic(input, config, nr_license_key):
handler = config["Configuration"]["Handler"]
runtime_handler = utils.RUNTIME_CONFIG.get(runtime, {}).get("Handler")

use_java_agent = input.java_agent and "java" in runtime

if "java" in runtime:
postfix = input.java_handler_method or "handleRequest"
runtime_handler = runtime_handler + postfix
if use_java_agent:
runtime_handler = None
else:
postfix = input.java_handler_method or "handleRequest"
runtime_handler = runtime_handler + postfix
if "nodejs" in runtime:
prefix = (
"/opt/nodejs/node_modules/newrelic-esm-lambda-wrapper/index"
Expand Down Expand Up @@ -179,6 +187,20 @@ def _add_new_relic(input, config, nr_license_key):
# discover compatible layers...
available_layers = index(aws_region, runtime, architecture)

if "java" in runtime:
if use_java_agent:
available_layers = [
l
for l in available_layers
if l.get("LayerName", "").lower().startswith("newrelicagent")
]
else:
available_layers = [
l
for l in available_layers
if not l.get("LayerName", "").lower().startswith("newrelicagent")
]

if not available_layers:
failure(
"No Lambda layers published for %s (%s) runtime: %s"
Expand Down Expand Up @@ -217,6 +239,18 @@ def _add_new_relic(input, config, nr_license_key):
if runtime_handler:
update_kwargs["Handler"] = runtime_handler

if use_java_agent:
update_kwargs["Environment"]["Variables"][
"AWS_LAMBDA_EXEC_WRAPPER"
] = "/opt/newrelic-java-handler"
original_handler = update_kwargs["Environment"]["Variables"].pop(
"NEW_RELIC_LAMBDA_HANDLER", None
)
if original_handler:
update_kwargs["Handler"] = original_handler
elif "java" in runtime:
update_kwargs["Environment"]["Variables"].pop("AWS_LAMBDA_EXEC_WRAPPER", None)

# Update the account id
update_kwargs["Environment"]["Variables"]["NEW_RELIC_ACCOUNT_ID"] = str(
input.nr_account_id
Expand Down Expand Up @@ -508,19 +542,29 @@ def _remove_new_relic(input, config):

handler = config["Configuration"]["Handler"]

# For java runtimes we need to remove the method name before
# validating because method names are variable
if "java" in runtime:
handler = handler.split("::", 1)[0] + "::"
is_java_agent = (
"java" in runtime
and config["Configuration"]
.get("Environment", {})
.get("Variables", {})
.get("AWS_LAMBDA_EXEC_WRAPPER")
== "/opt/newrelic-java-handler"
)

# Detect non-New Relic handler and error if necessary.
if not utils.is_valid_handler(runtime, handler):
failure(
"New Relic installation (via layers) not auto-detected for the specified "
"function '%s'. Unrecognized handler in deployed function."
% config["Configuration"]["FunctionArn"]
)
return False
if not is_java_agent:
# For java runtimes we need to remove the method name before
# validating because method names are variable
if "java" in runtime:
handler = handler.split("::", 1)[0] + "::"

# Detect non-New Relic handler and error if necessary.
if not utils.is_valid_handler(runtime, handler):
failure(
"New Relic installation (via layers) not auto-detected for the specified "
"function '%s'. Unrecognized handler in deployed function."
% config["Configuration"]["FunctionArn"]
)
return False

env_handler = (
config["Configuration"]
Expand Down
1 change: 1 addition & 0 deletions newrelic_lambda_cli/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
"send_platform_logs",
"disable_platform_logs",
"java_handler_method",
"java_agent",
"esm",
"slim",
"extension_logs_enabled",
Expand Down
Loading
Loading