From ddb551f009678da68e22dd2f6962029476b8b481 Mon Sep 17 00:00:00 2001 From: sjuarezgx Date: Thu, 9 Nov 2023 13:48:12 -0300 Subject: [PATCH 1/5] Support Automatic Spans for opentelemetry --- .../src/dotnetcore/GxClasses/GxClasses.csproj | 4 +- .../GxNetCoreStartup/GxNetCoreStartup.csproj | 2 +- .../GxClasses/Model/GXBaseObject.cs | 42 ++++++- .../GxClasses/Model/GXSilentTrn.cs | 103 ++++++++++++++++-- 4 files changed, 136 insertions(+), 15 deletions(-) diff --git a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj index f2cba9c17..76357c75d 100644 --- a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj +++ b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj @@ -1,4 +1,4 @@ - + net6.0;net8.0 @@ -163,7 +163,7 @@ - + diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj index ebd33f667..9b0bd19a9 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/GxNetCoreStartup.csproj @@ -16,7 +16,7 @@ - + diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs index 091060c9a..c672a867b 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXBaseObject.cs @@ -2,6 +2,9 @@ using GeneXus.Encryption; using GeneXus.Http; using GeneXus.Mock; +#if NETCORE +using GeneXus.Services.OpenTelemetry; +#endif using GeneXus.Utils; using Jayrock.Json; using log4net; @@ -10,6 +13,7 @@ #endif using System; using System.Collections.Generic; +using System.Diagnostics; using System.Reflection; using System.Threading; @@ -24,10 +28,24 @@ public class GXBaseObject { static readonly IGXLogger log = GXLoggerFactory.GetLogger(); +#if NETCORE + internal static ActivitySource activitySource; +#endif private Dictionary callTargetsByObject = new Dictionary(); protected IGxContext _Context; bool _isMain; protected bool _isApi; +#if NETCORE + internal static ActivitySource ActivitySource { + get { + if (activitySource == null) + activitySource = new(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME); + return activitySource; + } + } +#endif + protected virtual bool GenOtelSpanEnabled() { return false; } + protected virtual void ExecuteEx() { if (GxMockProvider.Provider != null) @@ -36,11 +54,31 @@ protected virtual void ExecuteEx() if (GxMockProvider.Provider.Handle(_Context, this, parmInfo)) return; } - ExecutePrivate(); + ExecuteImpl(); } protected virtual void ExecutePrivate() { - + + } +#if NETCORE + private void ExecuteUsingSpanCode() + { + using (Activity activity = ActivitySource.StartActivity($"{this.GetType().FullName}.execute")) + { + ExecutePrivate(); + } + } +#endif + protected virtual void ExecuteImpl() + { +#if NETCORE + if (GenOtelSpanEnabled()) + ExecuteUsingSpanCode(); + else + ExecutePrivate(); +#else + ExecutePrivate(); +#endif } protected virtual void ExecutePrivateCatch(object stateInfo) { diff --git a/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs b/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs index 56dd05080..6d4f8eb52 100644 --- a/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs +++ b/dotnet/src/dotnetframework/GxClasses/Model/GXSilentTrn.cs @@ -18,6 +18,7 @@ namespace GeneXus.Utils using Configuration; using System.Globalization; using GeneXus.Http; + using System.Diagnostics; public interface IGxSilentTrn { @@ -160,6 +161,7 @@ public IGxSilentTrn Transaction get {return trn;} set {trn = value;} } + public IGxSilentTrn getTransaction() { return trn; @@ -169,30 +171,105 @@ public void setTransaction(IGxSilentTrn t) dirties.Clear(); trn = t; } +#if NETCORE + protected virtual bool GenOtelSpanEnabled() { return false; } + private Activity StartSpan(string methodName) + { + if (GenOtelSpanEnabled()) + { + return InitializeSpan(methodName); + } + return null; + } + private void EndSpan(Activity activity) + { + if (GenOtelSpanEnabled()) + { + activity?.Stop(); + } + } + private Activity InitializeSpan(string methodName) + { + if (GenOtelSpanEnabled()) + { + return GXBaseObject.ActivitySource.StartActivity($"{this.GetType().FullName}.{methodName}"); + } + return null; + } +#endif public virtual void Save() { - if( Transaction != null) + if (Transaction != null) + { +#if NETCORE + Activity activity = StartSpan("Save"); +#endif Transaction.Save(); +#if NETCORE + EndSpan(activity); +#endif + } } public virtual bool Insert() { - if (Transaction != null) - return Transaction.Insert(); - return false; + if (Transaction != null) + { +#if NETCORE + Activity activity = StartSpan("Insert"); +#endif + try + { + return Transaction.Insert(); + } + finally + { +#if NETCORE + EndSpan(activity); + +#endif + } + } + return false; } public virtual bool Update() { if (Transaction != null) - return Transaction.Update(); - return false; + { +#if NETCORE + Activity activity = StartSpan("Update"); +#endif + try + { + return Transaction.Update(); + } + finally { +#if NETCORE + EndSpan(activity); +#endif + } + } + return false; } - public virtual bool InsertOrUpdate() - { - if (Transaction != null) - return Transaction.InsertOrUpdate(); + public virtual bool InsertOrUpdate() + { + if (Transaction != null) + { +#if NETCORE + Activity activity = StartSpan("InsertOrUpdate"); +#endif + try + { + return Transaction.InsertOrUpdate(); + } + finally { +#if NETCORE + EndSpan(activity); +#endif + } + } return false; } @@ -205,8 +282,14 @@ public virtual void Delete( ) { if( Transaction != null) { +#if NETCORE + Activity activity = StartSpan("Delete"); +#endif Transaction.SetMode("DLT") ; Transaction.Save(); +#if NETCORE + EndSpan(activity); +#endif } } public virtual bool Success() From 7df47d69f9d0d02396e658f834278868a5f514d5 Mon Sep 17 00:00:00 2001 From: sjuarezgx Date: Thu, 9 Nov 2023 17:31:42 -0300 Subject: [PATCH 2/5] Add classes for custom spans implementation --- dotnet/src/dotnetcore/GxClasses/GxClasses.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj index 76357c75d..a34978da3 100644 --- a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj +++ b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj @@ -120,7 +120,6 @@ - From 57443122cf29649bb2d0fcf2bf11d19520821fc1 Mon Sep 17 00:00:00 2001 From: sjuarezgx Date: Thu, 9 Nov 2023 17:33:20 -0300 Subject: [PATCH 3/5] Add classes for manual instrumentation --- .../Diagnostics/Opentelemetry/OtelSpan.cs | 106 ++++++++++++++++++ .../Diagnostics/Opentelemetry/OtelTracer.cs | 35 ++++++ 2 files changed, 141 insertions(+) create mode 100644 dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelSpan.cs create mode 100644 dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelTracer.cs diff --git a/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelSpan.cs b/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelSpan.cs new file mode 100644 index 000000000..0cc738452 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelSpan.cs @@ -0,0 +1,106 @@ +using System; +using System.Diagnostics; +using OpenTelemetry.Trace; + +namespace GxClasses.Diagnostics.Opentelemetry +{ + public class OtelSpan + { + private Activity _activity; + public string Id + { get => _activity?.Id; } + + public bool IsAllDataRequested + { + get + { + if (_activity != null) + return _activity.IsAllDataRequested; + return false; + } + } + + public bool IsStopped + { get + { + if (_activity != null ) + return _activity.IsStopped; + return false; + } + } + + public SpanKind Kind + { get => (SpanKind)_activity?.Kind; } + + public string ParentId + { get => _activity?.ParentId; } + + public string ParentSpanId + { get => _activity?.ParentSpanId.ToString(); } + + public string TraceId + { get => _activity?.TraceId.ToString(); } + + public SpanStatusCode Status + { get => (SpanStatusCode)_activity?.Status; } + + public enum SpanStatusCode + { + Unset, + Ok, + Error + } + + internal OtelSpan(Activity activity) + { + _activity = activity; + } + + public void Start() + { + _activity?.Start(); + } + + public void Stop() + { + _activity?.Stop(); + } + public void RecordException(string message) + { + _activity.RecordException(new Exception(message)); + } + + public void SetTag(string property, string value) + { + _activity.SetTag(property, value); + } + public string GetTagItem(string property) + { + return _activity.GetTagItem(property).ToString(); + } + public void AddBaggage(string property, string value) + { + _activity.AddBaggage(property, value); + } + public string GetBaggageItem(string property) + { + return _activity.GetBaggageItem(property).ToString(); + } + public void SetStatus(SpanStatusCode spanStatusCode, string message) + { + _activity.SetStatus((ActivityStatusCode)spanStatusCode, message); + } + + public SpanStatusCode GetStatus() + { + return (SpanStatusCode)_activity.GetStatus().StatusCode; + } + + //ToDO + //public void AddEvent() + //{ + + //} + + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelTracer.cs b/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelTracer.cs new file mode 100644 index 000000000..38a6504a1 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelTracer.cs @@ -0,0 +1,35 @@ +using System.Diagnostics; +using GeneXus.Application; +using GeneXus.Attributes; + +namespace GxClasses.Diagnostics.Opentelemetry +{ + [GXApi] + public class OtelTracer + { + public enum SpanKind + { + Client, + Consumer, + Internal, + Producer, + Server + } + + public static OtelSpan CreateSpan(string name, SpanKind kind) + { + Activity activity = GXBaseObject.ActivitySource.StartActivity(name, (ActivityKind)kind); + return new OtelSpan(activity); + } + + public static OtelSpan GetCurrent() + { + return new OtelSpan(Activity.Current); + } + + public static bool HasListeners() + { + return GXBaseObject.ActivitySource.HasListeners(); + } + } +} From c63d8d7e35e56b8f17db45e112560441a05ca78a Mon Sep 17 00:00:00 2001 From: sjuarezgx Date: Thu, 9 Nov 2023 20:45:42 -0300 Subject: [PATCH 4/5] Add method ExecuteImpl for reorganization src --- dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs b/dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs index ac28d111c..fab3b2b8e 100644 --- a/dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs +++ b/dotnet/src/dotnetframework/GxClasses/Reorg/GXReorg.cs @@ -58,7 +58,11 @@ protected virtual void ExecutePrivate() { } - public IGxContext context + protected virtual void ExecuteImpl() + { + ExecutePrivate(); + } + public IGxContext context { get { return _Context; } set { _Context = value; } From 758cef142fd8f32c3e25ddc17c2140f0cab9411f Mon Sep 17 00:00:00 2001 From: sjuarezgx Date: Sun, 12 Nov 2023 19:58:34 -0300 Subject: [PATCH 5/5] Move classes that implement custom spans to independent project. --- dotnet/DotNetStandardClasses.sln | 8 +++++ .../src/dotnetcore/GxClasses/GxClasses.csproj | 2 +- .../GxClasses/Properties/AssemblyInfo.cs | 3 +- .../GXOtel.Diagnostics.csproj | 23 ++++++++++++++ .../GXOtel.Diagnostics}/OtelSpan.cs | 30 +++++++++++-------- .../GXOtel.Diagnostics}/OtelTracer.cs | 10 +++---- 6 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj rename dotnet/src/dotnetcore/{GxClasses/Diagnostics/Opentelemetry => Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics}/OtelSpan.cs (84%) rename dotnet/src/dotnetcore/{GxClasses/Diagnostics/Opentelemetry => Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics}/OtelTracer.cs (68%) diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index bbd55c51d..e07c15409 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -250,8 +250,11 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreAttackMitigationTest", "test\DotNetCoreAttackMitigationTest\DotNetCoreAttackMitigationTest.csproj", "{2D615969-53E2-4B77-9A9A-75C33865CF76}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreChunkedTest", "test\DotNetCoreChunkedTest\DotNetCoreChunkedTest.csproj", "{5D2B1299-479F-430A-8D72-34D44FB299FD}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetPDFUnitTest", "test\DotNetPdfTest\DotNetPDFUnitTest.csproj", "{0FCFB078-5584-469F-92CC-61B0A6216D0D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXOtel.Diagnostics", "src\dotnetcore\Providers\OpenTelemetry\Diagnostics\GXOtel.Diagnostics\GXOtel.Diagnostics.csproj", "{A42066E8-DDB9-4767-AEFA-E6D13EFF051A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -614,6 +617,10 @@ Global {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {0FCFB078-5584-469F-92CC-61B0A6216D0D}.Release|Any CPU.Build.0 = Release|Any CPU + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -735,6 +742,7 @@ Global {2D615969-53E2-4B77-9A9A-75C33865CF76} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {5D2B1299-479F-430A-8D72-34D44FB299FD} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {0FCFB078-5584-469F-92CC-61B0A6216D0D} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {A42066E8-DDB9-4767-AEFA-E6D13EFF051A} = {BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj index a34978da3..43b211dc5 100644 --- a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj +++ b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj @@ -1,4 +1,4 @@ - + net6.0;net8.0 diff --git a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs index ee4196dbb..1eeded06b 100644 --- a/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs +++ b/dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs @@ -14,4 +14,5 @@ [assembly: InternalsVisibleTo("GeneXus.Deploy.AzureFunctions.Handlers")] [assembly: InternalsVisibleTo("AzureFunctionsTest")] [assembly: InternalsVisibleTo("GXMessageBroker")] -[assembly: InternalsVisibleTo("DotNetCoreChunkedTest")] \ No newline at end of file +[assembly: InternalsVisibleTo("DotNetCoreChunkedTest")] +[assembly: InternalsVisibleTo("GeneXus.OpenTelemetry.Diagnostics")] \ No newline at end of file diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj new file mode 100644 index 000000000..8c6a62190 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/GXOtel.Diagnostics.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + NETCORE; + Properties + false + GeneXus.OpenTelemetry.Diagnostics + OpenTelemetry Diagnostics + GeneXus.OpenTelemetry.Diagnostics + NU1605 + + + + + + + + + + + + diff --git a/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelSpan.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs similarity index 84% rename from dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelSpan.cs rename to dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs index 0cc738452..4b35201d4 100644 --- a/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelSpan.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelSpan.cs @@ -1,12 +1,21 @@ using System; using System.Diagnostics; +using GeneXus.Application; using OpenTelemetry.Trace; +using static GeneXus.OpenTelemetry.Diagnostics.OtelTracer; -namespace GxClasses.Diagnostics.Opentelemetry +namespace GeneXus.OpenTelemetry.Diagnostics { public class OtelSpan { private Activity _activity; + + public enum SpanStatusCode + { + Unset, + Ok, + Error + } public string Id { get => _activity?.Id; } @@ -29,8 +38,8 @@ public bool IsStopped } } - public SpanKind Kind - { get => (SpanKind)_activity?.Kind; } + public short Kind + { get => (short)_activity?.Kind; } public string ParentId { get => _activity?.ParentId; } @@ -41,21 +50,18 @@ public string ParentSpanId public string TraceId { get => _activity?.TraceId.ToString(); } - public SpanStatusCode Status - { get => (SpanStatusCode)_activity?.Status; } - - public enum SpanStatusCode - { - Unset, - Ok, - Error - } + public short Status + { get => (short)_activity?.Status; } internal OtelSpan(Activity activity) { _activity = activity; } + public OtelSpan() + { + _activity = Activity.Current; + } public void Start() { _activity?.Start(); diff --git a/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelTracer.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs similarity index 68% rename from dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelTracer.cs rename to dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs index 38a6504a1..8ce722bef 100644 --- a/dotnet/src/dotnetcore/GxClasses/Diagnostics/Opentelemetry/OtelTracer.cs +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/Diagnostics/GXOtel.Diagnostics/OtelTracer.cs @@ -2,12 +2,12 @@ using GeneXus.Application; using GeneXus.Attributes; -namespace GxClasses.Diagnostics.Opentelemetry +namespace GeneXus.OpenTelemetry.Diagnostics { [GXApi] public class OtelTracer { - public enum SpanKind + public enum SpanType { Client, Consumer, @@ -16,18 +16,18 @@ public enum SpanKind Server } - public static OtelSpan CreateSpan(string name, SpanKind kind) + public OtelSpan CreateSpan(string name, SpanType kind) { Activity activity = GXBaseObject.ActivitySource.StartActivity(name, (ActivityKind)kind); return new OtelSpan(activity); } - public static OtelSpan GetCurrent() + public OtelSpan GetCurrent() { return new OtelSpan(Activity.Current); } - public static bool HasListeners() + public bool HasListeners() { return GXBaseObject.ActivitySource.HasListeners(); }