diff --git a/Modules.Logging/LogConfig.vb b/Modules.Logging/LogConfig.vb
index e4c88a26..99a6959c 100644
--- a/Modules.Logging/LogConfig.vb
+++ b/Modules.Logging/LogConfig.vb
@@ -71,9 +71,9 @@ Public Class LogConfig
Private Const MAX_ARCHIVE_FILES_DEBUG_DETAIL As Integer = 0
Private Const ARCHIVE_EVERY As FileArchivePeriod = FileArchivePeriod.Day
- Private Const FILE_NAME_FORMAT_DEFAULT As String = "${shortdate}-${var:product}${var:suffix}.log"
- Private Const FILE_NAME_FORMAT_DEBUG As String = "${shortdate}-${var:product}${var:suffix}-Debug.log"
- Private Const FILE_NAME_FORMAT_ERROR As String = "${shortdate}-${var:product}${var:suffix}-Error.log"
+ Private Const FILE_NAME_FORMAT_DEFAULT As String = "${shortdate}-${var:product}${var:suffix}${event-properties:item=ModuleName}.log"
+ Private Const FILE_NAME_FORMAT_DEBUG As String = "${shortdate}-${var:product}${var:suffix}${event-properties:item=ModuleName}-Debug.log"
+ Private Const FILE_NAME_FORMAT_ERROR As String = "${shortdate}-${var:product}${var:suffix}${event-properties:item=ModuleName}-Error.log"
Private Const TARGET_DEFAULT As String = "defaultTarget"
Private Const TARGET_ERROR_EX As String = "errorExceptionTarget"
@@ -93,11 +93,14 @@ Public Class LogConfig
Private Const FILE_NAME_ACCESS_TEST = "accessTest.txt"
Private Const FOLDER_NAME_LOG = "Log"
- Private ReadOnly failSafePath As String = Path.GetTempPath()
- Private ReadOnly basePath As String = failSafePath
+ Private Const FILE_KEEP_RANGE As Integer = 90
+
+ Private ReadOnly _failSafePath As String = Path.GetTempPath()
+ Private ReadOnly _basePath As String = _failSafePath
+
+ Private _config As LoggingConfiguration
+ Private _isDebug As Boolean = False
- Private config As LoggingConfiguration
- Private isDebug As Boolean = False
#End Region
#Region "Public Properties"
Public Enum PathType As Integer
@@ -131,11 +134,10 @@ Public Class LogConfig
''' True, if debug log will be written. False otherwise.
Public Property Debug As Boolean
Get
- Return isDebug
+ Return _isDebug
End Get
Set(isDebug As Boolean)
- Me.isDebug = isDebug
- 'GetLogger().Debug("=> Debug is now {0}", isDebug)
+ _isDebug = isDebug
ReloadConfig(isDebug)
End Set
End Property
@@ -147,19 +149,27 @@ Public Class LogConfig
''' A list of log messages
Public ReadOnly Property Logs As List(Of String)
Get
- Dim oTarget = config.FindTargetByName(Of MemoryTarget)(TARGET_MEMORY)
+ Dim oTarget = _config.FindTargetByName(Of MemoryTarget)(TARGET_MEMORY)
Return oTarget?.Logs.ToList()
End Get
End Property
Public ReadOnly Property NLogConfig As LoggingConfiguration
Get
- Return config
+ Return _config
End Get
End Property
#End Region
+ '''
+ ''' Initializes a new LogConfig object with the options supplied as a LogOptions object
+ '''
+ '''
+ Public Sub New(Options As LogOptions)
+ MyClass.New(Options.LogPath, Options.CustomLogPath, Options.Suffix, Options.CompanyName, Options.ProductName, Options.FileKeepInterval)
+ End Sub
+
'''
''' Initializes a new LogConfig object with a logpath and optinally a filename-suffix.
'''
@@ -168,11 +178,13 @@ Public Class LogConfig
''' If set to anything other than Nothing, extends the logfile name with this suffix.
''' CompanyName is used to construct log-path in when LogPath is set to PathType:AppData
''' ProductName is used to construct log-path in when LogPath is set to PathType:AppData
+ ''' Amount of days where files are kept and not deleted.
Public Sub New(LogPath As PathType,
Optional CustomLogPath As String = Nothing,
Optional Suffix As String = Nothing,
Optional CompanyName As String = Nothing,
- Optional ProductName As String = Nothing)
+ Optional ProductName As String = Nothing,
+ Optional FileKeepRangeInDays As Integer = FILE_KEEP_RANGE)
If LogPath = PathType.AppData And (ProductName Is Nothing Or CompanyName Is Nothing) Then
Throw New ArgumentException("Modules.Logging: PathType is AppData and either CompanyName or ProductName was not supplied!")
@@ -184,26 +196,26 @@ Public Class LogConfig
If LogPath = PathType.AppData Then
Dim appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
- basePath = Path.Combine(appDataDir, CompanyName, ProductName, FOLDER_NAME_LOG)
+ _basePath = Path.Combine(appDataDir, CompanyName, ProductName, FOLDER_NAME_LOG)
ElseIf LogPath = PathType.Temp Then
- basePath = failSafePath
+ _basePath = _failSafePath
Else 'Custom Path
- basePath = CustomLogPath
+ _basePath = CustomLogPath
End If
' If directory does not exist, try to create it!
- If Not Directory.Exists(basePath) Then
+ If Not Directory.Exists(_basePath) Then
Try
- Directory.CreateDirectory(basePath)
+ Directory.CreateDirectory(_basePath)
Catch ex As Exception
' If creation fails, use failSafe path
- basePath = failSafePath
+ _basePath = _failSafePath
End Try
End If
' Try to create a file in `basePath` to check write permissions
Try
- Dim fileAccessPath = Path.Combine(basePath, FILE_NAME_ACCESS_TEST)
+ Dim fileAccessPath = Path.Combine(_basePath, FILE_NAME_ACCESS_TEST)
Using fs As FileStream = File.Create(fileAccessPath)
fs.WriteByte(0)
End Using
@@ -211,7 +223,7 @@ Public Class LogConfig
File.Delete(fileAccessPath)
Catch ex As Exception
' If creation fails, use failSafe path
- basePath = failSafePath
+ _basePath = _failSafePath
End Try
' Set the suffix to the given string if it exists
@@ -228,30 +240,69 @@ Public Class LogConfig
End If
' Create config object and initalize it
- config = GetConfig(oProductName, logFileSuffix)
+ _config = GetConfig(oProductName, logFileSuffix)
' Save config
LogFactory = New LogFactory With {
- .Configuration = config
+ .Configuration = _config
}
' Save log paths for files/directory
- LogDirectory = basePath
+ LogDirectory = _basePath
LogFile = GetCurrentLogFilePath()
+
+ ' Clear old Logfiles as defined in `FileKeepInterval`
+ ClearOldLogfiles(FileKeepRangeInDays)
End Sub
- Private Sub CheckMyApplication()
- Dim oAssembly = Assembly.GetEntryAssembly()
- Dim oMyApp = Nothing
- For Each oType As Type In oAssembly.DefinedTypes
- If oType.Name = "MyApplication" Then
- oMyApp = oType
- Exit For
+ '''
+ ''' Clears old LogFiles from the configured logpath for compliance with the GDPR
+ '''
+ ''' Days in which logfiles should be kept. All files older than `Now - FileKeepInterval` will be deleted.
+ ''' True, if files were deleted as expected or no files were deleted. Otherwise false.
+ Private Function ClearOldLogfiles(FileKeepRange As Integer) As Boolean
+ Dim oClassName As String = GetClassFullName()
+ Dim oLogger As Logger = GetLogger(oClassName)
+
+ Try
+ Dim oContinueOnError = True
+ Dim oUnableToDeleteCounter = 0
+ Dim oDirectory As New DirectoryInfo(LogDirectory)
+ Dim oDateLimit As Date = Date.Now.AddDays(-FileKeepRange)
+ Dim oFiles As List(Of FileInfo) = oDirectory.
+ EnumerateFiles().
+ Where(Function(oFileInfo As FileInfo) oFileInfo.Extension = ".log" And oFileInfo.LastWriteTime < oDateLimit).
+ ToList()
+
+ If oFiles.Count = 0 Then
+ oLogger.Info("No logfiles were marked for deletion in the range [last {0} days].", FileKeepRange)
+ Return True
End If
- Next
- oMyApp.GetType().GetProperty("")
- End Sub
+ oLogger.Info("Deleting [{0}] old logfiles that are marked for deletion in the range [last {1} days].", oFiles.Count, FileKeepRange)
+
+ For Each oFile As FileInfo In oFiles
+ Try
+ oFile.Delete()
+ Catch ex As Exception
+ oUnableToDeleteCounter += 1
+ oLogger.Warn("File {0} could not be deleted!")
+ End Try
+ Next
+
+ If oUnableToDeleteCounter > 0 Then
+ oLogger.Info("Delete old logfiles partially. {0} files could not be deleted.", oUnableToDeleteCounter)
+ Else
+ oLogger.Info("Deleted [{0}] old logfiles.", oFiles.Count)
+ End If
+
+ Return True
+ Catch ex As Exception
+ oLogger.Error(ex)
+
+ Return False
+ End Try
+ End Function
'''
''' Returns the Logger for the calling class
@@ -260,7 +311,25 @@ Public Class LogConfig
Public Function GetLogger() As Logger
Dim oClassName As String = GetClassFullName()
- Return LogFactory.GetLogger(Of Logger)(oClassName)
+ Return GetLogger(oClassName, String.Empty)
+ End Function
+
+ '''
+ ''' Returns the Logger for the specified classname
+ '''
+ ''' An object of Logging.Logger
+
+ Public Function GetLogger(ClassName As String) As Logger
+ Return GetLogger(ClassName, String.Empty)
+ End Function
+
+ '''
+ ''' Returns the Logger for the specified module
+ '''
+ ''' An object of Logging.Logger
+ Public Function GetLoggerWithModule(ModuleName As String) As Logger
+ Dim oClassName As String = GetClassFullName()
+ Return GetLogger(oClassName, ModuleName)
End Function
'''
@@ -269,15 +338,17 @@ Public Class LogConfig
''' The name of the class the logger belongs to
''' An object of Logging.Logger
- Public Function GetLogger(ClassName As String) As Logger
- Return LogFactory.GetLogger(Of Logger)(ClassName)
+ Public Function GetLogger(ClassName As String, ModuleName As String) As Logger
+ Dim oLogger = LogFactory.GetLogger(Of Logger)(ClassName)
+ oLogger.ModuleName = ModuleName
+ Return oLogger
End Function
'''
''' Clears the internal log
'''
Public Sub ClearLogs()
- Dim oTarget = config.FindTargetByName(Of MemoryTarget)(TARGET_MEMORY)
+ Dim oTarget = _config.FindTargetByName(Of MemoryTarget)(TARGET_MEMORY)
oTarget?.Logs.Clear()
End Sub
@@ -336,21 +407,21 @@ Public Class LogConfig
''' The chosen suffix
''' A NLog.LoggingConfiguration object
Private Function GetConfig(productName As String, logFileSuffix As String) As LoggingConfiguration
- config = New LoggingConfiguration()
- config.Variables("product") = productName
- config.Variables("suffix") = logFileSuffix
+ _config = New LoggingConfiguration()
+ _config.Variables("product") = productName
+ _config.Variables("suffix") = logFileSuffix
' Add default targets
- config.AddTarget(TARGET_ERROR_EX, GetErrorExceptionLogTarget(basePath))
- config.AddTarget(TARGET_ERROR, GetErrorLogTarget(basePath))
- config.AddTarget(TARGET_DEFAULT, GetDefaultLogTarget(basePath))
- config.AddTarget(TARGET_DEBUG, GetDebugLogTarget(basePath))
- config.AddTarget(TARGET_MEMORY, GetMemoryDebugTarget())
+ _config.AddTarget(TARGET_ERROR_EX, GetErrorExceptionLogTarget(_basePath))
+ _config.AddTarget(TARGET_ERROR, GetErrorLogTarget(_basePath))
+ _config.AddTarget(TARGET_DEFAULT, GetDefaultLogTarget(_basePath))
+ _config.AddTarget(TARGET_DEBUG, GetDebugLogTarget(_basePath))
+ _config.AddTarget(TARGET_MEMORY, GetMemoryDebugTarget())
' Add default rules
- AddDefaultRules(config)
+ AddDefaultRules(_config)
- Return config
+ Return _config
End Function
'''
@@ -371,7 +442,7 @@ Public Class LogConfig
''' Full path of the current default log file
Private Function GetCurrentLogFilePath()
Dim logEventInfo As New LogEventInfo() With {.TimeStamp = Date.Now}
- Dim target As FileTarget = config.FindTargetByName(TARGET_DEFAULT)
+ Dim target As FileTarget = _config.FindTargetByName(TARGET_DEFAULT)
Dim fileName As String = target.FileName.Render(logEventInfo)
Return fileName
@@ -383,15 +454,15 @@ Public Class LogConfig
''' Adds the Debug rule if true.
Private Sub ReloadConfig(Optional Debug As Boolean = False)
' Clear Logging Rules
- config.LoggingRules.Clear()
+ _config.LoggingRules.Clear()
' Add default rules
- AddDefaultRules(config)
+ AddDefaultRules(_config)
' Add debug rule, if configured
If Debug Then
- config.AddRuleForAllLevels(TARGET_DEBUG)
- config.AddRuleForAllLevels(TARGET_MEMORY)
+ _config.AddRuleForAllLevels(TARGET_DEBUG)
+ _config.AddRuleForAllLevels(TARGET_MEMORY)
End If
' Reload all running loggers
diff --git a/Modules.Logging/LogOptions.vb b/Modules.Logging/LogOptions.vb
new file mode 100644
index 00000000..98c42933
--- /dev/null
+++ b/Modules.Logging/LogOptions.vb
@@ -0,0 +1,10 @@
+Imports DigitalData.Modules.Logging.LogConfig
+
+Public Class LogOptions
+ Property LogPath As PathType
+ Property CustomLogPath As String = Nothing
+ Property Suffix As String = Nothing
+ Property CompanyName As String = Nothing
+ Property ProductName As String = Nothing
+ Property FileKeepInterval As Integer = 0
+End Class
diff --git a/Modules.Logging/Logger.vb b/Modules.Logging/Logger.vb
index c4d978b1..42167a74 100644
--- a/Modules.Logging/Logger.vb
+++ b/Modules.Logging/Logger.vb
@@ -4,6 +4,59 @@ Public Class Logger
Inherits NLog.Logger
Implements ILogger
+ '''
+ ''' Optional ModuleName to create different LogFiles for different Modules (arbitrary entities, User, File, etc.)
+ '''
+ ''' The current ModuleName of the logger, if any.
+ Public Property ModuleName As String = Nothing
+
+ Public Overloads Sub Info(Message As String)
+ LogWithModuleName(Message, LogLevel.Info)
+ End Sub
+
+ Public Overloads Sub Warn(Message As String)
+ LogWithModuleName(Message, LogLevel.Warn)
+ End Sub
+
+ Public Overloads Sub [Error](Exception As Exception)
+ LogWithModuleName(Exception)
+ End Sub
+
+ Public Overloads Sub Debug(Message As String)
+ LogWithModuleName(Message, LogLevel.Debug)
+ End Sub
+
+ Public Overloads Sub Trace(Message As String)
+ LogWithModuleName(Message, LogLevel.Trace)
+ End Sub
+
+ Private Sub LogWithModuleName(Exception As Exception)
+ Dim oEventInfo As New LogEventInfo() With {
+ .Exception = Exception,
+ .Level = LogLevel.Error,
+ .LoggerName = Name,
+ .Message = Exception.Message
+ }
+ oEventInfo = MaybeSetModuleName(oEventInfo)
+
+ Log(oEventInfo)
+ End Sub
+
+ Private Sub LogWithModuleName(Message As String, LogLevel As LogLevel)
+ Dim oEventInfo As New LogEventInfo(LogLevel, Name, Message)
+ oEventInfo = MaybeSetModuleName(oEventInfo)
+
+ Log(oEventInfo)
+ End Sub
+
+ Private Function MaybeSetModuleName(LogEventInfo As LogEventInfo) As LogEventInfo
+ If ModuleName IsNot Nothing AndAlso ModuleName <> String.Empty Then
+ LogEventInfo.Properties.Item("ModuleName") = ModuleName
+ End If
+
+ Return LogEventInfo
+ End Function
+
'''
''' Prints a preformatted Block including a block identifier
'''
diff --git a/Modules.Logging/Logging.vbproj b/Modules.Logging/Logging.vbproj
index ad3e27ef..3281a6f9 100644
--- a/Modules.Logging/Logging.vbproj
+++ b/Modules.Logging/Logging.vbproj
@@ -74,6 +74,7 @@
+
True