From 870ea92a74b10ff5cb1d5f0894e0a3dc7ea28cac Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Sun, 28 Nov 2021 02:59:09 +0000 Subject: [PATCH 1/8] add tracing attribute --- AWS.Lambda.Powertools.sln | 15 + examples/.gitignore | 454 +++++++++++++++ .../AWS.Lambda.PowerTools.Metrics/Metrics.cs | 3 + .../AWS.Lambda.PowerTools.Tracing.csproj | 11 +- .../CaptureHandler.cs | 19 - .../CaptureMethod.cs | 27 - .../AWS.Lambda.PowerTools.Tracing/ITracer.cs | 11 - .../ITracerOptions.cs | 27 - .../Internal/IXRayRecorder.cs | 13 + .../Internal/TracingAspectHandler.cs | 146 +++++ .../Internal/XRayRecorder.cs | 36 ++ .../InternalsVisibleTo.cs | 3 + .../AWS.Lambda.PowerTools.Tracing/Tracer.cs | 22 - .../TracingAttribute.cs | 46 ++ .../TracingCaptureMode.cs | 33 ++ .../AWS.Lambda.PowerTools.csproj | 4 + .../Aspects/BaseUniversalWrapperAspect.cs | 102 ++++ .../Aspects/MethodWrapperAspect.cs | 24 + .../Attributes/BaseMethodAspectAttribute.cs | 97 ++++ .../BaseUniversalWrapperAttribute.cs | 18 + .../Attributes/IBaseMethodAspectAttribute.cs | 13 + .../Attributes/MethodAspectAttribute.cs | 13 + .../AWS.Lambda.PowerTools/Core/Constants.cs | 18 + .../Core/IPowerToolsConfigurations.cs | 14 + .../Core/ISystemWrapper.cs | 7 + .../Core/PowerToolsConfigurations.cs | 45 ++ .../Core/SystemWrapper.cs | 17 + .../Events/AspectEventArgs.cs | 17 + .../InternalsVisibleTo.cs | 4 + .../AWS.Lambda.PowerTools.Tests.csproj | 21 + .../Core/PowerToolsConfigurationsTest.cs | 449 ++++++++++++++ ...AWS.Lambda.PowerTools.Tracing.Tests.csproj | 10 + .../TestFunction.cs | 37 -- .../TracingAttributeTest.cs | 548 ++++++++++++++++++ 34 files changed, 2180 insertions(+), 144 deletions(-) create mode 100644 examples/.gitignore delete mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/CaptureHandler.cs delete mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/CaptureMethod.cs delete mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/ITracer.cs delete mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/ITracerOptions.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/IXRayRecorder.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/XRayRecorder.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/InternalsVisibleTo.cs delete mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/Tracer.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools.Tracing/TracingCaptureMode.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Aspects/BaseUniversalWrapperAspect.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Aspects/MethodWrapperAspect.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Attributes/BaseMethodAspectAttribute.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Attributes/BaseUniversalWrapperAttribute.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Attributes/IBaseMethodAspectAttribute.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Attributes/MethodAspectAttribute.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Core/Constants.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Core/IPowerToolsConfigurations.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Core/ISystemWrapper.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Core/PowerToolsConfigurations.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Core/SystemWrapper.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/Events/AspectEventArgs.cs create mode 100644 libraries/src/AWS.Lambda.PowerTools/InternalsVisibleTo.cs create mode 100644 libraries/tests/AWS.Lambda.PowerTools.Tests/AWS.Lambda.PowerTools.Tests.csproj create mode 100644 libraries/tests/AWS.Lambda.PowerTools.Tests/Core/PowerToolsConfigurationsTest.cs delete mode 100644 libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TestFunction.cs create mode 100644 libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs diff --git a/AWS.Lambda.Powertools.sln b/AWS.Lambda.Powertools.sln index 448443ec6..493173d9a 100644 --- a/AWS.Lambda.Powertools.sln +++ b/AWS.Lambda.Powertools.sln @@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.PowerTools.Metri EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.PowerTools.Tracing.Tests", "libraries\tests\AWS.Lambda.PowerTools.Tracing.Tests\AWS.Lambda.PowerTools.Tracing.Tests.csproj", "{A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.PowerTools.Tests", "libraries\tests\AWS.Lambda.PowerTools.Tests\AWS.Lambda.PowerTools.Tests.csproj", "{4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -162,6 +164,18 @@ Global {A040AED5-BBB8-4BFA-B2A5-BBD82817B8A5}.Release|x86.Build.0 = Release|Any CPU {10E1D31D-AFE9-49EC-9041-920DF96AD74D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10E1D31D-AFE9-49EC-9041-920DF96AD74D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x64.ActiveCfg = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x64.Build.0 = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x86.ActiveCfg = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Debug|x86.Build.0 = Debug|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|Any CPU.Build.0 = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x64.ActiveCfg = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x64.Build.0 = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x86.ActiveCfg = Release|Any CPU + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {58F2F304-9E21-4902-8AD7-66C2022F3524} = {9E22D53B-4F30-4B82-98A2-9242B071D733} @@ -180,5 +194,6 @@ Global {0739912E-E3B6-4513-9E56-128749A78626} = {9E22D53B-4F30-4B82-98A2-9242B071D733} {5B3BBAF7-F8ED-4E2D-B161-0016942542B1} = {0739912E-E3B6-4513-9E56-128749A78626} {10E1D31D-AFE9-49EC-9041-920DF96AD74D} = {5B3BBAF7-F8ED-4E2D-B161-0016942542B1} + {4EC48E6A-45B5-4E25-ABBD-C23FE2BD6E1E} = {1CFF5568-8486-475F-81F6-06105C437528} EndGlobalSection EndGlobal diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 000000000..cb4fe3d70 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,454 @@ +# Created by https://www.toptal.com/developers/gitignore/api/dotnetcore,visualstudio,visualstudiocode,rider +# Edit at https://www.toptal.com/developers/gitignore?templates=dotnetcore,visualstudio,visualstudiocode,rider + +### DotnetCore ### +# .NET Core build folders +bin/ +obj/ + +# Common node modules locations +/node_modules +/wwwroot/node_modules + +### Rider ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +### 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 +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.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 + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*[.json, .xml, .info] + +# 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/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.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 +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/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 +*.appx +*.appxbundle +*.appxupload + +# 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 + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# 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 +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# 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/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,visualstudio,visualstudiocode,rider +.DS_Store diff --git a/libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs b/libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs index e738739e3..93000aeea 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs @@ -8,6 +8,9 @@ public class Metrics : IMetrics private MetricsContext _context; private bool _isColdStart = true; private bool _captureMetricsEvenIfEmpty; + + private static Metrics _instance; + public static Metrics Instance => _instance ??= new Metrics("dotnet-lambdapowertools", "lambda-example"); /// /// Creates Metrics with no namespace or service name defined - requires that they are defined after initialization diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/AWS.Lambda.PowerTools.Tracing.csproj b/libraries/src/AWS.Lambda.PowerTools.Tracing/AWS.Lambda.PowerTools.Tracing.csproj index 653721759..09cd1528b 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/AWS.Lambda.PowerTools.Tracing.csproj +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/AWS.Lambda.PowerTools.Tracing.csproj @@ -2,7 +2,16 @@ netcoreapp3.1 - Amazon.LambdaPowertools.Tracing + Amazon.Lambda.PowerTools.Tracing + + + + + + + + + diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/CaptureHandler.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/CaptureHandler.cs deleted file mode 100644 index edb2fc330..000000000 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/CaptureHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Amazon.LambdaPowertools.Tracing -{ - [AttributeUsage(AttributeTargets.Class, Inherited = false)] - public class CaptureHandler : Attribute - { - public CaptureHandler(ITracerOptions options) - { - // not implemented - } - - public CaptureHandler(string service = "undefined", bool disabled = false, bool autoPatch = true, - string patchmodules = null) - { - // not implemented - } - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/CaptureMethod.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/CaptureMethod.cs deleted file mode 100644 index 5dce472d7..000000000 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/CaptureMethod.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Amazon.LambdaPowertools.Tracing -{ - /// - /// Decorator to trace a function execution - /// Creates subsegment named after the method - /// Optionally supports both sync and async method - /// Adds function response as trace metadata using service attribute as metadata namespace - /// - - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - public class CaptureMethod : Attribute - { - public CaptureMethod(ITracerOptions options) - { - // not implemented - } - - public CaptureMethod(string service = "undefined", bool disabled = false, bool autoPatch = true, - string patchmodules = null) - { - // not implemented - } - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/ITracer.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/ITracer.cs deleted file mode 100644 index ac0c2e10b..000000000 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/ITracer.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace Amazon.LambdaPowertools.Tracing -{ - public interface ITracer - { - public void Patch(List modules); - public void PutAnnotation(string key, string value); - public void PutMetadata(string key, string value) ; - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/ITracerOptions.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/ITracerOptions.cs deleted file mode 100644 index a9886f30e..000000000 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/ITracerOptions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace Amazon.LambdaPowertools.Tracing -{ - public interface ITracerOptions - { - /// - /// Service: default "service_undefined", env:POWERTOOLS_SERVICE_NAME - /// - public string Service { get; set; } - - /// - /// Explicitly disable tracing via env var POWERTOOLS_TRACE_DISABLED="true" - /// - public bool Disabled { get; set; } - - /// - /// If true, it'll use X-Ray to patch all supported libraries at initialization - /// - public bool AutoPatch { get; set; } - - /// - /// Tuple of specific supported modules by X-Ray that should be patched - /// - public Tuple PatchModules { get; set; } - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/IXRayRecorder.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/IXRayRecorder.cs new file mode 100644 index 000000000..8ace7eef6 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/IXRayRecorder.cs @@ -0,0 +1,13 @@ +using System; + +namespace Amazon.Lambda.PowerTools.Tracing.Internal +{ + public interface IXRayRecorder + { + void BeginSubsegment(string name, DateTime? timestamp = null); + void SetNamespace(string value); + void AddAnnotation(string key, object value); + void AddMetadata(string nameSpace, string key, object value); + void EndSubsegment(); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs new file mode 100644 index 000000000..4034a6275 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs @@ -0,0 +1,146 @@ +using System; +using AWS.Lambda.PowerTools.Attributes; +using AWS.Lambda.PowerTools.Core; +using AWS.Lambda.PowerTools.Events; + +namespace Amazon.Lambda.PowerTools.Tracing.Internal +{ + internal class TracingAspectHandler : IBaseMethodAspectAttribute + { + private readonly string _segmentName; + private readonly string _namespace; + private readonly TracingCaptureMode _tracingCaptureMode; + private readonly IPowerToolsConfigurations _powerToolsConfigurations; + private readonly IXRayRecorder _xRayRecorder; + + private static bool _isColdStart = true; + private static bool _captureColdStart = true; + private bool _isColdStartCaptured; + + internal TracingAspectHandler + ( + string segmentName, + string @namespace, + TracingCaptureMode tracingCaptureMode, + IPowerToolsConfigurations powerToolsConfigurations, + IXRayRecorder xRayRecorder + ) + { + _segmentName = segmentName; + _namespace = @namespace; + _tracingCaptureMode = tracingCaptureMode; + _powerToolsConfigurations = powerToolsConfigurations; + _xRayRecorder = xRayRecorder; + } + + private string GetNamespace() + { + return !string.IsNullOrWhiteSpace(_namespace) ? _namespace : _powerToolsConfigurations.ServiceName; + } + + private bool CaptureResponse() + { + switch (_tracingCaptureMode) + { + case TracingCaptureMode.EnvironmentVariable: + return _powerToolsConfigurations.TracerCaptureResponse; + case TracingCaptureMode.Response: + case TracingCaptureMode.ResponseAndError: + return true; + case TracingCaptureMode.Error: + case TracingCaptureMode.Disabled: + default: + return false; + } + } + + private bool CaptureError() + { + switch (_tracingCaptureMode) + { + case TracingCaptureMode.EnvironmentVariable: + return _powerToolsConfigurations.TracerCaptureError; + case TracingCaptureMode.Error: + case TracingCaptureMode.ResponseAndError: + return true; + case TracingCaptureMode.Response: + case TracingCaptureMode.Disabled: + default: + return false; + } + } + + public void OnEntry(AspectEventArgs eventArgs) + { + Console.WriteLine($"OnEntry method {eventArgs.Name}"); + + var segmentName = !string.IsNullOrWhiteSpace(_segmentName) ? _segmentName : $"## {eventArgs.Name}"; + var nameSpace = GetNamespace(); + + Console.WriteLine($"BeginSubsegment method {eventArgs.Name}, SegmentName: {segmentName}, namespace: {nameSpace}"); + + _xRayRecorder.BeginSubsegment(segmentName); + _xRayRecorder.SetNamespace(nameSpace); + + if (_captureColdStart) + { + Console.WriteLine($"Capturing ColdStart for method: {eventArgs.Name}, ColdStart: {_isColdStart}"); + _xRayRecorder.AddAnnotation("ColdStart", _isColdStart); + _isColdStart = false; + _captureColdStart = false; + _isColdStartCaptured = true; + } + } + + public void OnSuccess(AspectEventArgs eventArgs, object result) + { + Console.WriteLine($"OnSuccess method {eventArgs.Name}"); + + if (CaptureResponse()) + { + var nameSpace = GetNamespace(); + Console.WriteLine($"Capturing Response for method: {eventArgs.Name}, namespace: {nameSpace}"); + _xRayRecorder.AddMetadata + ( + nameSpace: nameSpace, + key: $"{eventArgs.Name} response", + value: result + ); + } + } + + public T OnException(AspectEventArgs eventArgs, Exception exception) + { + Console.WriteLine($"OnException method {eventArgs.Name} --> {exception}"); + + if (CaptureError()) + { + var nameSpace = GetNamespace(); + Console.WriteLine($"Capturing Error for method: {eventArgs.Name}, namespace: {nameSpace}"); + + _xRayRecorder.AddMetadata + ( + nameSpace: nameSpace, + key: $"{eventArgs.Name} error", + value: exception + ); + } + + throw exception; + } + + public void OnExit(AspectEventArgs eventArgs) + { + Console.WriteLine($"OnExit method {eventArgs.Name}"); + + if (_isColdStartCaptured) + _captureColdStart = true; + + if (!_powerToolsConfigurations.IsSamLocal) + { + Console.WriteLine($"EndSubsegment method {eventArgs.Name}"); + _xRayRecorder.EndSubsegment(); + } + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/XRayRecorder.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/XRayRecorder.cs new file mode 100644 index 000000000..8778c043b --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/XRayRecorder.cs @@ -0,0 +1,36 @@ +using System; +using Amazon.XRay.Recorder.Core; + +namespace Amazon.Lambda.PowerTools.Tracing.Internal +{ + internal class XRayRecorder : IXRayRecorder + { + private static IXRayRecorder _instance; + public static IXRayRecorder Instance => _instance ??= new XRayRecorder(); + + public void BeginSubsegment(string name, DateTime? timestamp = null) + { + AWSXRayRecorder.Instance.BeginSubsegment(name, timestamp); + } + + public void SetNamespace(string value) + { + AWSXRayRecorder.Instance.SetNamespace(value); + } + + public void AddAnnotation(string key, object value) + { + AWSXRayRecorder.Instance.AddAnnotation(key, value); + } + + public void AddMetadata(string nameSpace, string key, object value) + { + AWSXRayRecorder.Instance.AddMetadata(nameSpace, key, value); + } + + public void EndSubsegment() + { + AWSXRayRecorder.Instance.EndSubsegment(); + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/InternalsVisibleTo.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/InternalsVisibleTo.cs new file mode 100644 index 000000000..04e7e0737 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("AWS.Lambda.PowerTools.Tracing.Tests")] \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/Tracer.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/Tracer.cs deleted file mode 100644 index 9daa356c7..000000000 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/Tracer.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace Amazon.LambdaPowertools.Tracing -{ - public class Tracer : ITracer - { - public void Patch(List modules) - { - throw new System.NotImplementedException(); - } - - public void PutAnnotation(string key, string value) - { - throw new System.NotImplementedException(); - } - - public void PutMetadata(string key, string value) - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs new file mode 100644 index 000000000..a563f1842 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs @@ -0,0 +1,46 @@ +using System; +using Amazon.Lambda.PowerTools.Tracing.Internal; +using AWS.Lambda.PowerTools.Attributes; +using AWS.Lambda.PowerTools.Core; +using AWS.Lambda.PowerTools.Events; + +namespace Amazon.Lambda.PowerTools.Tracing +{ + public class TracingAttribute : MethodAspectAttribute + { + public string SegmentName { get; set; } = ""; + public string Namespace { get; set; } = ""; + public TracingCaptureMode TracingCaptureMode { get; set; } = TracingCaptureMode.EnvironmentVariable; + + private IBaseMethodAspectAttribute _tracingHandler; + private IBaseMethodAspectAttribute TracingHandler => + _tracingHandler ??= new TracingAspectHandler + ( + SegmentName, + Namespace, + TracingCaptureMode, + PowerToolsConfigurations.Instance, + XRayRecorder.Instance + ); + + public override void OnEntry(AspectEventArgs eventArgs) + { + TracingHandler.OnEntry(eventArgs); + } + + public override void OnSuccess(AspectEventArgs eventArgs, object result) + { + TracingHandler.OnSuccess(eventArgs, result); + } + + public override T OnException(AspectEventArgs eventArgs, Exception exception) + { + return TracingHandler.OnException(eventArgs, exception); + } + + public override void OnExit(AspectEventArgs eventArgs) + { + TracingHandler.OnExit(eventArgs); + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingCaptureMode.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingCaptureMode.cs new file mode 100644 index 000000000..e64927bac --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingCaptureMode.cs @@ -0,0 +1,33 @@ +namespace Amazon.Lambda.PowerTools.Tracing +{ + public enum TracingCaptureMode + { + /** + * Enables annotation to capture only response. If this mode is explicitly overridden + * on {@link Tracing} annotation, it will override value of environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE + */ + Response, + /** + * Enabled annotation to capture only error from the method. If this mode is explicitly overridden + * on {@link Tracing} annotation, it will override value of environment variable POWERTOOLS_TRACER_CAPTURE_ERROR + */ + Error, + /** + * Enabled annotation to capture both response error from the method. If this mode is explicitly overridden + * on {@link Tracing} annotation, it will override value of environment variables POWERTOOLS_TRACER_CAPTURE_RESPONSE + * and POWERTOOLS_TRACER_CAPTURE_ERROR + */ + ResponseAndError, + /** + * Disables annotation to capture both response and error from the method. If this mode is explicitly overridden + * on {@link Tracing} annotation, it will override values of environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE + * and POWERTOOLS_TRACER_CAPTURE_ERROR + */ + Disabled, + /** + * Enables/Disables annotation to capture response and error from the method based on the value of + * environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE and POWERTOOLS_TRACER_CAPTURE_ERROR + */ + EnvironmentVariable + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/AWS.Lambda.PowerTools.csproj b/libraries/src/AWS.Lambda.PowerTools/AWS.Lambda.PowerTools.csproj index 3e2c1189f..d58610d3a 100644 --- a/libraries/src/AWS.Lambda.PowerTools/AWS.Lambda.PowerTools.csproj +++ b/libraries/src/AWS.Lambda.PowerTools/AWS.Lambda.PowerTools.csproj @@ -10,4 +10,8 @@ latest + + + + diff --git a/libraries/src/AWS.Lambda.PowerTools/Aspects/BaseUniversalWrapperAspect.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/BaseUniversalWrapperAspect.cs new file mode 100644 index 000000000..90f5ea65d --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Aspects/BaseUniversalWrapperAspect.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; +using AWS.Lambda.PowerTools.Attributes; +using AWS.Lambda.PowerTools.Events; + +namespace AWS.Lambda.PowerTools.Aspects +{ + public abstract class BaseUniversalWrapperAspect + { + private delegate object Handler(Func next, object[] args, AspectEventArgs eventArgs); + + private static readonly Dictionary _delegateCache = new Dictionary(); + + private static readonly MethodInfo _asyncGenericHandler = + typeof(BaseUniversalWrapperAttribute).GetMethod(nameof(BaseUniversalWrapperAttribute.WrapAsync), BindingFlags.NonPublic | BindingFlags.Instance); + + private static readonly MethodInfo _syncGenericHandler = + typeof(BaseUniversalWrapperAttribute).GetMethod(nameof(BaseUniversalWrapperAttribute.WrapSync), BindingFlags.NonPublic | BindingFlags.Instance); + + protected object BaseHandle( + object instance, + Type type, + MethodBase method, + Func target, + string name, + object[] args, + Type returnType, + Attribute[] triggers) + { + var eventArgs = new AspectEventArgs + { + Instance = instance, + Type = type, + Method = method, + Name = name, + Args = args, + ReturnType = returnType, + Triggers = triggers + }; + + var wrappers = triggers.OfType().ToArray(); + var handler = GetMethodHandler(method, returnType, wrappers); + return handler(target, args, eventArgs); + } + + private static Handler CreateMethodHandler(Type returnType, IEnumerable wrappers) + { + var targetParam = Expression.Parameter(typeof(Func), "orig"); + var eventArgsParam = Expression.Parameter(typeof(AspectEventArgs), "event"); + + MethodInfo wrapperMethod; + + if (typeof(Task).IsAssignableFrom(returnType)) + { + var taskType = returnType.IsConstructedGenericType ? returnType.GenericTypeArguments[0] : Type.GetType("System.Threading.Tasks.VoidTaskResult"); + returnType = typeof(Task<>).MakeGenericType(new[] { taskType }); + wrapperMethod = _asyncGenericHandler.MakeGenericMethod(new[] { taskType }); + } + else + { + if (returnType == typeof(void)) + returnType = typeof(object); + wrapperMethod = _syncGenericHandler.MakeGenericMethod(new[] { returnType }); + } + + var converArgs = Expression.Parameter(typeof(object[]), "args"); + var next = Expression.Lambda(Expression.Convert(Expression.Invoke(targetParam, converArgs), returnType), converArgs); + + foreach (var wrapper in wrappers) + { + var argsParam = Expression.Parameter(typeof(object[]), "args"); + next = Expression.Lambda(Expression.Call(Expression.Constant(wrapper), wrapperMethod, next, argsParam, eventArgsParam), argsParam); + } + + var orig_args = Expression.Parameter(typeof(object[]), "orig_args"); + var handler = Expression.Lambda(Expression.Convert(Expression.Invoke(next, orig_args), typeof(object)), targetParam, orig_args, eventArgsParam); + + var handlerCompiled = handler.Compile(); + + return handlerCompiled; + } + + private static Handler GetMethodHandler(MethodBase method, Type returnType, IEnumerable wrappers) + { + if (!_delegateCache.TryGetValue(method, out var handler)) + { + lock (method) + { + if (!_delegateCache.TryGetValue(method, out handler)) + { + _delegateCache[method] = handler = CreateMethodHandler(returnType, wrappers); + } + } + } + return handler; + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodWrapperAspect.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodWrapperAspect.cs new file mode 100644 index 000000000..6c2bf5246 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodWrapperAspect.cs @@ -0,0 +1,24 @@ +using System; +using System.Reflection; +using AspectInjector.Broker; + +namespace AWS.Lambda.PowerTools.Aspects +{ + [Aspect(Scope.Global)] + public class MethodWrapperAspect : BaseUniversalWrapperAspect + { + [Advice(Kind.Around, Targets = Target.Method)] + public object Handle( + [Argument(Source.Instance)] object instance, + [Argument(Source.Type)] Type type, + [Argument(Source.Metadata)] MethodBase method, + [Argument(Source.Target)] Func target, + [Argument(Source.Name)] string name, + [Argument(Source.Arguments)] object[] args, + [Argument(Source.ReturnType)] Type returnType, + [Argument(Source.Triggers)] Attribute[] triggers) + { + return BaseHandle(instance, type, method, target, name, args, returnType, triggers); + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseMethodAspectAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseMethodAspectAttribute.cs new file mode 100644 index 000000000..811b529e5 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseMethodAspectAttribute.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using AWS.Lambda.PowerTools.Events; + +namespace AWS.Lambda.PowerTools.Attributes +{ + public abstract class BaseMethodAspectAttribute : BaseUniversalWrapperAttribute, IBaseMethodAspectAttribute + { + protected internal sealed override T WrapSync(Func target, object[] args, AspectEventArgs eventArgs) + { + OnEntry(eventArgs); + try + { + var result = base.WrapSync(target, GetArguments(eventArgs, args), eventArgs); + OnSuccess(eventArgs, result); + return result; + } + catch (Exception exception) + { + return OnException(eventArgs, exception); + } + finally + { + OnExit(eventArgs); + } + } + + protected internal sealed override async Task WrapAsync(Func> target, object[] args, AspectEventArgs eventArgs) + { + OnEntry(eventArgs); + try + { + var result = await base.WrapSync(target, GetArguments(eventArgs, args), eventArgs); + OnSuccess(eventArgs, result); + return result; + } + catch (Exception exception) + { + return OnException(eventArgs, exception); + } + finally + { + OnExit(eventArgs); + } + } + + private object[] GetArguments(AspectEventArgs eventArgs, object[] args) + { + if (args is null || !args.Any()) + return args; + + var extraArg = GetExtraArgument(eventArgs); + if (extraArg is null) + return args; + + var parameters = ((MethodInfo) eventArgs.Method).GetParameters(); + if (parameters.Length != args.Length) + return args; + + var argType = extraArg.GetType(); + for (var i = 0; i < parameters.Length; i++) + { + if (parameters[i].ParameterType == argType && args[i] is null) + { + args[i] = extraArg; + break; + } + } + + return args; + } + + protected virtual object GetExtraArgument(AspectEventArgs eventArgs) + { + return null; + } + + public virtual void OnEntry(AspectEventArgs eventArgs) + { + } + + public virtual void OnSuccess(AspectEventArgs eventArgs, object result) + { + } + + public virtual T OnException(AspectEventArgs eventArgs, Exception exception) + { + throw exception; + } + + public virtual void OnExit(AspectEventArgs eventArgs) + { + } + } +} diff --git a/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseUniversalWrapperAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseUniversalWrapperAttribute.cs new file mode 100644 index 000000000..8173c8da6 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseUniversalWrapperAttribute.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; +using AWS.Lambda.PowerTools.Events; + +namespace AWS.Lambda.PowerTools.Attributes +{ + public abstract class BaseUniversalWrapperAttribute : Attribute + { + protected internal virtual T WrapSync(Func target, object[] args, AspectEventArgs eventArgs) + { + return target(args); + } + protected internal virtual Task WrapAsync(Func> target, object[] args, AspectEventArgs eventArgs) + { + return target(args); + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Attributes/IBaseMethodAspectAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Attributes/IBaseMethodAspectAttribute.cs new file mode 100644 index 000000000..30da02cd1 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Attributes/IBaseMethodAspectAttribute.cs @@ -0,0 +1,13 @@ +using System; +using AWS.Lambda.PowerTools.Events; + +namespace AWS.Lambda.PowerTools.Attributes +{ + public interface IBaseMethodAspectAttribute + { + void OnEntry(AspectEventArgs eventArgs); + void OnSuccess(AspectEventArgs eventArgs, object result); + T OnException(AspectEventArgs eventArgs, Exception exception); + void OnExit(AspectEventArgs eventArgs); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Attributes/MethodAspectAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Attributes/MethodAspectAttribute.cs new file mode 100644 index 000000000..ae11de98f --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Attributes/MethodAspectAttribute.cs @@ -0,0 +1,13 @@ + +using System; +using AspectInjector.Broker; +using AWS.Lambda.PowerTools.Aspects; + +namespace AWS.Lambda.PowerTools.Attributes +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + [Injection(typeof(MethodWrapperAspect), Inherited = true)] + public abstract class MethodAspectAttribute : BaseMethodAspectAttribute + { + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Core/Constants.cs b/libraries/src/AWS.Lambda.PowerTools/Core/Constants.cs new file mode 100644 index 000000000..ef6598982 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Core/Constants.cs @@ -0,0 +1,18 @@ +namespace AWS.Lambda.PowerTools.Core +{ + public static class Constants + { + public const string TRACER_CAPTURE_RESPONSE_ENV = "POWERTOOLS_TRACER_CAPTURE_RESPONSE"; + public const string TRACER_CAPTURE_ERROR_ENV = "POWERTOOLS_TRACER_CAPTURE_ERROR"; + public const string TRACER_DISABLED_ENV = "POWERTOOLS_TRACE_DISABLED"; + public const string LOGGER_LOG_SAMPLING_RATE = "POWERTOOLS_LOGGER_SAMPLE_RATE"; + public const string LOGGER_LOG_EVENT_ENV = "POWERTOOLS_LOGGER_LOG_EVENT"; + public const string LOGGER_LOG_DEDUPLICATION_ENV = "POWERTOOLS_LOG_DEDUPLICATION_DISABLED"; + public const string MIDDLEWARE_FACTORY_TRACE_ENV = "POWERTOOLS_TRACE_MIDDLEWARES"; + public const string METRICS_NAMESPACE_ENV = "POWERTOOLS_METRICS_NAMESPACE"; + public const string SAM_LOCAL_ENV = "AWS_SAM_LOCAL"; + public const string CHALICE_LOCAL_ENV = "AWS_CHALICE_CLI_MODE"; + public const string SERVICE_NAME_ENV = "POWERTOOLS_SERVICE_NAME"; + public const string XRAY_TRACE_ID_ENV = "_X_AMZN_TRACE_ID"; + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Core/IPowerToolsConfigurations.cs b/libraries/src/AWS.Lambda.PowerTools/Core/IPowerToolsConfigurations.cs new file mode 100644 index 000000000..b99dd2646 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Core/IPowerToolsConfigurations.cs @@ -0,0 +1,14 @@ +namespace AWS.Lambda.PowerTools.Core +{ + public interface IPowerToolsConfigurations + { + string ServiceName { get; } + bool TracerCaptureResponse{ get; } + bool TracerCaptureError{ get; } + bool IsSamLocal{ get; } + + string GetEnvironmentVariable(string variable); + string GetEnvironmentVariableOrDefault(string variable, string defaultValue); + bool GetEnvironmentVariableOrDefault(string variable, bool defaultValue); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Core/ISystemWrapper.cs b/libraries/src/AWS.Lambda.PowerTools/Core/ISystemWrapper.cs new file mode 100644 index 000000000..2a2ca2d50 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Core/ISystemWrapper.cs @@ -0,0 +1,7 @@ +namespace AWS.Lambda.PowerTools.Core +{ + public interface ISystemWrapper + { + string GetEnvironmentVariable(string variable); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Core/PowerToolsConfigurations.cs b/libraries/src/AWS.Lambda.PowerTools/Core/PowerToolsConfigurations.cs new file mode 100644 index 000000000..176832512 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Core/PowerToolsConfigurations.cs @@ -0,0 +1,45 @@ +namespace AWS.Lambda.PowerTools.Core +{ + public class PowerToolsConfigurations : IPowerToolsConfigurations + { + private static IPowerToolsConfigurations _instance; + public static IPowerToolsConfigurations Instance => _instance ??= new PowerToolsConfigurations(SystemWrapper.Instance); + + private readonly ISystemWrapper _systemWrapper; + + internal PowerToolsConfigurations(ISystemWrapper systemWrapper) + { + _systemWrapper = systemWrapper; + } + + public string GetEnvironmentVariable(string variable) + { + return _systemWrapper.GetEnvironmentVariable(variable); + } + + public string GetEnvironmentVariableOrDefault(string variable, string defaultValue) + { + var result = _systemWrapper.GetEnvironmentVariable(variable); + return string.IsNullOrWhiteSpace(result) ? defaultValue : result; + } + + public bool GetEnvironmentVariableOrDefault(string variable, bool defaultValue) + { + return bool.TryParse(_systemWrapper.GetEnvironmentVariable(variable), out var result) + ? result + : defaultValue; + } + + public string ServiceName => + GetEnvironmentVariableOrDefault(Constants.SERVICE_NAME_ENV, "service_undefined"); + + public bool TracerCaptureResponse => + GetEnvironmentVariableOrDefault(Constants.TRACER_CAPTURE_RESPONSE_ENV, true); + + public bool TracerCaptureError => + GetEnvironmentVariableOrDefault(Constants.TRACER_CAPTURE_ERROR_ENV, true); + + public bool IsSamLocal => + GetEnvironmentVariableOrDefault(Constants.SAM_LOCAL_ENV, false); + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Core/SystemWrapper.cs b/libraries/src/AWS.Lambda.PowerTools/Core/SystemWrapper.cs new file mode 100644 index 000000000..b68cd0980 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Core/SystemWrapper.cs @@ -0,0 +1,17 @@ +using System; + +namespace AWS.Lambda.PowerTools.Core +{ + public class SystemWrapper : ISystemWrapper + { + private static ISystemWrapper _instance; + public static ISystemWrapper Instance => _instance ??= new SystemWrapper(); + + private SystemWrapper() { } + + public string GetEnvironmentVariable(string variable) + { + return Environment.GetEnvironmentVariable(variable); + } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Events/AspectEventArgs.cs b/libraries/src/AWS.Lambda.PowerTools/Events/AspectEventArgs.cs new file mode 100644 index 000000000..a0f5f7e04 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/Events/AspectEventArgs.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace AWS.Lambda.PowerTools.Events +{ + public class AspectEventArgs : EventArgs + { + public object Instance { get; internal set; } + public Type Type { get; internal set; } + public MethodBase Method { get; internal set; } + public string Name { get; internal set; } + public IReadOnlyList Args { get; internal set; } + public Type ReturnType { get; internal set; } + public Attribute[] Triggers { get; internal set; } + } +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/InternalsVisibleTo.cs b/libraries/src/AWS.Lambda.PowerTools/InternalsVisibleTo.cs new file mode 100644 index 000000000..b7515cdb4 --- /dev/null +++ b/libraries/src/AWS.Lambda.PowerTools/InternalsVisibleTo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("AWS.Lambda.PowerTools.Tests")] +[assembly: InternalsVisibleTo("AWS.Lambda.PowerTools.Tracing.Tests")] \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.PowerTools.Tests/AWS.Lambda.PowerTools.Tests.csproj b/libraries/tests/AWS.Lambda.PowerTools.Tests/AWS.Lambda.PowerTools.Tests.csproj new file mode 100644 index 000000000..c5ab48dd9 --- /dev/null +++ b/libraries/tests/AWS.Lambda.PowerTools.Tests/AWS.Lambda.PowerTools.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/libraries/tests/AWS.Lambda.PowerTools.Tests/Core/PowerToolsConfigurationsTest.cs b/libraries/tests/AWS.Lambda.PowerTools.Tests/Core/PowerToolsConfigurationsTest.cs new file mode 100644 index 000000000..5f9bad9c7 --- /dev/null +++ b/libraries/tests/AWS.Lambda.PowerTools.Tests/Core/PowerToolsConfigurationsTest.cs @@ -0,0 +1,449 @@ +using System; +using AWS.Lambda.PowerTools.Core; +using Moq; +using Xunit; + +namespace AWS.Lambda.PowerTools.Tests.Core +{ + public class PowerToolsConfigurationsTest + { + #region GetEnvironmentVariable Tests + + [Fact] + public void GetEnvironmentVariableOrDefault_WhenEnvironmentVariableIsNull_ReturnsDefaultValueString() + { + // Arrange + var key = Guid.NewGuid().ToString(); + var defaultValue = Guid.NewGuid().ToString(); + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(key) + ).Returns(string.Empty); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.GetEnvironmentVariableOrDefault(key, defaultValue); + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == key) + ), Times.Once); + + Assert.Equal(result, defaultValue); + } + + [Fact] + public void GetEnvironmentVariableOrDefault_WhenEnvironmentVariableIsNull_ReturnsDefaultValueFalse() + { + // Arrange + var key = Guid.NewGuid().ToString(); + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(key) + ).Returns(string.Empty); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.GetEnvironmentVariableOrDefault(key, false); + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == key) + ), Times.Once); + + Assert.False(result); + } + + [Fact] + public void GetEnvironmentVariableOrDefault_WhenEnvironmentVariableIsNull_ReturnsDefaultValueTrue() + { + // Arrange + var key = Guid.NewGuid().ToString(); + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(key) + ).Returns(string.Empty); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.GetEnvironmentVariableOrDefault(key, true); + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == key) + ), Times.Once); + + Assert.True(result); + } + + [Fact] + public void GetEnvironmentVariableOrDefault_WhenEnvironmentVariableHasValue_ReturnsValueString() + { + // Arrange + var key = Guid.NewGuid().ToString(); + var defaultValue = Guid.NewGuid().ToString(); + var value = Guid.NewGuid().ToString(); + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(key) + ).Returns(value); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.GetEnvironmentVariableOrDefault(key, defaultValue); + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == key) + ), Times.Once); + + Assert.Equal(result, value); + } + + [Fact] + public void GetEnvironmentVariableOrDefault_WhenEnvironmentVariableHasValue_ReturnsValueTrue() + { + // Arrange + var key = Guid.NewGuid().ToString(); + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(key) + ).Returns("true"); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.GetEnvironmentVariableOrDefault(key, false); + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == key) + ), Times.Once); + + Assert.True(result); + } + + [Fact] + public void GetEnvironmentVariableOrDefault_WhenEnvironmentVariableHasValue_ReturnsValueFalse() + { + // Arrange + var key = Guid.NewGuid().ToString(); + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(key) + ).Returns("false"); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.GetEnvironmentVariableOrDefault(key, true); + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == key) + ), Times.Once); + + Assert.False(result); + } + + #endregion + + #region ServiceName Tests + + [Fact] + public void ServiceName_WhenEnvironmentIsNull_ReturnsDefaultValue() + { + // Arrange + var defaultServiceName = "service_undefined"; + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.SERVICE_NAME_ENV) + ).Returns(string.Empty); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.ServiceName; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.SERVICE_NAME_ENV) + ), Times.Once); + + Assert.Equal(result, defaultServiceName); + } + + [Fact] + public void ServiceName_WhenEnvironmentHasValue_ReturnsValue() + { + // Arrange + var serviceName = Guid.NewGuid().ToString(); + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.SERVICE_NAME_ENV) + ).Returns(serviceName); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.ServiceName; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.SERVICE_NAME_ENV) + ), Times.Once); + + Assert.Equal(result, serviceName); + } + + #endregion + + #region TracerCaptureResponse Tests + + [Fact] + public void TracerCaptureResponse_WhenEnvironmentIsNull_ReturnsDefaultValue() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.TRACER_CAPTURE_RESPONSE_ENV) + ).Returns(string.Empty); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.TracerCaptureResponse; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.TRACER_CAPTURE_RESPONSE_ENV) + ), Times.Once); + + Assert.True(result); + } + + [Fact] + public void TracerCaptureResponse_WhenEnvironmentHasValue_ReturnsValueFalse() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.TRACER_CAPTURE_RESPONSE_ENV) + ).Returns("false"); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.TracerCaptureResponse; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.TRACER_CAPTURE_RESPONSE_ENV) + ), Times.Once); + + Assert.False(result); + } + + [Fact] + public void TracerCaptureResponse_WhenEnvironmentHasValue_ReturnsValueTrue() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.TRACER_CAPTURE_RESPONSE_ENV) + ).Returns("true"); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.TracerCaptureResponse; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.TRACER_CAPTURE_RESPONSE_ENV) + ), Times.Once); + + Assert.True(result); + } + + #endregion + + #region TracerCaptureError Tests + + [Fact] + public void TracerCaptureError_WhenEnvironmentIsNull_ReturnsDefaultValue() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.TRACER_CAPTURE_ERROR_ENV) + ).Returns(string.Empty); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.TracerCaptureError; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.TRACER_CAPTURE_ERROR_ENV) + ), Times.Once); + + Assert.True(result); + } + + [Fact] + public void TracerCaptureError_WhenEnvironmentHasValue_ReturnsValueFalse() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.TRACER_CAPTURE_ERROR_ENV) + ).Returns("false"); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.TracerCaptureError; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.TRACER_CAPTURE_ERROR_ENV) + ), Times.Once); + + Assert.False(result); + } + + [Fact] + public void TracerCaptureError_WhenEnvironmentHasValue_ReturnsValueTrue() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.TRACER_CAPTURE_ERROR_ENV) + ).Returns("true"); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.TracerCaptureError; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.TRACER_CAPTURE_ERROR_ENV) + ), Times.Once); + + Assert.True(result); + } + + #endregion + + #region TracerCaptureError Tests + + [Fact] + public void IsSamLocal_WhenEnvironmentIsNull_ReturnsDefaultValue() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.SAM_LOCAL_ENV) + ).Returns(string.Empty); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.IsSamLocal; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.SAM_LOCAL_ENV) + ), Times.Once); + + Assert.False(result); + } + + [Fact] + public void IsSamLocal_WhenEnvironmentHasValue_ReturnsValueFalse() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.SAM_LOCAL_ENV) + ).Returns("false"); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.IsSamLocal; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.SAM_LOCAL_ENV) + ), Times.Once); + + Assert.False(result); + } + + [Fact] + public void IsSamLocal_WhenEnvironmentHasValue_ReturnsValueTrue() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.SAM_LOCAL_ENV) + ).Returns("true"); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.IsSamLocal; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.SAM_LOCAL_ENV) + ), Times.Once); + + Assert.True(result); + } + + #endregion + } +} \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/AWS.Lambda.PowerTools.Tracing.Tests.csproj b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/AWS.Lambda.PowerTools.Tracing.Tests.csproj index a2c691f33..ad4ca18e6 100644 --- a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/AWS.Lambda.PowerTools.Tracing.Tests.csproj +++ b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/AWS.Lambda.PowerTools.Tracing.Tests.csproj @@ -8,4 +8,14 @@ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TestFunction.cs b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TestFunction.cs deleted file mode 100644 index 6c1042df3..000000000 --- a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TestFunction.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks; -using Amazon.LambdaPowertools.Tracing; - -namespace AWS.Lambda.PowerTools.Tracing.Tests -{ - - public class TestFunction - { - private readonly ITracer _tracer; - - public TestFunction() - { - _tracer = new Tracer(); - } - - [CaptureMethod(service: "MyService", disabled:false, autoPatch: false, patchmodules: null)] - private string ConfirmBooking(string bookingId) - { - var response = AddConfirmation(bookingId); - - _tracer.PutMetadata("BookingConfirmation", response["requestId"]); - _tracer.PutMetadata("Booking confirmation", "{}"); - _tracer.PutMetadata("", ""); - - - return response.ToString(); - } - - private Dictionary AddConfirmation(string bookingId) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs new file mode 100644 index 000000000..5b23a646b --- /dev/null +++ b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs @@ -0,0 +1,548 @@ +using System; +using System.Linq; +using Amazon.Lambda.PowerTools.Tracing; +using Amazon.Lambda.PowerTools.Tracing.Internal; +using AWS.Lambda.PowerTools.Core; +using AWS.Lambda.PowerTools.Events; +using Moq; +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] + +namespace AWS.Lambda.PowerTools.Tracing.Tests +{ + [Collection("Sequential")] + public class TracingAttributeColdStartTest + { + [Fact] + public void OnEntry_WhenFirstCall_CapturesColdStart() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + + var handler = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnEntry(eventArgs); + + // Assert + recorder.Verify(v => + v.AddAnnotation( + It.Is(i => i == "ColdStart"), + It.Is(i => i) + ), Times.Once); + } + } + + [Collection("Sequential")] + public class TracingAttributeTest + { + #region OnEntry Tests + + [Fact] + public void OnEntry_WhenSegmentNameIsNull_BeginSubsegmentWithMethodName() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + + var handler = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnEntry(eventArgs); + + // Assert + recorder.Verify(v => + v.BeginSubsegment( + It.Is(i => i == $"## {methodName}"), + It.IsAny() + ), Times.Once); + } + + [Fact] + public void OnEntry_WhenSegmentNameHasValue_BeginSubsegmentWithValue() + { + // Arrange + var segmentName = Guid.NewGuid().ToString(); + var methodName = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + + var handler = new TracingAspectHandler(segmentName, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnEntry(eventArgs); + + // Assert + recorder.Verify(v => + v.BeginSubsegment( + It.Is(i => i == segmentName), + It.IsAny() + ), Times.Once); + } + + [Fact] + public void OnEntry_WhenNamespaceIsNull_SetNamespaceWithServiceName() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var serviceName = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.ServiceName + ).Returns(serviceName); + var recorder = new Mock(); + + var handler = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnEntry(eventArgs); + + // Assert + recorder.Verify(v => + v.SetNamespace( + It.Is(i => i == serviceName) + ), Times.Once); + } + + [Fact] + public void OnEntry_WhenNamespaceHasValue_SetNamespaceWithValue() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnEntry(eventArgs); + + // Assert + recorder.Verify(v => + v.SetNamespace( + It.Is(i => i == nameSpace) + ), Times.Once); + } + + #endregion + + #region OnSuccess Tests + + [Fact] + public void OnSuccess_WhenTracerCaptureResponseEnvironmentVariableIsTrue_CapturesResponse() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.TracerCaptureResponse + ).Returns(true); + var recorder = new Mock(); + var results = new[] {"A", "B"}; + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnSuccess(eventArgs, results); + + // Assert + recorder.Verify(v => + v.AddMetadata( + It.Is(i => i == nameSpace), + It.Is(i => i == $"{methodName} response"), + It.Is(i => + i.First() == results.First() && + i.Last() == results.Last() + ) + ), Times.Once); + } + + [Fact] + public void OnSuccess_WhenTracerCaptureResponseEnvironmentVariableIsFalse_DoesNotCaptureResponse() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.TracerCaptureResponse + ).Returns(false); + var recorder = new Mock(); + var results = new[] {"A", "B"}; + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnSuccess(eventArgs, results); + + // Assert + recorder.Verify(v => + v.AddMetadata( + It.IsAny(), + It.IsAny(), + It.IsAny() + ), Times.Never); + } + + [Fact] + public void OnSuccess_WhenTracerCaptureModeIsResponse_CapturesResponse() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + var results = new[] {"A", "B"}; + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.Response, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnSuccess(eventArgs, results); + + // Assert + recorder.Verify(v => + v.AddMetadata( + It.Is(i => i == nameSpace), + It.Is(i => i == $"{methodName} response"), + It.Is(i => + i.First() == results.First() && + i.Last() == results.Last() + ) + ), Times.Once); + } + + [Fact] + public void OnSuccess_WhenTracerCaptureModeIsResponseAndError_CapturesResponse() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + var results = new[] {"A", "B"}; + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.ResponseAndError, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnSuccess(eventArgs, results); + + // Assert + recorder.Verify(v => + v.AddMetadata( + It.Is(i => i == nameSpace), + It.Is(i => i == $"{methodName} response"), + It.Is(i => + i.First() == results.First() && + i.Last() == results.Last() + ) + ), Times.Once); + } + + [Fact] + public void OnSuccess_WhenTracerCaptureModeIsError_DoesNotCaptureResponse() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + var results = new[] {"A", "B"}; + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.Error, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnSuccess(eventArgs, results); + + // Assert + recorder.Verify(v => + v.AddMetadata( + It.IsAny(), + It.IsAny(), + It.IsAny() + ), Times.Never); + } + + [Fact] + public void OnSuccess_WhenTracerCaptureModeIsDisabled_DoesNotCaptureResponse() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + var results = new[] {"A", "B"}; + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.Disabled, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnSuccess(eventArgs, results); + + // Assert + recorder.Verify(v => + v.AddMetadata( + It.IsAny(), + It.IsAny(), + It.IsAny() + ), Times.Never); + } + + #endregion + + #region OnException Tests + + [Fact] + public void OnException_WhenTracerCaptureErrorEnvironmentVariableIsTrue_CapturesError() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.TracerCaptureError + ).Returns(true); + var recorder = new Mock(); + var exception = new Exception("Test Exception"); + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + object Act() => handler.OnException(eventArgs, exception); + + // Assert + Assert.Throws(Act); + recorder.Verify(v => + v.AddMetadata( + It.Is(i => i == nameSpace), + It.Is(i => i == $"{methodName} error"), + It.Is(i => i == exception + ) + ), Times.Once); + } + + [Fact] + public void OnException_WhenTracerCaptureErrorEnvironmentVariableIsTrueFalse_DoesNotCaptureError() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.TracerCaptureError + ).Returns(false); + var recorder = new Mock(); + var exception = new Exception("Test Exception"); + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + object Act() => handler.OnException(eventArgs, exception); + + // Assert + Assert.Throws(Act); + recorder.Verify(v => + v.AddMetadata( + It.IsAny(), + It.IsAny(), + It.IsAny() + ), Times.Never); + } + + [Fact] + public void OnException_WhenTracerCaptureModeIsError_CapturesError() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + var exception = new Exception("Test Exception"); + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.Error, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + object Act() => handler.OnException(eventArgs, exception); + + // Assert + Assert.Throws(Act); + recorder.Verify(v => + v.AddMetadata( + It.Is(i => i == nameSpace), + It.Is(i => i == $"{methodName} error"), + It.Is(i => i == exception + ) + ), Times.Once); + } + + [Fact] + public void OnException_WhenTracerCaptureModeIsResponseAndError_CapturesError() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + var recorder = new Mock(); + var exception = new Exception("Test Exception"); + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.ResponseAndError, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + object Act() => handler.OnException(eventArgs, exception); + + // Assert + Assert.Throws(Act); + recorder.Verify(v => + v.AddMetadata( + It.Is(i => i == nameSpace), + It.Is(i => i == $"{methodName} error"), + It.Is(i => i == exception + ) + ), Times.Once); + } + + [Fact] + public void OnException_WhenTracerCaptureModeIsResponse_DoesNotCaptureError() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.TracerCaptureError + ).Returns(false); + var recorder = new Mock(); + var exception = new Exception("Test Exception"); + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.Response, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + object Act() => handler.OnException(eventArgs, exception); + + // Assert + Assert.Throws(Act); + recorder.Verify(v => + v.AddMetadata( + It.IsAny(), + It.IsAny(), + It.IsAny() + ), Times.Never); + } + + [Fact] + public void OnException_WhenTracerCaptureModeIsDisabled_DoesNotCaptureError() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var nameSpace = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.TracerCaptureError + ).Returns(false); + var recorder = new Mock(); + var exception = new Exception("Test Exception"); + + var handler = new TracingAspectHandler(null, nameSpace, TracingCaptureMode.Disabled, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + object Act() => handler.OnException(eventArgs, exception); + + // Assert + Assert.Throws(Act); + recorder.Verify(v => + v.AddMetadata( + It.IsAny(), + It.IsAny(), + It.IsAny() + ), Times.Never); + } + + #endregion + + #region OnExit Tests + + [Fact] + public void OnExit_WhenIsNotSamLocal_EndSubsegment() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.IsSamLocal + ).Returns(false); + var recorder = new Mock(); + + var handler = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnExit(eventArgs); + + // Assert + recorder.Verify(v => v.EndSubsegment(), Times.Once); + } + + [Fact] + public void OnExit_WhenIsSamLocal_DoesNotEndSubsegment() + { + // Arrange + var methodName = Guid.NewGuid().ToString(); + var configurations = new Mock(); + configurations.Setup(c => + c.IsSamLocal + ).Returns(true); + var recorder = new Mock(); + + var handler = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; + + // Act + handler.OnExit(eventArgs); + + // Assert + recorder.Verify(v => v.EndSubsegment(), Times.Never); + } + + #endregion + } +} \ No newline at end of file From 1bf833924ff497456ddd2435973d8fc1adad4817 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Mon, 29 Nov 2021 10:51:29 +0000 Subject: [PATCH 2/8] refactor tracing attribute --- .../Internal/TracingAspectHandler.cs | 5 +- .../TracingAttribute.cs | 7 ++- .../{Events => Aspects}/AspectEventArgs.cs | 2 +- .../IMethodAspectAttribute.cs} | 5 +- .../MethodAspectAttribute.cs} | 8 +-- .../Aspects/MethodWrapperAspect.cs | 24 --------- ...perAspect.cs => UniversalWrapperAspect.cs} | 51 +++++++++--------- .../UniversalWrapperAttribute.cs} | 5 +- .../Attributes/MethodAspectAttribute.cs | 13 ----- .../TracingAttributeTest.cs | 54 ++++++++++++++++--- 10 files changed, 88 insertions(+), 86 deletions(-) rename libraries/src/AWS.Lambda.PowerTools/{Events => Aspects}/AspectEventArgs.cs (92%) rename libraries/src/AWS.Lambda.PowerTools/{Attributes/IBaseMethodAspectAttribute.cs => Aspects/IMethodAspectAttribute.cs} (67%) rename libraries/src/AWS.Lambda.PowerTools/{Attributes/BaseMethodAspectAttribute.cs => Aspects/MethodAspectAttribute.cs} (89%) delete mode 100644 libraries/src/AWS.Lambda.PowerTools/Aspects/MethodWrapperAspect.cs rename libraries/src/AWS.Lambda.PowerTools/Aspects/{BaseUniversalWrapperAspect.cs => UniversalWrapperAspect.cs} (74%) rename libraries/src/AWS.Lambda.PowerTools/{Attributes/BaseUniversalWrapperAttribute.cs => Aspects/UniversalWrapperAttribute.cs} (74%) delete mode 100644 libraries/src/AWS.Lambda.PowerTools/Attributes/MethodAspectAttribute.cs diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs index 4034a6275..3ffaecb3d 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs @@ -1,11 +1,10 @@ using System; -using AWS.Lambda.PowerTools.Attributes; +using AWS.Lambda.PowerTools.Aspects; using AWS.Lambda.PowerTools.Core; -using AWS.Lambda.PowerTools.Events; namespace Amazon.Lambda.PowerTools.Tracing.Internal { - internal class TracingAspectHandler : IBaseMethodAspectAttribute + internal class TracingAspectHandler : IMethodAspectAttribute { private readonly string _segmentName; private readonly string _namespace; diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs index a563f1842..922ec945b 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs @@ -1,8 +1,7 @@ using System; using Amazon.Lambda.PowerTools.Tracing.Internal; -using AWS.Lambda.PowerTools.Attributes; +using AWS.Lambda.PowerTools.Aspects; using AWS.Lambda.PowerTools.Core; -using AWS.Lambda.PowerTools.Events; namespace Amazon.Lambda.PowerTools.Tracing { @@ -12,8 +11,8 @@ public class TracingAttribute : MethodAspectAttribute public string Namespace { get; set; } = ""; public TracingCaptureMode TracingCaptureMode { get; set; } = TracingCaptureMode.EnvironmentVariable; - private IBaseMethodAspectAttribute _tracingHandler; - private IBaseMethodAspectAttribute TracingHandler => + private IMethodAspectAttribute _tracingHandler; + private IMethodAspectAttribute TracingHandler => _tracingHandler ??= new TracingAspectHandler ( SegmentName, diff --git a/libraries/src/AWS.Lambda.PowerTools/Events/AspectEventArgs.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/AspectEventArgs.cs similarity index 92% rename from libraries/src/AWS.Lambda.PowerTools/Events/AspectEventArgs.cs rename to libraries/src/AWS.Lambda.PowerTools/Aspects/AspectEventArgs.cs index a0f5f7e04..3f627a6fa 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Events/AspectEventArgs.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Aspects/AspectEventArgs.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Reflection; -namespace AWS.Lambda.PowerTools.Events +namespace AWS.Lambda.PowerTools.Aspects { public class AspectEventArgs : EventArgs { diff --git a/libraries/src/AWS.Lambda.PowerTools/Attributes/IBaseMethodAspectAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/IMethodAspectAttribute.cs similarity index 67% rename from libraries/src/AWS.Lambda.PowerTools/Attributes/IBaseMethodAspectAttribute.cs rename to libraries/src/AWS.Lambda.PowerTools/Aspects/IMethodAspectAttribute.cs index 30da02cd1..40f5d3202 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Attributes/IBaseMethodAspectAttribute.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Aspects/IMethodAspectAttribute.cs @@ -1,9 +1,8 @@ using System; -using AWS.Lambda.PowerTools.Events; -namespace AWS.Lambda.PowerTools.Attributes +namespace AWS.Lambda.PowerTools.Aspects { - public interface IBaseMethodAspectAttribute + public interface IMethodAspectAttribute { void OnEntry(AspectEventArgs eventArgs); void OnSuccess(AspectEventArgs eventArgs, object result); diff --git a/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseMethodAspectAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodAspectAttribute.cs similarity index 89% rename from libraries/src/AWS.Lambda.PowerTools/Attributes/BaseMethodAspectAttribute.cs rename to libraries/src/AWS.Lambda.PowerTools/Aspects/MethodAspectAttribute.cs index 811b529e5..bf245e2f2 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseMethodAspectAttribute.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodAspectAttribute.cs @@ -2,11 +2,13 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using AWS.Lambda.PowerTools.Events; +using AspectInjector.Broker; -namespace AWS.Lambda.PowerTools.Attributes +namespace AWS.Lambda.PowerTools.Aspects { - public abstract class BaseMethodAspectAttribute : BaseUniversalWrapperAttribute, IBaseMethodAspectAttribute + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] + [Injection(typeof(UniversalWrapperAspect), Inherited = true)] + public abstract class MethodAspectAttribute : UniversalWrapperAttribute, IMethodAspectAttribute { protected internal sealed override T WrapSync(Func target, object[] args, AspectEventArgs eventArgs) { diff --git a/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodWrapperAspect.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodWrapperAspect.cs deleted file mode 100644 index 6c2bf5246..000000000 --- a/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodWrapperAspect.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Reflection; -using AspectInjector.Broker; - -namespace AWS.Lambda.PowerTools.Aspects -{ - [Aspect(Scope.Global)] - public class MethodWrapperAspect : BaseUniversalWrapperAspect - { - [Advice(Kind.Around, Targets = Target.Method)] - public object Handle( - [Argument(Source.Instance)] object instance, - [Argument(Source.Type)] Type type, - [Argument(Source.Metadata)] MethodBase method, - [Argument(Source.Target)] Func target, - [Argument(Source.Name)] string name, - [Argument(Source.Arguments)] object[] args, - [Argument(Source.ReturnType)] Type returnType, - [Argument(Source.Triggers)] Attribute[] triggers) - { - return BaseHandle(instance, type, method, target, name, args, returnType, triggers); - } - } -} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.PowerTools/Aspects/BaseUniversalWrapperAspect.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/UniversalWrapperAspect.cs similarity index 74% rename from libraries/src/AWS.Lambda.PowerTools/Aspects/BaseUniversalWrapperAspect.cs rename to libraries/src/AWS.Lambda.PowerTools/Aspects/UniversalWrapperAspect.cs index 90f5ea65d..958a4099c 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Aspects/BaseUniversalWrapperAspect.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Aspects/UniversalWrapperAspect.cs @@ -4,32 +4,23 @@ using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; -using AWS.Lambda.PowerTools.Attributes; -using AWS.Lambda.PowerTools.Events; +using AspectInjector.Broker; namespace AWS.Lambda.PowerTools.Aspects { - public abstract class BaseUniversalWrapperAspect + [Aspect(Scope.Global)] + public class UniversalWrapperAspect { - private delegate object Handler(Func next, object[] args, AspectEventArgs eventArgs); - - private static readonly Dictionary _delegateCache = new Dictionary(); - - private static readonly MethodInfo _asyncGenericHandler = - typeof(BaseUniversalWrapperAttribute).GetMethod(nameof(BaseUniversalWrapperAttribute.WrapAsync), BindingFlags.NonPublic | BindingFlags.Instance); - - private static readonly MethodInfo _syncGenericHandler = - typeof(BaseUniversalWrapperAttribute).GetMethod(nameof(BaseUniversalWrapperAttribute.WrapSync), BindingFlags.NonPublic | BindingFlags.Instance); - - protected object BaseHandle( - object instance, - Type type, - MethodBase method, - Func target, - string name, - object[] args, - Type returnType, - Attribute[] triggers) + [Advice(Kind.Around, Targets = Target.Method)] + public object Handle( + [Argument(Source.Instance)] object instance, + [Argument(Source.Type)] Type type, + [Argument(Source.Metadata)] MethodBase method, + [Argument(Source.Target)] Func target, + [Argument(Source.Name)] string name, + [Argument(Source.Arguments)] object[] args, + [Argument(Source.ReturnType)] Type returnType, + [Argument(Source.Triggers)] Attribute[] triggers) { var eventArgs = new AspectEventArgs { @@ -42,12 +33,22 @@ protected object BaseHandle( Triggers = triggers }; - var wrappers = triggers.OfType().ToArray(); + var wrappers = triggers.OfType().ToArray(); var handler = GetMethodHandler(method, returnType, wrappers); return handler(target, args, eventArgs); } + + private delegate object Handler(Func next, object[] args, AspectEventArgs eventArgs); + + private static readonly Dictionary _delegateCache = new Dictionary(); + + private static readonly MethodInfo _asyncGenericHandler = + typeof(UniversalWrapperAttribute).GetMethod(nameof(UniversalWrapperAttribute.WrapAsync), BindingFlags.NonPublic | BindingFlags.Instance); + + private static readonly MethodInfo _syncGenericHandler = + typeof(UniversalWrapperAttribute).GetMethod(nameof(UniversalWrapperAttribute.WrapSync), BindingFlags.NonPublic | BindingFlags.Instance); - private static Handler CreateMethodHandler(Type returnType, IEnumerable wrappers) + private static Handler CreateMethodHandler(Type returnType, IEnumerable wrappers) { var targetParam = Expression.Parameter(typeof(Func), "orig"); var eventArgsParam = Expression.Parameter(typeof(AspectEventArgs), "event"); @@ -84,7 +85,7 @@ private static Handler CreateMethodHandler(Type returnType, IEnumerable wrappers) + private static Handler GetMethodHandler(MethodBase method, Type returnType, IEnumerable wrappers) { if (!_delegateCache.TryGetValue(method, out var handler)) { diff --git a/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseUniversalWrapperAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/UniversalWrapperAttribute.cs similarity index 74% rename from libraries/src/AWS.Lambda.PowerTools/Attributes/BaseUniversalWrapperAttribute.cs rename to libraries/src/AWS.Lambda.PowerTools/Aspects/UniversalWrapperAttribute.cs index 8173c8da6..d61165aca 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Attributes/BaseUniversalWrapperAttribute.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Aspects/UniversalWrapperAttribute.cs @@ -1,10 +1,9 @@ using System; using System.Threading.Tasks; -using AWS.Lambda.PowerTools.Events; -namespace AWS.Lambda.PowerTools.Attributes +namespace AWS.Lambda.PowerTools.Aspects { - public abstract class BaseUniversalWrapperAttribute : Attribute + public abstract class UniversalWrapperAttribute : Attribute { protected internal virtual T WrapSync(Func target, object[] args, AspectEventArgs eventArgs) { diff --git a/libraries/src/AWS.Lambda.PowerTools/Attributes/MethodAspectAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Attributes/MethodAspectAttribute.cs deleted file mode 100644 index ae11de98f..000000000 --- a/libraries/src/AWS.Lambda.PowerTools/Attributes/MethodAspectAttribute.cs +++ /dev/null @@ -1,13 +0,0 @@ - -using System; -using AspectInjector.Broker; -using AWS.Lambda.PowerTools.Aspects; - -namespace AWS.Lambda.PowerTools.Attributes -{ - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] - [Injection(typeof(MethodWrapperAspect), Inherited = true)] - public abstract class MethodAspectAttribute : BaseMethodAspectAttribute - { - } -} \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs index 5b23a646b..66b2eaed5 100644 --- a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs +++ b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs @@ -2,8 +2,8 @@ using System.Linq; using Amazon.Lambda.PowerTools.Tracing; using Amazon.Lambda.PowerTools.Tracing.Internal; +using AWS.Lambda.PowerTools.Aspects; using AWS.Lambda.PowerTools.Core; -using AWS.Lambda.PowerTools.Events; using Moq; using Xunit; @@ -18,23 +18,63 @@ public class TracingAttributeColdStartTest public void OnEntry_WhenFirstCall_CapturesColdStart() { // Arrange + const bool isColdStart = true; var methodName = Guid.NewGuid().ToString(); var configurations = new Mock(); - var recorder = new Mock(); - var handler = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, - configurations.Object, recorder.Object); + var recorder1 = new Mock(); + var recorder2 = new Mock(); + var recorder3 = new Mock(); + var recorder4 = new Mock(); + + var handler1 = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder1.Object); + var handler2 = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder2.Object); + var handler3 = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder3.Object); + var handler4 = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, + configurations.Object, recorder4.Object); + var eventArgs = new AspectEventArgs {Name = methodName}; // Act - handler.OnEntry(eventArgs); + // Cold Start Execution + handler1.OnEntry(eventArgs); + handler2.OnEntry(eventArgs); + handler2.OnExit(eventArgs); + handler1.OnExit(eventArgs); + + // Warm Start Execution + handler3.OnEntry(eventArgs); + handler4.OnEntry(eventArgs); + handler4.OnExit(eventArgs); + handler3.OnExit(eventArgs); // Assert - recorder.Verify(v => + recorder1.Verify(v => v.AddAnnotation( It.Is(i => i == "ColdStart"), - It.Is(i => i) + It.Is(i => i == isColdStart) ), Times.Once); + + recorder2.Verify(v => + v.AddAnnotation( + It.IsAny(), + It.IsAny() + ), Times.Never); + + recorder3.Verify(v => + v.AddAnnotation( + It.Is(i => i == "ColdStart"), + It.Is(i => i == !isColdStart) + ), Times.Once); + + recorder4.Verify(v => + v.AddAnnotation( + It.IsAny(), + It.IsAny() + ), Times.Never); } } From 4899ad220a0b97134e131f32728c113396ead8d0 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Mon, 29 Nov 2021 16:19:37 +0000 Subject: [PATCH 3/8] add cooments --- .../Internal/TracingAspectHandler.cs | 10 +-- .../TracingAttribute.cs | 4 +- .../TracingCaptureMode.cs | 64 ++++++++++--------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs index 3ffaecb3d..cc0ed118b 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs @@ -8,7 +8,7 @@ internal class TracingAspectHandler : IMethodAspectAttribute { private readonly string _segmentName; private readonly string _namespace; - private readonly TracingCaptureMode _tracingCaptureMode; + private readonly TracingCaptureMode _captureMode; private readonly IPowerToolsConfigurations _powerToolsConfigurations; private readonly IXRayRecorder _xRayRecorder; @@ -20,14 +20,14 @@ internal TracingAspectHandler ( string segmentName, string @namespace, - TracingCaptureMode tracingCaptureMode, + TracingCaptureMode captureMode, IPowerToolsConfigurations powerToolsConfigurations, IXRayRecorder xRayRecorder ) { _segmentName = segmentName; _namespace = @namespace; - _tracingCaptureMode = tracingCaptureMode; + _captureMode = captureMode; _powerToolsConfigurations = powerToolsConfigurations; _xRayRecorder = xRayRecorder; } @@ -39,7 +39,7 @@ private string GetNamespace() private bool CaptureResponse() { - switch (_tracingCaptureMode) + switch (_captureMode) { case TracingCaptureMode.EnvironmentVariable: return _powerToolsConfigurations.TracerCaptureResponse; @@ -55,7 +55,7 @@ private bool CaptureResponse() private bool CaptureError() { - switch (_tracingCaptureMode) + switch (_captureMode) { case TracingCaptureMode.EnvironmentVariable: return _powerToolsConfigurations.TracerCaptureError; diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs index 922ec945b..f8cb40d69 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingAttribute.cs @@ -9,7 +9,7 @@ public class TracingAttribute : MethodAspectAttribute { public string SegmentName { get; set; } = ""; public string Namespace { get; set; } = ""; - public TracingCaptureMode TracingCaptureMode { get; set; } = TracingCaptureMode.EnvironmentVariable; + public TracingCaptureMode CaptureMode { get; set; } = TracingCaptureMode.EnvironmentVariable; private IMethodAspectAttribute _tracingHandler; private IMethodAspectAttribute TracingHandler => @@ -17,7 +17,7 @@ public class TracingAttribute : MethodAspectAttribute ( SegmentName, Namespace, - TracingCaptureMode, + CaptureMode, PowerToolsConfigurations.Instance, XRayRecorder.Instance ); diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingCaptureMode.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingCaptureMode.cs index e64927bac..2649898f5 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingCaptureMode.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/TracingCaptureMode.cs @@ -1,33 +1,37 @@ namespace Amazon.Lambda.PowerTools.Tracing { - public enum TracingCaptureMode - { - /** - * Enables annotation to capture only response. If this mode is explicitly overridden - * on {@link Tracing} annotation, it will override value of environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE - */ - Response, - /** - * Enabled annotation to capture only error from the method. If this mode is explicitly overridden - * on {@link Tracing} annotation, it will override value of environment variable POWERTOOLS_TRACER_CAPTURE_ERROR - */ - Error, - /** - * Enabled annotation to capture both response error from the method. If this mode is explicitly overridden - * on {@link Tracing} annotation, it will override value of environment variables POWERTOOLS_TRACER_CAPTURE_RESPONSE - * and POWERTOOLS_TRACER_CAPTURE_ERROR - */ - ResponseAndError, - /** - * Disables annotation to capture both response and error from the method. If this mode is explicitly overridden - * on {@link Tracing} annotation, it will override values of environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE - * and POWERTOOLS_TRACER_CAPTURE_ERROR - */ - Disabled, - /** - * Enables/Disables annotation to capture response and error from the method based on the value of - * environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE and POWERTOOLS_TRACER_CAPTURE_ERROR - */ - EnvironmentVariable - } + public enum TracingCaptureMode + { + /// + /// Enables attribute to capture only response. If this mode is explicitly overridden + /// on { attribute, it will override value of environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE + /// + Response, + + /// + /// Enabled attribute to capture only error from the method. If this mode is explicitly overridden + /// on attribute, it will override value of environment variable POWERTOOLS_TRACER_CAPTURE_ERROR + /// + Error, + + /// + /// Enabled attribute to capture both response error from the method. If this mode is explicitly overridden + /// on attribute, it will override value of environment variables POWERTOOLS_TRACER_CAPTURE_RESPONSE + /// and POWERTOOLS_TRACER_CAPTURE_ERROR + /// + ResponseAndError, + + /// + /// Disables attribute to capture both response and error from the method. If this mode is explicitly overridden + /// on attribute, it will override values of environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE + /// and POWERTOOLS_TRACER_CAPTURE_ERROR + /// + Disabled, + + /// + /// Enables/Disables attribute to capture response and error from the method based on the value of + /// environment variable POWERTOOLS_TRACER_CAPTURE_RESPONSE and POWERTOOLS_TRACER_CAPTURE_ERROR + /// + EnvironmentVariable + } } \ No newline at end of file From b8f0d2b162a9e6bf760c98ac8434bd1fc91ccc12 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Mon, 29 Nov 2021 16:54:10 +0000 Subject: [PATCH 4/8] Clean Constants --- .../AWS.Lambda.PowerTools/Core/Constants.cs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/libraries/src/AWS.Lambda.PowerTools/Core/Constants.cs b/libraries/src/AWS.Lambda.PowerTools/Core/Constants.cs index ef6598982..bba6ff69f 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Core/Constants.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Core/Constants.cs @@ -1,18 +1,10 @@ namespace AWS.Lambda.PowerTools.Core { - public static class Constants + internal static class Constants { - public const string TRACER_CAPTURE_RESPONSE_ENV = "POWERTOOLS_TRACER_CAPTURE_RESPONSE"; - public const string TRACER_CAPTURE_ERROR_ENV = "POWERTOOLS_TRACER_CAPTURE_ERROR"; - public const string TRACER_DISABLED_ENV = "POWERTOOLS_TRACE_DISABLED"; - public const string LOGGER_LOG_SAMPLING_RATE = "POWERTOOLS_LOGGER_SAMPLE_RATE"; - public const string LOGGER_LOG_EVENT_ENV = "POWERTOOLS_LOGGER_LOG_EVENT"; - public const string LOGGER_LOG_DEDUPLICATION_ENV = "POWERTOOLS_LOG_DEDUPLICATION_DISABLED"; - public const string MIDDLEWARE_FACTORY_TRACE_ENV = "POWERTOOLS_TRACE_MIDDLEWARES"; - public const string METRICS_NAMESPACE_ENV = "POWERTOOLS_METRICS_NAMESPACE"; - public const string SAM_LOCAL_ENV = "AWS_SAM_LOCAL"; - public const string CHALICE_LOCAL_ENV = "AWS_CHALICE_CLI_MODE"; - public const string SERVICE_NAME_ENV = "POWERTOOLS_SERVICE_NAME"; - public const string XRAY_TRACE_ID_ENV = "_X_AMZN_TRACE_ID"; + internal const string SERVICE_NAME_ENV = "POWERTOOLS_SERVICE_NAME"; + internal const string SAM_LOCAL_ENV = "AWS_SAM_LOCAL"; + internal const string TRACER_CAPTURE_RESPONSE_ENV = "POWERTOOLS_TRACER_CAPTURE_RESPONSE"; + internal const string TRACER_CAPTURE_ERROR_ENV = "POWERTOOLS_TRACER_CAPTURE_ERROR"; } } \ No newline at end of file From 389b63c36e8d73887041359fdc151635eb228846 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Mon, 29 Nov 2021 18:30:09 +0000 Subject: [PATCH 5/8] add Service annotation --- .../Internal/TracingAspectHandler.cs | 25 +++++---- .../Core/IPowerToolsConfigurations.cs | 1 + .../Core/PowerToolsConfigurations.cs | 3 ++ .../Core/PowerToolsConfigurationsTest.cs | 53 +++++++++++++++++++ .../TracingAttributeTest.cs | 47 ++++++++++++++-- 5 files changed, 115 insertions(+), 14 deletions(-) diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs index cc0ed118b..183fdcddb 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs @@ -13,20 +13,20 @@ internal class TracingAspectHandler : IMethodAspectAttribute private readonly IXRayRecorder _xRayRecorder; private static bool _isColdStart = true; - private static bool _captureColdStart = true; - private bool _isColdStartCaptured; + private static bool _captureAnnotations = true; + private bool _isAnnotationsCaptured; internal TracingAspectHandler ( string segmentName, - string @namespace, + string nameSpace, TracingCaptureMode captureMode, IPowerToolsConfigurations powerToolsConfigurations, IXRayRecorder xRayRecorder ) { _segmentName = segmentName; - _namespace = @namespace; + _namespace = nameSpace; _captureMode = captureMode; _powerToolsConfigurations = powerToolsConfigurations; _xRayRecorder = xRayRecorder; @@ -81,13 +81,20 @@ public void OnEntry(AspectEventArgs eventArgs) _xRayRecorder.BeginSubsegment(segmentName); _xRayRecorder.SetNamespace(nameSpace); - if (_captureColdStart) + if (_captureAnnotations) { Console.WriteLine($"Capturing ColdStart for method: {eventArgs.Name}, ColdStart: {_isColdStart}"); _xRayRecorder.AddAnnotation("ColdStart", _isColdStart); + _isColdStart = false; - _captureColdStart = false; - _isColdStartCaptured = true; + _captureAnnotations = false; + _isAnnotationsCaptured = true; + + if (_powerToolsConfigurations.IsServiceNameDefined) + { + Console.WriteLine($"Capturing ServiceName for method: {eventArgs.Name}, ServiceName: {_powerToolsConfigurations.ServiceName}"); + _xRayRecorder.AddAnnotation("Service", _powerToolsConfigurations.ServiceName); + } } } @@ -132,8 +139,8 @@ public void OnExit(AspectEventArgs eventArgs) { Console.WriteLine($"OnExit method {eventArgs.Name}"); - if (_isColdStartCaptured) - _captureColdStart = true; + if (_isAnnotationsCaptured) + _captureAnnotations = true; if (!_powerToolsConfigurations.IsSamLocal) { diff --git a/libraries/src/AWS.Lambda.PowerTools/Core/IPowerToolsConfigurations.cs b/libraries/src/AWS.Lambda.PowerTools/Core/IPowerToolsConfigurations.cs index b99dd2646..0bbde90db 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Core/IPowerToolsConfigurations.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Core/IPowerToolsConfigurations.cs @@ -3,6 +3,7 @@ namespace AWS.Lambda.PowerTools.Core public interface IPowerToolsConfigurations { string ServiceName { get; } + bool IsServiceNameDefined { get; } bool TracerCaptureResponse{ get; } bool TracerCaptureError{ get; } bool IsSamLocal{ get; } diff --git a/libraries/src/AWS.Lambda.PowerTools/Core/PowerToolsConfigurations.cs b/libraries/src/AWS.Lambda.PowerTools/Core/PowerToolsConfigurations.cs index 176832512..3f6948b4d 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Core/PowerToolsConfigurations.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Core/PowerToolsConfigurations.cs @@ -33,6 +33,9 @@ public bool GetEnvironmentVariableOrDefault(string variable, bool defaultValue) public string ServiceName => GetEnvironmentVariableOrDefault(Constants.SERVICE_NAME_ENV, "service_undefined"); + public bool IsServiceNameDefined => + !string.IsNullOrWhiteSpace(GetEnvironmentVariable(Constants.SERVICE_NAME_ENV)); + public bool TracerCaptureResponse => GetEnvironmentVariableOrDefault(Constants.TRACER_CAPTURE_RESPONSE_ENV, true); diff --git a/libraries/tests/AWS.Lambda.PowerTools.Tests/Core/PowerToolsConfigurationsTest.cs b/libraries/tests/AWS.Lambda.PowerTools.Tests/Core/PowerToolsConfigurationsTest.cs index 5f9bad9c7..0a4fda649 100644 --- a/libraries/tests/AWS.Lambda.PowerTools.Tests/Core/PowerToolsConfigurationsTest.cs +++ b/libraries/tests/AWS.Lambda.PowerTools.Tests/Core/PowerToolsConfigurationsTest.cs @@ -215,6 +215,59 @@ public void ServiceName_WhenEnvironmentHasValue_ReturnsValue() Assert.Equal(result, serviceName); } + + #endregion + + #region IsServiceNameDefined Tests + + [Fact] + public void IsServiceNameDefined_WhenEnvironmentHasValue_ReturnsTrue() + { + // Arrange + var serviceName = Guid.NewGuid().ToString(); + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.SERVICE_NAME_ENV) + ).Returns(serviceName); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.IsServiceNameDefined; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.SERVICE_NAME_ENV) + ), Times.Once); + + Assert.True(result); + } + + [Fact] + public void IsServiceNameDefined_WhenEnvironmentDoesNotHaveValue_ReturnsFalse() + { + // Arrange + var systemWrapper = new Mock(); + + systemWrapper.Setup(c => + c.GetEnvironmentVariable(Constants.SERVICE_NAME_ENV) + ).Returns(string.Empty); + + var configurations = new PowerToolsConfigurations(systemWrapper.Object); + + // Act + var result = configurations.IsServiceNameDefined; + + // Assert + systemWrapper.Verify(v => + v.GetEnvironmentVariable( + It.Is(i => i == Constants.SERVICE_NAME_ENV) + ), Times.Once); + + Assert.False(result); + } #endregion diff --git a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs index 66b2eaed5..1e95152f9 100644 --- a/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs +++ b/libraries/tests/AWS.Lambda.PowerTools.Tracing.Tests/TracingAttributeTest.cs @@ -20,7 +20,20 @@ public void OnEntry_WhenFirstCall_CapturesColdStart() // Arrange const bool isColdStart = true; var methodName = Guid.NewGuid().ToString(); - var configurations = new Mock(); + var serviceName = Guid.NewGuid().ToString(); + + var configurations1 = new Mock(); + configurations1.Setup(c => + c.IsServiceNameDefined + ).Returns(true); + configurations1.Setup(c => + c.ServiceName + ).Returns(serviceName); + + var configurations2 = new Mock(); + configurations2.Setup(c => + c.IsServiceNameDefined + ).Returns(false); var recorder1 = new Mock(); var recorder2 = new Mock(); @@ -28,13 +41,13 @@ public void OnEntry_WhenFirstCall_CapturesColdStart() var recorder4 = new Mock(); var handler1 = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, - configurations.Object, recorder1.Object); + configurations1.Object, recorder1.Object); var handler2 = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, - configurations.Object, recorder2.Object); + configurations1.Object, recorder2.Object); var handler3 = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, - configurations.Object, recorder3.Object); + configurations2.Object, recorder3.Object); var handler4 = new TracingAspectHandler(null, null, TracingCaptureMode.EnvironmentVariable, - configurations.Object, recorder4.Object); + configurations2.Object, recorder4.Object); var eventArgs = new AspectEventArgs {Name = methodName}; @@ -57,24 +70,48 @@ public void OnEntry_WhenFirstCall_CapturesColdStart() It.Is(i => i == "ColdStart"), It.Is(i => i == isColdStart) ), Times.Once); + + recorder1.Verify(v => + v.AddAnnotation( + It.Is(i => i == "Service"), + It.Is(i => i == serviceName) + ), Times.Once); recorder2.Verify(v => v.AddAnnotation( It.IsAny(), It.IsAny() ), Times.Never); + + recorder2.Verify(v => + v.AddAnnotation( + It.IsAny(), + It.IsAny() + ), Times.Never); recorder3.Verify(v => v.AddAnnotation( It.Is(i => i == "ColdStart"), It.Is(i => i == !isColdStart) ), Times.Once); + + recorder3.Verify(v => + v.AddAnnotation( + It.IsAny(), + It.IsAny() + ), Times.Never); recorder4.Verify(v => v.AddAnnotation( It.IsAny(), It.IsAny() ), Times.Never); + + recorder3.Verify(v => + v.AddAnnotation( + It.IsAny(), + It.IsAny() + ), Times.Never); } } From 1d97876dca29549abc1d0c285d322d6f72f5ddf4 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Mon, 29 Nov 2021 19:17:34 +0000 Subject: [PATCH 6/8] remove parameter injection --- .../Aspects/MethodAspectAttribute.cs | 37 +------------------ 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodAspectAttribute.cs b/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodAspectAttribute.cs index bf245e2f2..66a061a2a 100644 --- a/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodAspectAttribute.cs +++ b/libraries/src/AWS.Lambda.PowerTools/Aspects/MethodAspectAttribute.cs @@ -1,6 +1,4 @@ using System; -using System.Linq; -using System.Reflection; using System.Threading.Tasks; using AspectInjector.Broker; @@ -15,7 +13,7 @@ protected internal sealed override T WrapSync(Func target, objec OnEntry(eventArgs); try { - var result = base.WrapSync(target, GetArguments(eventArgs, args), eventArgs); + var result = base.WrapSync(target, args, eventArgs); OnSuccess(eventArgs, result); return result; } @@ -34,7 +32,7 @@ protected internal sealed override async Task WrapAsync(Func WrapAsync(Func Date: Mon, 29 Nov 2021 20:17:44 +0000 Subject: [PATCH 7/8] remove console logs --- .../Internal/TracingAspectHandler.cs | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs index 183fdcddb..e5cd3f935 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Tracing/Internal/TracingAspectHandler.cs @@ -71,19 +71,14 @@ private bool CaptureError() public void OnEntry(AspectEventArgs eventArgs) { - Console.WriteLine($"OnEntry method {eventArgs.Name}"); - var segmentName = !string.IsNullOrWhiteSpace(_segmentName) ? _segmentName : $"## {eventArgs.Name}"; var nameSpace = GetNamespace(); - - Console.WriteLine($"BeginSubsegment method {eventArgs.Name}, SegmentName: {segmentName}, namespace: {nameSpace}"); - + _xRayRecorder.BeginSubsegment(segmentName); _xRayRecorder.SetNamespace(nameSpace); if (_captureAnnotations) { - Console.WriteLine($"Capturing ColdStart for method: {eventArgs.Name}, ColdStart: {_isColdStart}"); _xRayRecorder.AddAnnotation("ColdStart", _isColdStart); _isColdStart = false; @@ -91,21 +86,16 @@ public void OnEntry(AspectEventArgs eventArgs) _isAnnotationsCaptured = true; if (_powerToolsConfigurations.IsServiceNameDefined) - { - Console.WriteLine($"Capturing ServiceName for method: {eventArgs.Name}, ServiceName: {_powerToolsConfigurations.ServiceName}"); _xRayRecorder.AddAnnotation("Service", _powerToolsConfigurations.ServiceName); - } } } public void OnSuccess(AspectEventArgs eventArgs, object result) { - Console.WriteLine($"OnSuccess method {eventArgs.Name}"); - if (CaptureResponse()) { var nameSpace = GetNamespace(); - Console.WriteLine($"Capturing Response for method: {eventArgs.Name}, namespace: {nameSpace}"); + _xRayRecorder.AddMetadata ( nameSpace: nameSpace, @@ -117,13 +107,10 @@ public void OnSuccess(AspectEventArgs eventArgs, object result) public T OnException(AspectEventArgs eventArgs, Exception exception) { - Console.WriteLine($"OnException method {eventArgs.Name} --> {exception}"); - if (CaptureError()) { var nameSpace = GetNamespace(); - Console.WriteLine($"Capturing Error for method: {eventArgs.Name}, namespace: {nameSpace}"); - + _xRayRecorder.AddMetadata ( nameSpace: nameSpace, @@ -137,16 +124,11 @@ public T OnException(AspectEventArgs eventArgs, Exception exception) public void OnExit(AspectEventArgs eventArgs) { - Console.WriteLine($"OnExit method {eventArgs.Name}"); - if (_isAnnotationsCaptured) _captureAnnotations = true; if (!_powerToolsConfigurations.IsSamLocal) - { - Console.WriteLine($"EndSubsegment method {eventArgs.Name}"); _xRayRecorder.EndSubsegment(); - } } } } \ No newline at end of file From 047b95e1853a6abf86a84f06f1665c87ccb30e80 Mon Sep 17 00:00:00 2001 From: Amir Khairalomoum Date: Mon, 29 Nov 2021 20:42:30 +0000 Subject: [PATCH 8/8] undo metrics changes --- libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs b/libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs index 93000aeea..da08b6e08 100644 --- a/libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs +++ b/libraries/src/AWS.Lambda.PowerTools.Metrics/Metrics.cs @@ -9,9 +9,6 @@ public class Metrics : IMetrics private bool _isColdStart = true; private bool _captureMetricsEvenIfEmpty; - private static Metrics _instance; - public static Metrics Instance => _instance ??= new Metrics("dotnet-lambdapowertools", "lambda-example"); - /// /// Creates Metrics with no namespace or service name defined - requires that they are defined after initialization ///