diff --git a/examples/sam/dotnet/custom-error-catching/.gitignore b/examples/sam/dotnet/custom-error-catching/.gitignore new file mode 100644 index 00000000..dad88a7c --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/.gitignore @@ -0,0 +1,403 @@ + +# Created by https://www.gitignore.io/api/macos,linux,windows,sublimetext,dotsettings,visualstudio,visualstudiocode + +### DotSettings ### +*.DotSettings +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SublimeText ### +# cache files for sublime text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# workspace files are user-specific +*.sublime-workspace + +# project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using SublimeText +# *.sublime-project + +# sftp configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +.aws-sam/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +*.pubxml.user +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +### VisualStudio Patch ### +# By default, sensitive information, such as encrypted password +# should be stored in the .pubxml.user file. + + +# End of https://www.gitignore.io/api/macos,linux,windows,sublimetext,dotsettings,visualstudio,visualstudiocode diff --git a/examples/sam/dotnet/custom-error-catching/README.md b/examples/sam/dotnet/custom-error-catching/README.md new file mode 100644 index 00000000..664c8145 --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/README.md @@ -0,0 +1,200 @@ +# Instrumented Dotnet Lambda + +This is a "Hello, World" style Lambda function in .NET, instrumented +with the New Relic .NET Agent AWS Lambda layer. + +This example is both instructive, and a diagnostic tool: if you can +deploy this Lambda function, and see its events in NR One, you'll +know that all the telemetry plumbing is connected correctly. + +# Custom Error Monitoring With NewRelic.Agent.Api + +Lambdas have several conditions where they can never go through an invocation error and should always be handled cleanly. +This occurs with integrations into ALBs, API Gateways, batch-working SQS Queues(s), etc... + +ALB/API Gateway expects a response body containing a 500 code, notifying the user of an error. + +The Lambda should never completely error out when batch-working multiple messages from single or multiple queues. Instead it should return which items it successfully worked. + + The SQS auto-redrives anything not included in the response. If the Lambda errors out, SQS retrieves all messages regardless of how many were completed. + +These are a few examples where Lambda invocation errors will result in degraded performance and errors in event flows. + +All invocation errors cause a cold start. + +### Lambdas Execution Environment + +Lambda's has a very unique life cycle that is exampled in the following [documentation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html) + +TLDR: +* INIT Phase: +A cold start is when a lambda performs the init process of the life cycle, where it will initialize Extension, the RunTime, including the Java process that invokes your function, and finalize initializing the user's code. + +* Invoke Phase: +The lambda runtime invokes the user's code until the Shutdown Phase. +The Lambda is considered `hot` and can respond quickly. The INIT Phase does not happen again unless there is a failed invocation. + +* Shutdown Phase: +Reaping of the instance will occur when no invocations occur for 15 minutes. The creation of new instance to meet scaling demands will result in a cold start. The shutdown phase may not happen if the autoscaling and provision settings do not allow it. + +* Invoke With Error: +When invocation errors occur, the Lambda virtual environment resets the runtime and shuts down the extensions. The following invocation forces a Coldish Start with a new INIT Phase in an already provisioned instance. + +#### How Do Errors Affect The Life Cycle + +When errors occur during a Lambda's invocation, it always causes a `Invoke With Error`; the following invocation causes a new `INIT Phase` for that Lambda's virtual environment or provisioning. + +This will always result in a coldish start. The provisioned virtual environment will start the INIT Phase again. + +If enough errors occur for a single provisioned Lambda's, AWS will reap that provisioned virtual environment and create a new one for the next invocation. + +From experience, AWS monitors how often a Lambda is erroring out and will auto-descale them regardless of scaling or provisioning rules. + +New Relic's Lambda monitoring can monitor errors without the Lambda erroring out, ensuring performance and monitoring needs are met. + +### Custom Error Notification With New Relic's Nuget NewRelic.Agent.Api + +An instrumented Lambda, layer, layerless or containerized, can utilize NewRelic.Agent.Api Nuget, which can alter an invocation's instrumentation metadata. + +After installing `` + +This Nuget is not the same as `NewRelic.Agent` which is used to instrument Lambda's without New Relic's Lambda layer. `NewRelic.Agent.Api` is compatible with `NewRelic.Agent`! + +In the `function.cs` is the Lambda responding to an Api Gateway; the source really does not matter. To simulate an unhandled exception that bubbled all the way up, there is a try-catch that will handle it. +This try catch will respond with a 500 response to Api Gateway and use the following line: + +`NewRelic.Api.Agent.NewRelic.NoticeError(e);` + +The Lambda will exit successfully and avoid performance issues with the following invocation. However, this invocation is now marked as an error regardless of the Cloudwatch data. This invocation is tracked through New Relic's `Error Triage` and error metrics. + +Now, you can monitor Lambda using New Relic native dashboards, metrics, and alerts to inform reports about internal errors without incurring performance penalties for invocation errors. + +#### Custom Error/Invocation Tracking and the NewRelic.Agent.Api + +`NoticedError` is callable anytime during a lambda invocation and can be used deeper in the call stack to avoid the need of bubbling the exception to the top. + +Also, you can pass in custom exceptions to improve the feedback in `Error Triage` with the following: + +Just some sudo code deep inside a repository client + +```C# + + public async Task GetDadJokeFromQueueID(string id) { + + try { + var response = await client.getDadJokes(id); + + return new () { + id = id, + joke = response.joke, + success = true, + } + + } catch(e) { + `NewRelic.Api.Agent.NewRelic.NoticeError(e);` + } + + } +``` + +Tracking failed business logic is trackable without throwing an exception and still show show up on the `Error Triage` dashboard + +```C# + + public async Task ValidIdDoesNotExist (string id) { + + var response = await client.doesIdExist(id); + + if(response.existing == true) { // meaning it is an existing id + // an Exception is an object is optional + NewRelic.Api.Agent.NewRelic.NoticeError(new ExistingIDException($"ID: {id} is already existing"));` + return false; + } else { + return response.existing; + } + } +``` + +New Relic can respond to any number of problems that also do not incur an invocation error. This will help with less time sorting through logs for problems +but directly using New Relic Lambda's built-in `Dashboards` and `Error Triage`, reducing the frustration and creating custom alerts based on log messages. + +`Error Triage` and `Invocations` dashboards can filter with the Exception Class and the error message. + +```C# + public async Task ValidIdDoesNotExist (IdLookUp idLookup) { + + var response = await client.doesIdExist(idLookup.id); + + if(response.existing == true) { // meaning it is an existing id + // an Exception is an object is optional + var agent = NewRelic.Api.Agent.NewRelic.GetAgent(); + + var transaction = agent.CurrentTransaction; + + transaction.AddCustomAttribute("correlation-id", idLookup.correlationId); + + NewRelic.Api.Agent.NewRelic.NoticeError(new ExistingIDException($"ID: {idLookup.id} is already existing"));` + return false; + } else { + return response.existing; + } + } + +``` + +Now any invocation in `Error Triage` and `Invocations` can be found with a corrlection id or any additional metadata. + +This will greatly enhanced the usablity of the builtin New Relic's Lambda Dashboards which can be directly access through New Relic's Workloads and feed better monitoring into New Relic's Workflows! + +## Building and deploying + +### Prerequisites + +- The [AWS CLI v2](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) +- [Docker](https://docs.docker.com/get-docker/) +- The [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- [newrelic-lambda](https://github.com/newrelic/newrelic-lambda-cli#installation) CLI tool + +Make sure you've run the `newrelic-lambda install` command in your +AWS Region, and included the `--enable-license-key-secret` flag. + +### deploy script + +From a command prompt, in this directory, run + + ./deploy.sh + +where `` is your New Relic account ID, and `` +is your AWS Region, like "us-west-2". + +This will package and deploy the CloudFormation stack for this example +function. + +At this point, you can invoke the function. As provided, the example +function doesn't pay attention to its invocation event. If everything +has gone well, each invocation gets reported to New Relic, and its +telemetry appears in NR One. + +## Code Structure + +Now is also a good time to look at the structure of the example code. + +### template.yaml + +This function is deployed using a SAM template, which is a CloudFormation +template with some extra syntactic sugar for Lambda functions. In it, we +tell CloudFormation where to find lambda function code, what layers to use, and +what IAM policies to add to the Lambda function's execution role. We also set +environment variables that are available to the handler function. + +### Function.cs + +Lambda functions written in .NET are C# classes. The runtime loads them +just like any C# class, and then invokes the handler function for each +invocation event. + +The New Relic .NET Agent is used to instrument your AWS Lambda. In most cases, +the agent automatically instruments your AWS Lambda function handler. The layer +used in this example includes both the agent and the required New Relic Lambda +Extension. When instrumenting an AWS Lambda, the .NET Agent relies on the Lambda +Extension to send telemetry to New Relic. diff --git a/examples/sam/dotnet/custom-error-catching/deploy.ps1 b/examples/sam/dotnet/custom-error-catching/deploy.ps1 new file mode 100644 index 00000000..7128c907 --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/deploy.ps1 @@ -0,0 +1,28 @@ +param ( + [Parameter(Mandatory=$true)] + [string]$accountId, + + [Parameter(Mandatory=$true)] + [string]$region +) + +Write-Host "region set to $region" + +# Call SAM build +& sam build + +$bucket = "newrelic-example-$region-$accountId" + +# Create S3 bucket +& aws s3 mb --region $region "s3://$bucket" + +# Package SAM application +& sam package --region $region --s3-bucket $bucket --output-template-file packaged.yaml + +# Deploy CloudFormation stack +& aws cloudformation deploy ` + --region $region ` + --template-file packaged.yaml ` + --stack-name NewrelicExampleDotnet ` + --capabilities CAPABILITY_IAM ` + --parameter-overrides "NRAccountId=$accountId" \ No newline at end of file diff --git a/examples/sam/dotnet/custom-error-catching/deploy.sh b/examples/sam/dotnet/custom-error-catching/deploy.sh new file mode 100644 index 00000000..c896d8db --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/deploy.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +accountId=$1 + +region=$2 +echo "region set to ${region}" + +sam build + +bucket="newrelic-example-${region}-${accountId}" + +aws s3 mb --region "${region}" "s3://${bucket}" + +sam package --region "${region}" --s3-bucket "${bucket}" --output-template-file packaged.yaml + +aws cloudformation deploy \ + --region "${region}" \ + --template-file packaged.yaml \ + --stack-name NewrelicExampleDotnet \ + --capabilities CAPABILITY_IAM \ + --parameter-overrides "NRAccountId=${accountId}" diff --git a/examples/sam/dotnet/custom-error-catching/events/event.json b/examples/sam/dotnet/custom-error-catching/events/event.json new file mode 100644 index 00000000..4e3e8a60 --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/events/event.json @@ -0,0 +1,57 @@ +{ + "body": "steve", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.{dns_suffix}", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "apiKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890" + } +} \ No newline at end of file diff --git a/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/Function.cs b/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/Function.cs new file mode 100644 index 00000000..479fcad3 --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/Function.cs @@ -0,0 +1,37 @@ +using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.Core; +using System.Net; +using System.Text.Json; + +// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. +[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] + +namespace NewRelicExampleDotnet; + +public class Function +{ + + /// + /// A simple function that takes a string and does a ToUpper + /// + /// The event for the Lambda function handler to process. + /// The ILambdaContext that provides methods for logging and describing the Lambda environment. + /// + public async Task FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context) + { + try + { + throw new Error("There was a total meltdown. Did Homer not push the button?"); + } + catch (System.Exception) + { + NewRelic.Api.Agent.NewRelic.NoticeError(e); + return new APIGatewayProxyResponse + { + + StatusCode = (int)HttpStatusCode.InternalServerError, + Body = JsonSerializer.Serialize(DateTime.Now.ToString() + " Hello " + request.Body.ToUpper()) + }; + } + } +} \ No newline at end of file diff --git a/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/NewRelicExampleDotnet.csproj b/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/NewRelicExampleDotnet.csproj new file mode 100644 index 00000000..f6198441 --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/NewRelicExampleDotnet.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + true + Lambda + + true + + true + + + + + + + + + diff --git a/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/aws-lambda-tools-defaults.json b/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/aws-lambda-tools-defaults.json new file mode 100644 index 00000000..f7173ab8 --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/src/NewRelicExampleDotnet/aws-lambda-tools-defaults.json @@ -0,0 +1,20 @@ +{ + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile": "", + "region": "", + "configuration": "Release", + "framework": "net8.0", + "function-runtime": "dotnet8", + "function-architecture": "x86_64", + "function-memory-size": 512, + "function-timeout": 30, + "package-type": "Zip", + "function-name": "NewRelicExampleDotnet", + "function-description": "NewRelicExampleDotnet", + "function-handler": "NewRelicExampleDotnet::NewRelicExampleDotnet.Function::FunctionHandler" +} \ No newline at end of file diff --git a/examples/sam/dotnet/custom-error-catching/template.yaml b/examples/sam/dotnet/custom-error-catching/template.yaml new file mode 100644 index 00000000..5c0ce89a --- /dev/null +++ b/examples/sam/dotnet/custom-error-catching/template.yaml @@ -0,0 +1,47 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: And example of a simple instrumented .NET Lambda + +Parameters: + NRAccountId: + Type: String + Description: Your New Relic account ID; necessary for distributed tracing. + AllowedPattern: '[0-9]+' + +Resources: + NewRelicExample: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src/NewRelicExampleDotnet/ + Description: A simple Lambda, with New Relic telemetry + FunctionName: newrelic-example-dotnet + Handler: NewRelicExampleDotnet::NewRelicExampleDotnet.Function::FunctionHandler + Runtime: dotnet8 + PackageType: Zip + Timeout: 6 + Environment: + Variables: + CORECLR_ENABLE_PROFILING: 1 + CORECLR_PROFILER: "{36032161-FFC0-4B61-B559-F6C5D41BAE5A}" + CORECLR_NEWRELIC_HOME: /opt/lib/newrelic-dotnet-agent + CORECLR_PROFILER_PATH: /opt/lib/newrelic-dotnet-agent/libNewRelicProfiler.so + # NEW_RELIC_LOG_CONSOLE: true + # NEWRELIC_LOG_LEVEL: finest + # NEW_RELIC_APP_NAME: NewRelicExampleDotnet + NEW_RELIC_ACCOUNT_ID: !Sub ${NRAccountId} + # NEW_RELIC_EXTENSION_SEND_FUNCTION_LOGS: true + # NEW_RELIC_EXTENSION_LOG_LEVEL: DEBUG + Layers: + # This layer includes the .NET Agent and the New Relic Lambda Extension, a sidecar process that sends telemetry. + - !Sub arn:${AWS::Partition}:lambda:${AWS::Region}:451483290750:layer:NewRelicDotnet:4 + Policies: + # This policy allows the lambda to know the value of the New Relic license key. We need this so + # that we can send telemetry back to New Relic + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: !ImportValue NewRelicLicenseKeySecret-NewRelic-LicenseKeySecretARN + Logs: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: {"Fn::Join": ["", ["/aws/lambda/", {"Ref": "NewRelicExample"}]]} + # Lambda functions will auto-create their log group on first execution, but it retains logs forever, which can get expensive. + RetentionInDays: 7 diff --git a/newrelic-lambda-extension.sln b/newrelic-lambda-extension.sln new file mode 100644 index 00000000..6c1f7083 --- /dev/null +++ b/newrelic-lambda-extension.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{7F8A3C64-06CB-4043-9738-6597AC4551ED}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sam", "sam", "{EC0B92F9-16DB-4394-A8D4-3B7D3541CF36}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dotnet", "dotnet", "{1524380C-F39A-423F-A8B7-F8E2D2BC736E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A3AA52B5-C871-4D0E-8D78-90C4AEBBF366}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewRelicExampleDotnet", "examples\sam\dotnet\src\NewRelicExampleDotnet\NewRelicExampleDotnet.csproj", "{0EEFEC17-210C-4F37-A6EC-9F1924372776}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0EEFEC17-210C-4F37-A6EC-9F1924372776}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EEFEC17-210C-4F37-A6EC-9F1924372776}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EEFEC17-210C-4F37-A6EC-9F1924372776}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EEFEC17-210C-4F37-A6EC-9F1924372776}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EC0B92F9-16DB-4394-A8D4-3B7D3541CF36} = {7F8A3C64-06CB-4043-9738-6597AC4551ED} + {1524380C-F39A-423F-A8B7-F8E2D2BC736E} = {EC0B92F9-16DB-4394-A8D4-3B7D3541CF36} + {A3AA52B5-C871-4D0E-8D78-90C4AEBBF366} = {1524380C-F39A-423F-A8B7-F8E2D2BC736E} + {0EEFEC17-210C-4F37-A6EC-9F1924372776} = {A3AA52B5-C871-4D0E-8D78-90C4AEBBF366} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {557C3AF1-E6F1-4275-BB83-A3179EBAEDC0} + EndGlobalSection +EndGlobal