diff --git a/dotnet/DotNetStandardClasses.sln b/dotnet/DotNetStandardClasses.sln
index 6b41cd91d..037774d6b 100644
--- a/dotnet/DotNetStandardClasses.sln
+++ b/dotnet/DotNetStandardClasses.sln
@@ -104,6 +104,9 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXAzureStorage", "src\dotnetcore\Providers\Storage\GXAzureStorage\GXAzureStorage.csproj", "{BF72FAF5-3A7C-41B6-A27F-9BE049290356}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC}"
+ ProjectSection(SolutionItems) = preProject
+ src\dotnetcore\GxNetCoreStartup\Startup.cs = src\dotnetcore\GxNetCoreStartup\Startup.cs
+ EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GxEncryptCMD", "src\dotnetframework\GxEncryptCMD\GxEncryptCMD.csproj", "{E2CC404A-3AE4-4CE9-84DC-0EA6078F69E3}"
EndProject
@@ -255,6 +258,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GXAzureEventGrid", "src\dot
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCoreAttackMitigationTest", "test\DotNetCoreAttackMitigationTest\DotNetCoreAttackMitigationTest.csproj", "{2D615969-53E2-4B77-9A9A-75C33865CF76}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gxlogging", "gxlogging", "{AA04A8A9-3CBD-431D-9711-C4D481E4A1E5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeneXus.Log.Azure.AzureAppInsights", "src\dotnetcore\Providers\Logging\GeneXus.Log.Azure.AzureAppInsights\GeneXus.Log.Azure.AzureAppInsights.csproj", "{6B28F942-D355-44A2-9C5D-92AF2D256A0D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -613,6 +620,10 @@ Global
{2D615969-53E2-4B77-9A9A-75C33865CF76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D615969-53E2-4B77-9A9A-75C33865CF76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D615969-53E2-4B77-9A9A-75C33865CF76}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6B28F942-D355-44A2-9C5D-92AF2D256A0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B28F942-D355-44A2-9C5D-92AF2D256A0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B28F942-D355-44A2-9C5D-92AF2D256A0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B28F942-D355-44A2-9C5D-92AF2D256A0D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -729,6 +740,8 @@ Global
{5BBC75F0-E51A-4EBD-A628-92498D319B1D} = {4C43F2DA-59E5-46F5-B691-195449498555}
{7250CDB1-95C4-4822-B01B-3CBD73324CC9} = {30159B0F-BE61-4DB7-AC02-02851426BE4B}
{2D615969-53E2-4B77-9A9A-75C33865CF76} = {1D6F1776-FF4B-46C2-9B3D-BC46CCF049DC}
+ {AA04A8A9-3CBD-431D-9711-C4D481E4A1E5} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3}
+ {6B28F942-D355-44A2-9C5D-92AF2D256A0D} = {AA04A8A9-3CBD-431D-9711-C4D481E4A1E5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C}
diff --git a/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj b/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj
index 3227ecb70..9cc233113 100644
--- a/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj
+++ b/dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj
@@ -30,6 +30,8 @@
+
+
diff --git a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj
index 41c7ff2ad..bb08809b2 100644
--- a/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj
+++ b/dotnet/src/dotnetcore/GxClasses/GxClasses.csproj
@@ -158,6 +158,8 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs b/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs
new file mode 100644
index 000000000..774047c67
--- /dev/null
+++ b/dotnet/src/dotnetcore/GxClasses/Services/LogService/GXLogService.cs
@@ -0,0 +1,48 @@
+using System;
+using GeneXus.Application;
+using GxClasses.Helpers;
+using Microsoft.Extensions.Logging;
+
+namespace GeneXus.Services.Log
+{
+ public interface IGXLogProvider : ILoggerFactory
+ {
+ ILoggerFactory GetLoggerFactory();
+ }
+
+ public static class GXLogService
+ {
+ private static string LOG_SERVICE = "Log";
+
+ public static ILoggerFactory GetLogFactory()
+ {
+ IGXLogProvider gxLogProvider = null;
+ GXService providerService = GXServices.Instance?.Get(LOG_SERVICE);
+
+ if (providerService != null)
+ {
+ try
+ {
+#if !NETCORE
+ Type type = Type.GetType(providerService.ClassName, true, true);
+#else
+ Type type = AssemblyLoader.GetType(providerService.ClassName);
+#endif
+ gxLogProvider = (IGXLogProvider)Activator.CreateInstance(type, new object[] { providerService });
+ return gxLogProvider.GetLoggerFactory();
+ }
+ catch (Exception e)
+ {
+ throw e;
+ }
+ }
+ else
+ {
+ string log4net_config = GxContext.IsHttpContext ? "log.config" : "log.console.config";
+ Log4NetProvider log4NetProvider = new Log4NetProvider(log4net_config);
+ return LoggerFactory.Create(builder => builder.AddProvider(log4NetProvider));
+ }
+
+ }
+ }
+}
diff --git a/dotnet/src/dotnetcore/Providers/Logging/GeneXus.Log.Azure.AzureAppInsights/AzureAppInsightsLogProvider.cs b/dotnet/src/dotnetcore/Providers/Logging/GeneXus.Log.Azure.AzureAppInsights/AzureAppInsightsLogProvider.cs
new file mode 100644
index 000000000..7bc3de514
--- /dev/null
+++ b/dotnet/src/dotnetcore/Providers/Logging/GeneXus.Log.Azure.AzureAppInsights/AzureAppInsightsLogProvider.cs
@@ -0,0 +1,51 @@
+using System;
+using GeneXus.Services;
+using GeneXus.Services.Log;
+using Microsoft.Extensions.Logging;
+
+namespace GeneXus.Log.Azure
+{
+ public class AzureAppInsightsLogProvider : IGXLogProvider
+ {
+ private static string APPLICATIONINSIGHTS_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING";
+
+ public static ILoggerFactory loggerFactory;
+
+ public AzureAppInsightsLogProvider(GXService s) { }
+ public ILoggerFactory GetLoggerFactory()
+ {
+ string appInsightsConnection = Environment.GetEnvironmentVariable(APPLICATIONINSIGHTS_CONNECTION_STRING);
+ if (appInsightsConnection != null) {
+ loggerFactory = LoggerFactory.Create(builder => builder.AddApplicationInsights(
+
+ configureTelemetryConfiguration: (config) =>
+ config.ConnectionString = appInsightsConnection,
+ configureApplicationInsightsLoggerOptions: (options) => { }
+ )
+ );
+
+ }
+ else
+ {
+ throw new ArgumentNullException("APPLICATIONINSIGHTS_CONNECTION_STRING","Application Insight Log could not be initialized due to missing APPLICATIONINSIGHTS_CONNECTION_STRING environment variable.");
+ }
+ return loggerFactory;
+ }
+
+ public void AddProvider(ILoggerProvider provider)
+ {
+ loggerFactory.AddProvider(provider);
+ }
+
+ public void Dispose()
+ {
+ loggerFactory.Dispose();
+ }
+
+ public ILogger CreateLogger(string name)
+ {
+ return loggerFactory.CreateLogger(name);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/dotnet/src/dotnetcore/Providers/Logging/GeneXus.Log.Azure.AzureAppInsights/GeneXus.Log.Azure.AzureAppInsights.csproj b/dotnet/src/dotnetcore/Providers/Logging/GeneXus.Log.Azure.AzureAppInsights/GeneXus.Log.Azure.AzureAppInsights.csproj
new file mode 100644
index 000000000..06128b5db
--- /dev/null
+++ b/dotnet/src/dotnetcore/Providers/Logging/GeneXus.Log.Azure.AzureAppInsights/GeneXus.Log.Azure.AzureAppInsights.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net6.0
+ NETCORE;
+ Properties
+ false
+ GeneXus.Log.Azure.AzureAppInsights
+ Azure Application Insights Log GeneXus
+ GeneXus.Log.Azure.AzureAppInsights
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj b/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj
index 1e437fe5a..a719b8d79 100644
--- a/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj
+++ b/dotnet/src/dotnetframework/GxClasses/GxClasses.csproj
@@ -8,8 +8,13 @@
Data Access
GeneXus.Classes
+
+
+
+
+
diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs
index d4a68765e..9b6f1e4cd 100644
--- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs
+++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXLogging.cs
@@ -1,22 +1,41 @@
using System;
+using System.Collections.Concurrent;
+using System.IO;
using System.Reflection;
using System.Text;
+using GeneXus.Services.Log;
using log4net;
+using log4net.Config;
using log4net.Core;
+using Microsoft.Extensions.Logging;
+using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace GeneXus
{
-
- public static class GXLogging
+ public static class GXLogging
{
+
+ private static Microsoft.Extensions.Logging.ILoggerFactory _instance = GXLogService.GetLogFactory();
+
+ public static ILogger GetLogger() where Type : class
+ {
+ return _instance.CreateLogger();
+ }
+
public static void Trace(this ILog log, params string[] list)
{
if (log.Logger.IsEnabledFor(Level.Trace))
{
- log.Logger.Log(MethodBase.GetCurrentMethod().DeclaringType, Level.Trace, String.Join(" ", list), null);
+ log.Logger.Log(MethodBase.GetCurrentMethod().DeclaringType, Level.Trace, string.Join(" ", list), null);
}
}
- public static void Error(ILog log, string msg, Exception ex)
+
+ public static void Trace(ILogger log, params string[] list)
+ {
+ if (log.IsEnabled(LogLevel.Trace))
+ log.LogTrace(string.Join(" ", list));
+ }
+ public static void Error(ILog log, string msg, Exception ex)
{
if (log.IsErrorEnabled)
{
@@ -24,6 +43,14 @@ public static void Error(ILog log, string msg, Exception ex)
}
}
+ public static void Error(ILogger log, string msg, Exception ex)
+ {
+ if (log.IsEnabled(LogLevel.Error))
+ {
+ log.LogError(msg, ex);
+ }
+ }
+
public static void ErrorSanitized(ILog log, string msg, Exception ex)
{
if (log.IsErrorEnabled)
@@ -32,24 +59,54 @@ public static void ErrorSanitized(ILog log, string msg, Exception ex)
}
}
+ public static void ErrorSanitized(ILogger log, string msg, Exception ex)
+ {
+ if (log.IsEnabled(LogLevel.Error))
+ {
+ log.LogError(Utils.StringUtil.Sanitize(msg, Utils.StringUtil.LogUserEntryWhiteList), ex);
+ }
+ }
+
public static void Error(ILog log, string msg1, string msg2, Exception ex)
{
Error(log, msg1 + msg2, ex);
}
- public static void Error(ILog log, Exception ex, params string[] list)
+
+ public static void Error(ILogger log, string msg1, string msg2, Exception ex)
+ {
+ Error(log, msg1 + msg2, ex);
+ }
+ public static void Error(ILog log, Exception ex, params string[] list)
{
- if (log.IsErrorEnabled){
- foreach (string parm in list){
+ if (log.IsErrorEnabled)
+ {
+ foreach (string parm in list)
+ {
log.Error(parm);
}
}
}
- public static void Error(ILog log, params string[] list)
+ public static void Error(ILogger log, Exception ex, params string[] list)
+ {
+ if (log.IsEnabled(LogLevel.Error))
+ {
+ foreach (string parm in list)
+ {
+ log.LogError(parm);
+ }
+ }
+ }
+ public static void Error(ILog log, params string[] list)
+ {
+ Error(log, null, list);
+ }
+
+ public static void Error(ILogger log, params string[] list)
{
Error(log, null, list);
}
- public static void Warn(ILog log, Exception ex, params string[] list)
+ public static void Warn(ILog log, Exception ex, params string[] list)
{
if (log.IsWarnEnabled)
{
@@ -64,18 +121,48 @@ public static void Warn(ILog log, Exception ex, params string[] list)
log.Warn(msg);
}
}
- public static void Warn(ILog log, params string[] list)
+
+ public static void Warn(ILogger log, Exception ex, params string[] list)
+ {
+ if (log.IsEnabled(LogLevel.Warning))
+ {
+ StringBuilder msg = new StringBuilder();
+ foreach (string parm in list)
+ {
+ msg.Append(parm);
+ }
+ if (ex != null)
+ log.LogWarning(exception:ex, message:msg.ToString());
+ else
+ log.LogWarning(message:msg.ToString());
+ }
+ }
+ public static void Warn(ILog log, params string[] list)
+ {
+ Warn(log, null, list);
+ }
+
+ public static void Warn(ILogger log, params string[] list)
{
Warn(log, null, list);
}
- public static void Warn(ILog log, string msg, Exception ex)
+
+ public static void Warn(ILog log, string msg, Exception ex)
{
if (log.IsWarnEnabled)
{
log.Warn(msg, ex);
}
}
- public static void Debug(ILog log, Exception ex, params string[] list)
+ public static void Warn(ILogger log, string msg, Exception ex)
+ {
+ if (log.IsEnabled(LogLevel.Warning))
+ {
+ log.LogWarning
+ (exception:ex,msg);
+ }
+ }
+ public static void Debug(ILog log, Exception ex, params string[] list)
{
if (log.IsDebugEnabled)
{
@@ -90,6 +177,21 @@ public static void Debug(ILog log, Exception ex, params string[] list)
log.Debug(msg);
}
}
+ public static void Debug(ILogger log, Exception ex, params string[] list)
+ {
+ if (log.IsEnabled(LogLevel.Debug))
+ {
+ StringBuilder msg = new StringBuilder();
+ foreach (string parm in list)
+ {
+ msg.Append(parm);
+ }
+ if (ex != null)
+ log.LogDebug(exception :ex, message: msg.ToString());
+ else
+ log.LogDebug(message: msg.ToString());
+ }
+ }
public static void DebugSanitized(ILog log, Exception ex, params string[] list)
{
@@ -107,17 +209,40 @@ public static void DebugSanitized(ILog log, Exception ex, params string[] list)
}
}
-
+ public static void DebugSanitized(ILogger log, Exception ex, params string[] list)
+ {
+ if (log.IsEnabled(LogLevel.Debug))
+ {
+ StringBuilder msg = new StringBuilder();
+ foreach (string parm in list)
+ {
+ msg.Append(Utils.StringUtil.Sanitize(parm, Utils.StringUtil.LogUserEntryWhiteList));
+ }
+ if (ex != null)
+ log.LogDebug(exception:ex,message:msg.ToString());
+ else
+ log.LogDebug(msg.ToString());
+ }
+ }
public static void Debug(ILog log, params string[] list)
{
Debug(log, null, list);
}
+ public static void Debug(ILogger log, params string[] list)
+ {
+ Debug(log, null, list);
+ }
+
public static void DebugSanitized(ILog log, params string[] list)
{
DebugSanitized(log, null, list);
}
+ public static void DebugSanitized(ILogger log, params string[] list)
+ {
+ DebugSanitized(log, null, list);
+ }
public static void Debug(ILog log, string startMsg, Func buildMsg)
{
@@ -127,18 +252,38 @@ public static void Debug(ILog log, string startMsg, Func buildMsg)
log.Debug(startMsg + msg);
}
}
- public static void Debug(ILog log, string msg1, string msg2, Exception ex)
+ public static void Debug(ILogger log, string startMsg, Func buildMsg)
+ {
+ if (log.IsEnabled(LogLevel.Debug))
+ {
+ string msg = buildMsg();
+ log.LogDebug(startMsg + msg);
+ }
+ }
+ public static void Debug(ILog log, string msg1, string msg2, Exception ex)
+ {
+ Debug(log, msg1 + msg2, ex);
+ }
+
+ public static void Debug(ILogger log, string msg1, string msg2, Exception ex)
{
Debug(log, msg1 + msg2, ex);
}
- public static void Debug(ILog log, string msg, Exception ex)
+ public static void Debug(ILog log, string msg, Exception ex)
{
if (log.IsDebugEnabled)
{
log.Debug(msg, ex);
}
}
- public static void Info(ILog log, params string[] list)
+ public static void Debug(ILogger log, string msg, Exception ex)
+ {
+ if (log.IsEnabled(LogLevel.Debug))
+ {
+ log.LogDebug(exception:ex,msg);
+ }
+ }
+ public static void Info(ILog log, params string[] list)
{
if (log.IsInfoEnabled)
{
@@ -150,6 +295,147 @@ public static void Info(ILog log, params string[] list)
log.Info(msg);
}
}
+ public static void Info(ILogger log, params string[] list)
+ {
+ if (log.IsEnabled(LogLevel.Information))
+ {
+ StringBuilder msg = new StringBuilder();
+ foreach (string parm in list)
+ {
+ msg.Append(parm);
+ }
+ log.LogInformation(msg.ToString());
+ }
+ }
+ public static void Info(ILogger log, string msg, params string[] list)
+ {
+ if (log.IsEnabled(LogLevel.Information))
+ {
+ log.LogInformation(msg.ToString(),list);
+ }
+ }
+ }
+ internal class Log4NetProvider : ILoggerProvider
+ {
+ private readonly string _log4NetConfigFile;
+
+ private readonly ConcurrentDictionary _loggers =
+ new ConcurrentDictionary();
+
+ internal Log4NetProvider(string log4NetConfigFile)
+ {
+ _log4NetConfigFile = log4NetConfigFile;
+ }
+
+ public ILogger CreateLogger(string categoryName)
+ {
+ return _loggers.GetOrAdd(categoryName, CreateLoggerImplementation);
+ }
+
+ public void Dispose()
+ {
+ _loggers.Clear();
+ }
+
+ private ILogger CreateLoggerImplementation(string name)
+ {
+ return new Log4NetLogger(name, new FileInfo(_log4NetConfigFile));
+ }
+ }
+ internal class Log4NetLogger : ILogger
+ {
+ private readonly string _name;
+
+ private readonly ILog _log;
+
+ private log4net.Repository.ILoggerRepository _loggerRepository;
+
+ public Log4NetLogger(string name, FileInfo fileInfo)
+ {
+ _name = name;
+ _loggerRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
+
+ _log = LogManager.GetLogger(_loggerRepository.Name, name);
+ XmlConfigurator.ConfigureAndWatch(_loggerRepository, fileInfo);
+
+ if (_log.IsDebugEnabled) {
+ _log.Debug($"log4net configured with {fileInfo.FullName}");
+ }
+
+ }
+
+ public IDisposable BeginScope(TState state)
+ {
+ return null;
+ }
+
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ switch (logLevel)
+ {
+ case LogLevel.Critical:
+ return _log.IsFatalEnabled;
+ case LogLevel.Debug:
+ case LogLevel.Trace:
+ return _log.IsDebugEnabled;
+ case LogLevel.Error:
+ return _log.IsErrorEnabled;
+ case LogLevel.Information:
+ return _log.IsInfoEnabled;
+ case LogLevel.Warning:
+ return _log.IsWarnEnabled;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(logLevel));
+ }
+ }
+
+ public void Log(
+ LogLevel logLevel,
+ EventId eventId,
+ TState state,
+ Exception exception,
+ Func formatter)
+ {
+ if (!IsEnabled(logLevel))
+ {
+ return;
+ }
+
+ if (formatter == null)
+ {
+ throw new ArgumentNullException(nameof(formatter));
+ }
+
+ string message = $"{formatter(state, exception)} {exception}";
+ if (!string.IsNullOrEmpty(message) || exception != null)
+ {
+ switch (logLevel)
+ {
+ case LogLevel.Critical:
+ _log.Fatal(message);
+ break;
+ case LogLevel.Debug:
+ case LogLevel.Trace:
+ _log.Debug(message);
+ break;
+ case LogLevel.Error:
+ _log.Error(message);
+ break;
+ case LogLevel.Information:
+ _log.Info(message);
+ break;
+ case LogLevel.Warning:
+ _log.Warn(message);
+ break;
+ default:
+ _log.Warn($"Encountered unknown log level {logLevel}, writing out as Info.");
+ _log.Info(message, exception);
+ break;
+ }
+ }
+ }
}
+
}
+