diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln index 9acaf7c71..2df891d39 100644 --- a/dotnet/DotNetStandardClasses.sln +++ b/dotnet/DotNetStandardClasses.sln @@ -212,9 +212,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXAmazonSQS", "src\dotnetco EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "apiattractions", "src\extensions\Azure\test\apiattractions\apiattractions.csproj", "{E85FDB0F-FA81-4CDD-8BF3-865269CE2DB3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GXMessageBroker", "src\dotnetcore\Providers\Messaging\GXMessageBroker\GXMessageBroker.csproj", "{3B1B5706-E896-4CEB-A551-E30226303BDB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXMessageBroker", "src\dotnetcore\Providers\Messaging\GXMessageBroker\GXMessageBroker.csproj", "{3B1B5706-E896-4CEB-A551-E30226303BDB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GXAzureServiceBus", "src\dotnetcore\Providers\Messaging\GXAzureServiceBus\GXAzureServiceBus.csproj", "{F8ABEA82-F823-4E9C-96FA-26AF24C932E0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXAzureServiceBus", "src\dotnetcore\Providers\Messaging\GXAzureServiceBus\GXAzureServiceBus.csproj", "{F8ABEA82-F823-4E9C-96FA-26AF24C932E0}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectHealthTest", "test\ProjectHealthTest\ProjectHealthTest.csproj", "{65048104-212A-4819-AECF-89CA9C08C83F}" EndProject @@ -222,6 +222,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetRedisTest", "test\Dot EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreWebUnitTest", "test\DotNetCoreWebUnitTest\DotNetCoreWebUnitTest.csproj", "{531863CA-93A0-42AA-AB5C-FA0E672C03B8}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "opentelemetry", "opentelemetry", "{BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeneXus.OpenTelemetry.Lightstep.AspNet", "src\dotnetcore\Providers\OpenTelemetry\OpenTelemetryLightStep\GeneXus.OpenTelemetry.Lightstep.AspNet.csproj", "{27D54041-BDD5-428E-8CA6-C96D519F5451}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeneXus.OpenTelemetry.AWS.AspNet", "src\dotnetcore\Providers\OpenTelemetry\OpenTelemetryAWSOtel\GeneXus.OpenTelemetry.AWS.AspNet.csproj", "{B5A9DEA7-67EC-49E4-924E-4729C34286EC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneXus.OpenTelemetry", "src\dotnetcore\Providers\OpenTelemetry\OpenTelemetry\GeneXus.OpenTelemetry.csproj", "{00B1FA38-7D0B-47E4-860C-23490249A4D6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -520,14 +528,6 @@ Global {E85FDB0F-FA81-4CDD-8BF3-865269CE2DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU {E85FDB0F-FA81-4CDD-8BF3-865269CE2DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU {E85FDB0F-FA81-4CDD-8BF3-865269CE2DB3}.Release|Any CPU.Build.0 = Release|Any CPU - {65048104-212A-4819-AECF-89CA9C08C83F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {65048104-212A-4819-AECF-89CA9C08C83F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {65048104-212A-4819-AECF-89CA9C08C83F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {65048104-212A-4819-AECF-89CA9C08C83F}.Release|Any CPU.Build.0 = Release|Any CPU - {48430E50-043A-47A2-8278-B86A4420758A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48430E50-043A-47A2-8278-B86A4420758A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48430E50-043A-47A2-8278-B86A4420758A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48430E50-043A-47A2-8278-B86A4420758A}.Release|Any CPU.Build.0 = Release|Any CPU {3B1B5706-E896-4CEB-A551-E30226303BDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3B1B5706-E896-4CEB-A551-E30226303BDB}.Debug|Any CPU.Build.0 = Debug|Any CPU {3B1B5706-E896-4CEB-A551-E30226303BDB}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -536,10 +536,30 @@ Global {F8ABEA82-F823-4E9C-96FA-26AF24C932E0}.Debug|Any CPU.Build.0 = Debug|Any CPU {F8ABEA82-F823-4E9C-96FA-26AF24C932E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {F8ABEA82-F823-4E9C-96FA-26AF24C932E0}.Release|Any CPU.Build.0 = Release|Any CPU + {65048104-212A-4819-AECF-89CA9C08C83F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65048104-212A-4819-AECF-89CA9C08C83F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65048104-212A-4819-AECF-89CA9C08C83F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65048104-212A-4819-AECF-89CA9C08C83F}.Release|Any CPU.Build.0 = Release|Any CPU + {48430E50-043A-47A2-8278-B86A4420758A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48430E50-043A-47A2-8278-B86A4420758A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48430E50-043A-47A2-8278-B86A4420758A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48430E50-043A-47A2-8278-B86A4420758A}.Release|Any CPU.Build.0 = Release|Any CPU {531863CA-93A0-42AA-AB5C-FA0E672C03B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {531863CA-93A0-42AA-AB5C-FA0E672C03B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {531863CA-93A0-42AA-AB5C-FA0E672C03B8}.Release|Any CPU.ActiveCfg = Release|Any CPU {531863CA-93A0-42AA-AB5C-FA0E672C03B8}.Release|Any CPU.Build.0 = Release|Any CPU + {27D54041-BDD5-428E-8CA6-C96D519F5451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27D54041-BDD5-428E-8CA6-C96D519F5451}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27D54041-BDD5-428E-8CA6-C96D519F5451}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27D54041-BDD5-428E-8CA6-C96D519F5451}.Release|Any CPU.Build.0 = Release|Any CPU + {B5A9DEA7-67EC-49E4-924E-4729C34286EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5A9DEA7-67EC-49E4-924E-4729C34286EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5A9DEA7-67EC-49E4-924E-4729C34286EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5A9DEA7-67EC-49E4-924E-4729C34286EC}.Release|Any CPU.Build.0 = Release|Any CPU + {00B1FA38-7D0B-47E4-860C-23490249A4D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00B1FA38-7D0B-47E4-860C-23490249A4D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00B1FA38-7D0B-47E4-860C-23490249A4D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00B1FA38-7D0B-47E4-860C-23490249A4D6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -642,11 +662,15 @@ Global {DCEC0B38-93B6-4003-81E6-9FBC2BB4F163} = {7BA5A2CE-7992-4F87-9D84-91AE4D046F5A} {F8BA0D65-267D-491F-BFAB-33F5E5B61AD7} = {30159B0F-BE61-4DB7-AC02-02851426BE4B} {E85FDB0F-FA81-4CDD-8BF3-865269CE2DB3} = {7BA5A2CE-7992-4F87-9D84-91AE4D046F5A} - {65048104-212A-4819-AECF-89CA9C08C83F} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} - {48430E50-043A-47A2-8278-B86A4420758A} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {3B1B5706-E896-4CEB-A551-E30226303BDB} = {4C43F2DA-59E5-46F5-B691-195449498555} {F8ABEA82-F823-4E9C-96FA-26AF24C932E0} = {30159B0F-BE61-4DB7-AC02-02851426BE4B} + {65048104-212A-4819-AECF-89CA9C08C83F} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {48430E50-043A-47A2-8278-B86A4420758A} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} {531863CA-93A0-42AA-AB5C-FA0E672C03B8} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC} + {BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3} + {27D54041-BDD5-428E-8CA6-C96D519F5451} = {BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8} + {B5A9DEA7-67EC-49E4-924E-4729C34286EC} = {BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8} + {00B1FA38-7D0B-47E4-860C-23490249A4D6} = {BBE020D4-C0FF-41A9-9EB1-D1EE12CC4BB8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C} diff --git a/dotnet/src/dotnetcore/GxClasses/Diagnostics/GxHttpActivitySourceHelper.cs b/dotnet/src/dotnetcore/GxClasses/Diagnostics/GxHttpActivitySourceHelper.cs new file mode 100644 index 000000000..1d88564f2 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Diagnostics/GxHttpActivitySourceHelper.cs @@ -0,0 +1,38 @@ +using System; +using System.Diagnostics; +using System.Reflection; + + +namespace GeneXus.Diagnostics +{ + public static class GxHttpActivitySourceHelper + { + public static string GX_HTTP_INSTRUMENTATION = "GeneXus.Instrumentation.Http"; + private static ActivitySource ActivitySource { get; } = CreateActivitySource(); + + public const string ThreadIdTagName = "thread.id"; + public const string StatusCodeTagName = "otel.status_code"; + + private static ActivitySource CreateActivitySource() + { + Assembly assembly = typeof(GxHttpActivitySourceHelper).Assembly; + string version = assembly.GetCustomAttribute()!.Version; + return new ActivitySource(GX_HTTP_INSTRUMENTATION, version); + } + + public static void SetException(Activity activity, Exception exception) + { + string description = exception.Message; + activity?.SetStatus(ActivityStatusCode.Error, description); + activity?.SetTag(StatusCodeTagName, "ERROR"); + activity?.SetTag("otel.status_description", description); + activity?.AddEvent(new ActivityEvent("exception", tags: new ActivityTagsCollection + { + { "exception.type", exception.GetType().FullName }, + { "exception.message", exception.Message }, + { "exception.source", exception.Source }, + { "exception.stacktrace", exception.ToString() }, + })); + } + } +} diff --git a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj index 548a74a43..d23b9b235 100644 --- a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj +++ b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj @@ -17,6 +17,7 @@ + diff --git a/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs b/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs new file mode 100644 index 000000000..125f05ff5 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Services/OpenTelemetry/OpenTelemetryService.cs @@ -0,0 +1,59 @@ +using System; +using GxClasses.Helpers; +using log4net; + +namespace GeneXus.Services.OpenTelemetry +{ + public interface IOpenTelemetryProvider + { + bool InstrumentAspNetCoreApplication(Microsoft.Extensions.DependencyInjection.IServiceCollection services); + } + + public static class OpenTelemetryService + { + private static readonly ILog log = log4net.LogManager.GetLogger(typeof(OpenTelemetryService)); + private static string OPENTELEMETRY_SERVICE = "Observability"; + public static string GX_ACTIVITY_SOURCE_NAME = "GeneXus.Tracing"; + + private static IOpenTelemetryProvider GetOpenTelemetryProvider() + { + IOpenTelemetryProvider otelProvider = null; + GXService providerService = GXServices.Instance?.Get(OPENTELEMETRY_SERVICE); + + if (providerService != null) + { + try + { + GXLogging.Debug(log, "Loading OpenTelemetry provider:", providerService.ClassName); +#if !NETCORE + Type type = Type.GetType(providerService.ClassName, true, true); +#else + Type type = AssemblyLoader.GetType(providerService.ClassName); +#endif + otelProvider = (IOpenTelemetryProvider)Activator.CreateInstance(type, new object[] { providerService }); + } + catch (Exception e) + { + GXLogging.Error(log, "Couldn´t create OpenTelemetry provider.", e.Message, e); + throw e; + } + } + return otelProvider; + } + + internal static void Setup(Microsoft.Extensions.DependencyInjection.IServiceCollection services) + { + IOpenTelemetryProvider provider = GetOpenTelemetryProvider(); + if (provider != null) + { + bool started = provider.InstrumentAspNetCoreApplication(services); + if (started) + { + log.Info("OpenTelemetry instrumentation started"); + } + } + } + } + + +} diff --git a/dotnet/src/dotnetcore/GxClasses/Services/ServiceSettings.cs b/dotnet/src/dotnetcore/GxClasses/Services/ServiceSettings.cs new file mode 100644 index 000000000..2249548f0 --- /dev/null +++ b/dotnet/src/dotnetcore/GxClasses/Services/ServiceSettings.cs @@ -0,0 +1,94 @@ +using System; +using GeneXus.Encryption; +using log4net; + +namespace GeneXus.Services.Common +{ + public class ServiceSettingsReader + { + static readonly ILog logger = log4net.LogManager.GetLogger(typeof(ServiceSettingsReader)); + + internal GXService service; + public string serviceNameResolver { get; } + public string name { get; } + + public ServiceSettingsReader(string serviceNameResolver, string name, GXService gXService) + { + this.serviceNameResolver = serviceNameResolver; + this.name = name; + this.service = gXService; + } + + public string GetEncryptedPropertyValue(string propertyName) + { + String value = GetEncryptedPropertyValue(propertyName, null); + if (value == null) + { + String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); + logger.Fatal(errorMessage); + throw new Exception(errorMessage); + } + return value; + } + public string GetEncryptedPropertyValue(string propertyName, string defaultValue) + { + String value = GetPropertyValue(propertyName, defaultValue); + if (!String.IsNullOrEmpty(value)) + { + try + { + string ret = String.Empty; + if (CryptoImpl.Decrypt(ref ret, value)) + { + value = ret; + } + } + catch (Exception) + { + logger.Warn($"Could not decrypt property name: {ResolvePropertyName(propertyName)}"); + } + } + return value; + } + + public string GetPropertyValue(string propertyName) + { + String value = GetPropertyValue(propertyName, null); + if (value == null) + { + String errorMessage = String.Format($"Service configuration error - Property name {ResolvePropertyName(propertyName)} must be defined"); + logger.Fatal(errorMessage); + throw new Exception(errorMessage); + } + return value; + } + + public string GetPropertyValue(string propertyName, string defaultValue) + { + String value = null; + value = string.IsNullOrEmpty(value) ? GetPropertyValueImpl(ResolvePropertyName(propertyName)) : value; + value = string.IsNullOrEmpty(value) ? GetPropertyValueImpl(propertyName) : value; + value = string.IsNullOrEmpty(value) ? defaultValue : value; + return value; + } + + internal string GetPropertyValueImpl(string propertyName) + { + String value = null; + if (!string.IsNullOrEmpty(propertyName)) + { + value = Environment.GetEnvironmentVariable(propertyName); + if (service != null && value == null) + { + value = service.Properties.Get(propertyName); + } + } + return value; + } + + internal string ResolvePropertyName(string propertyName) + { + return $"${serviceNameResolver}_{name}_{propertyName}"; + } + } +} diff --git a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs index 01fff17b2..7b6531cb9 100644 --- a/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs +++ b/dotnet/src/dotnetcore/GxNetCoreStartup/Startup.cs @@ -7,6 +7,7 @@ using GeneXus.Http; using GeneXus.HttpHandlerFactory; using GeneXus.Services; +using GeneXus.Services.OpenTelemetry; using GeneXus.Utils; using GxClasses.Web.Middleware; using log4net; @@ -28,6 +29,7 @@ using Microsoft.Extensions.Logging; using StackExchange.Redis; + namespace GeneXus.Application { public class Program @@ -62,7 +64,7 @@ public static void Main(string[] args) Console.Error.WriteLine("ERROR:"); Console.Error.WriteLine("Web Host terminated unexpectedly: {0}", e.Message); Console.Read(); - } + } } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) @@ -135,6 +137,8 @@ public Startup(IConfiguration configuration, IHostingEnvironment env) } public void ConfigureServices(IServiceCollection services) { + OpenTelemetryService.Setup(services); + services.AddMvc(option => option.EnableEndpointRouting = false); services.Configure(options => { @@ -265,7 +269,7 @@ private void ConfigureSessionService(IServiceCollection services, ISessionServic public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env, ILoggerFactory loggerFactory) { string baseVirtualPath = string.IsNullOrEmpty(VirtualPath) ? VirtualPath : $"/{VirtualPath}"; - + LogConfiguration.SetupLog4Net(); var provider = new FileExtensionContentTypeProvider(); //mappings provider.Mappings[".json"] = "application/json"; diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj new file mode 100644 index 000000000..fba026b94 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GeneXus.OpenTelemetry.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + GeneXus.OpenTelemetry + OpenTelemetry GeneXus DotNet + GeneXus.OpenTelemetry.OpenTelemetry + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs new file mode 100644 index 000000000..58da3b8d2 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/GxTraceProviderBuilder.cs @@ -0,0 +1,36 @@ +using GeneXus.Services.OpenTelemetry; +using OpenTelemetry.Trace; + +namespace GeneXus.OpenTelemetry +{ + public static class GxTraceProviderBuilder + { + public static TracerProviderBuilder AddGxAspNetInstrumentation(this TracerProviderBuilder tracer) + { + tracer + .AddAspNetCoreInstrumentation(opt => + { + opt.RecordException = true; + }) + .SetErrorStatusOnException(true) + .AddSource("MySqlConnector") + .AddSource(OpenTelemetryService.GX_ACTIVITY_SOURCE_NAME) + .AddHttpClientInstrumentation(opt => + { + opt.RecordException = true; + }) + .AddAspNetCoreInstrumentation(opt => + { + opt.RecordException = true; + }) + .AddSqlClientInstrumentation(opt => + { + opt.RecordException = true; + opt.EnableConnectionLevelAttributes = true; + opt.SetDbStatementForText = true; + }) + .AddConsoleExporter(); + return tracer; + } + } +} diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs new file mode 100644 index 000000000..7b66e07b6 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetry/OpenTelemetryProvider.cs @@ -0,0 +1,27 @@ +using GeneXus.Services; +using GeneXus.Services.OpenTelemetry; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Trace; + +namespace GeneXus.OpenTelemetry.OpenTelemetry +{ + public class OpenTelemetryProvider : IOpenTelemetryProvider + { + public OpenTelemetryProvider(GXService s) + { + } + + public bool InstrumentAspNetCoreApplication(IServiceCollection services) + { + + services.AddOpenTelemetryTracing(tracerProviderBuilder => + { + tracerProviderBuilder + .AddOtlpExporter() + .AddGxAspNetInstrumentation(); + }); + + return true; + } + } +} \ No newline at end of file diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs new file mode 100644 index 000000000..2d9d10a04 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/AWSOtelProvider.cs @@ -0,0 +1,40 @@ +using System; +using GeneXus.Services.OpenTelemetry; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry; +using OpenTelemetry.Trace; +using OpenTelemetry.Contrib.Extensions.AWSXRay.Trace; +using GeneXus.Services; +using GeneXus.Services.Common; +using GeneXus.Diagnostics; + +namespace GeneXus.OpenTelemetry.AWS +{ + public class AWSOtelProvider : IOpenTelemetryProvider + { + public AWSOtelProvider(GXService s) + { + } + + public bool InstrumentAspNetCoreApplication(IServiceCollection _) + { + string oltpEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT"); + + Sdk.CreateTracerProviderBuilder() + .AddXRayTraceId() // for generating AWS X-Ray compliant trace IDs + .AddOtlpExporter(options => + { + if (!string.IsNullOrEmpty(oltpEndpoint)) + { + options.Endpoint = new Uri(oltpEndpoint); + } + }) + .AddGxAspNetInstrumentation() + .Build(); + + Sdk.SetDefaultTextMapPropagator(new AWSXRayPropagator()); + + return true; + } + } +} \ No newline at end of file diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/GeneXus.OpenTelemetry.AWS.AspNet.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/GeneXus.OpenTelemetry.AWS.AspNet.csproj new file mode 100644 index 000000000..094dfbd46 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryAWSOtel/GeneXus.OpenTelemetry.AWS.AspNet.csproj @@ -0,0 +1,32 @@ + + + + net6.0 + NETCORE; + Properties + false + GeneXus.OpenTelemetry.AWS.AspNet + AWSOtel OpenDistro OpenTelemetry GeneXus + GeneXus.OpenTelemetry.AWS.AspNet + + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/GeneXus.OpenTelemetry.Lightstep.AspNet.csproj b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/GeneXus.OpenTelemetry.Lightstep.AspNet.csproj new file mode 100644 index 000000000..7d62fab6e --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/GeneXus.OpenTelemetry.Lightstep.AspNet.csproj @@ -0,0 +1,29 @@ + + + + net6.0 + NETCORE; + Properties + false + GeneXus.OpenTelemetry.Lightstep.AspNet + Lightstep OpenTelemetry GeneXus DotNet + GeneXus.OpenTelemetry.Lightstep.AspNet + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/LightstepProvider.cs b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/LightstepProvider.cs new file mode 100644 index 000000000..67fa41aa6 --- /dev/null +++ b/dotnet/src/dotnetcore/Providers/OpenTelemetry/OpenTelemetryLightStep/LightstepProvider.cs @@ -0,0 +1,48 @@ + +using System; +using GeneXus.Services.OpenTelemetry; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry; +using OpenTelemetry.Trace; +using OpenTelemetry.Resources; +using log4net; +using GeneXus.Services; + +namespace GeneXus.OpenTelemetry.Lightstep +{ + + public class LightStepOpenTelemetry : IOpenTelemetryProvider + { + private static readonly ILog log = LogManager.GetLogger(typeof(LightStepOpenTelemetry)); + private const string LIGHTSTEP_INGREST_URL = "ingest.lightstep.com:443"; + private const string LIGHTSTEP_ACCESS_TOKEN = "LS_ACCESS_TOKEN"; + + public LightStepOpenTelemetry(GXService s) + { + } + + public bool InstrumentAspNetCoreApplication(IServiceCollection services) + { + string lightstepToken = Environment.GetEnvironmentVariable(LIGHTSTEP_ACCESS_TOKEN); + + if (string.IsNullOrEmpty(lightstepToken)) + { + log.Warn("OpenTelemetry Lightstep was not initialized due to missing 'LS_ACCESS_TOKEN' Environment Variable"); + return false; + } + + services.AddOpenTelemetryTracing(tracerProviderBuilder => + { + tracerProviderBuilder + .AddOtlpExporter(opt => + { + opt.Endpoint = new Uri(LIGHTSTEP_INGREST_URL); + opt.Headers = $"lightstep-access-token=${lightstepToken}"; + }) + .AddGxAspNetInstrumentation(); + }); + + return true; + } + } +} diff --git a/dotnet/src/dotnetframework/GxClasses/Configuration/LogConfiguration.cs b/dotnet/src/dotnetframework/GxClasses/Configuration/LogConfiguration.cs new file mode 100644 index 000000000..43495ecf7 --- /dev/null +++ b/dotnet/src/dotnetframework/GxClasses/Configuration/LogConfiguration.cs @@ -0,0 +1,74 @@ +using System; +using log4net; +using log4net.Appender; +using log4net.Core; +using log4net.Repository; +using log4net.Repository.Hierarchy; +using System.Linq; +using log4net.Layout; + +namespace GeneXus.Configuration +{ + internal class LogConfiguration + { + private static readonly ILog logger = log4net.LogManager.GetLogger(typeof(LogConfiguration)); + + public const string USER_LOG_TOPIC = "GeneXusUserLog"; + private const string LOG_LEVEL_ENVVAR = "GX_LOG_LEVEL"; + private const string LOG_LEVEL_USER_ENVVAR = "GX_LOG_LEVEL_USER"; + private const string LOG_OUTPUT_ENVVAR = "GX_LOG_OUTPUT"; + + public static void SetupLog4Net() + { + SetupLog4NetFromEnvironmentVariables(); + } + + private static void SetupLog4NetFromEnvironmentVariables() + { + string logLevel = Environment.GetEnvironmentVariable(LOG_LEVEL_ENVVAR); + + if (!string.IsNullOrEmpty(logLevel)) + { + Hierarchy h = (Hierarchy)LogManager.GetRepository(); + h.Root.Level = h.LevelMap[logLevel.ToUpper()]; + } + + string userLogLevel = Environment.GetEnvironmentVariable(LOG_LEVEL_USER_ENVVAR); + + if (!string.IsNullOrEmpty(userLogLevel)) + { + ILoggerRepository[] repositories = LogManager.GetAllRepositories(); + foreach (ILoggerRepository repository in repositories) + { + Hierarchy hier = (Hierarchy)repository; + ILogger logger = hier.GetLogger(USER_LOG_TOPIC); + if (logger != null) + { + ((Logger)logger).Level = hier.LevelMap[userLogLevel]; + } + } + } + + string appenderName = Environment.GetEnvironmentVariable(LOG_OUTPUT_ENVVAR); + if (!String.IsNullOrEmpty(appenderName)) + { + Hierarchy h = (Hierarchy) LogManager.GetRepository(); + IAppender appenderToAdd = h.GetAppenders().First(a => a.Name == appenderName); + if (appenderToAdd == null) + { + LogConfiguration.logger.Error($"Appender '{appenderName}' was not found on Log4Net Config file"); + return; + } + + h.Root.AddAppender(appenderToAdd); + ILogger logger = h.GetLogger(USER_LOG_TOPIC); + if (logger != null) + { + ((Logger)logger).AddAppender(appenderToAdd); + } + + } + } + + } +} diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs index 8b116938b..c452e5091 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs @@ -973,7 +973,7 @@ private bool CheckFileExists(string fileName) { string path = Path.Combine(this.GetPhysicalPath(), fileName); fileExists = File.Exists(path); - GXLogging.Info(log, $"Searching if file exists ({fileName}). Found: {fileExists}"); + GXLogging.Debug(log, $"Searching if file exists ({fileName}). Found: {fileExists}"); } catch (Exception e) { diff --git a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs index 7443d0f67..f59aff718 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/gxconfig.cs @@ -80,6 +80,7 @@ public static void ParseArgs(ref string[] args) } } + LogConfiguration.SetupLog4Net(); } private static void RemoveArg(ref string[] args, ref int i) diff --git a/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs b/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs index 02f0bfe0a..17ede87d5 100644 --- a/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs +++ b/dotnet/src/dotnetframework/GxClasses/Diagnostics/Log.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using GeneXus.Attributes; +using GeneXus.Configuration; using log4net; namespace GeneXus.Diagnostics @@ -25,7 +23,7 @@ private enum LogLevel #else static readonly string defaultRepository = LogManager.GetRepository().Name; #endif - public static string defaultUserLogNamespace = Configuration.Config.GetValueOf("USER_LOG_NAMESPACE", "GeneXusUserLog"); + public static string defaultUserLogNamespace = Configuration.Config.GetValueOf("USER_LOG_NAMESPACE", LogConfiguration.USER_LOG_TOPIC); static readonly ILog globalLog = LogManager.GetLogger(defaultRepository, defaultUserLogNamespace); diff --git a/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs b/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs index 7067963fb..9a60d158b 100644 --- a/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs +++ b/dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs @@ -28,6 +28,8 @@ using Jayrock.Json; using Microsoft.Net.Http.Headers; using System.Net.Http; +using System.Diagnostics; +using GeneXus.Diagnostics; namespace GeneXus.Application @@ -698,6 +700,9 @@ string dateTimeToHTMLDate(DateTime dt) } public Task WebException(Exception ex) { +#if NETCORE + GxHttpActivitySourceHelper.SetException(Activity.Current, ex); +#endif GXLogging.Error(log, "WebException", ex); if (ex is FormatException) { diff --git a/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/GeneXus.Programs.Common.csproj b/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/GeneXus.Programs.Common.csproj index 85b46302a..c445f203f 100644 --- a/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/GeneXus.Programs.Common.csproj +++ b/dotnet/src/extensions/Azure/test/GeneXus.Programs.Common/GeneXus.Programs.Common.csproj @@ -2,6 +2,7 @@ net6.0 + false diff --git a/dotnet/src/extensions/Azure/test/apiattractions/apiattractions.csproj b/dotnet/src/extensions/Azure/test/apiattractions/apiattractions.csproj index 12c8a31ed..bb8cc1893 100644 --- a/dotnet/src/extensions/Azure/test/apiattractions/apiattractions.csproj +++ b/dotnet/src/extensions/Azure/test/apiattractions/apiattractions.csproj @@ -4,6 +4,7 @@ net6.0 enable enable + false