MONSTER: Rename Monorepo to Modules, only keep Projects under Modules.*

This commit is contained in:
Jonathan Jenne
2022-09-29 13:46:00 +02:00
parent e87b97bfec
commit 042bbce9f4
1557 changed files with 380 additions and 160017 deletions

590
Logging/LogConfig.vb Normal file
View File

@@ -0,0 +1,590 @@
Imports System.IO
Imports System.Reflection
Imports NLog
Imports NLog.Config
Imports NLog.Targets
''' <module>LogConfig</module>
''' <version>0.0.1.0</version>
''' <date>02.10.2018</date>
''' <summary>
''' Module that writes file-logs to different locations:
''' local application data, the current directory or a custom path.
''' Files and directories will be automatically created.
''' </summary>
''' <dependencies>
''' NLog, >= 4.5.8
''' </dependencies>
''' <example>
''' Imports DigitalData.Modules.Logging
'''
''' Class FooProgram
''' Private Logger as Logger
''' Private LogConfig as LogConfig
'''
''' Public Sub New()
''' LogConfig = new LogConfig(args)
''' Logger = LogConfig.GetLogger()
''' End Sub
'''
''' Public Sub Bar()
''' Logger.Info("Baz")
''' End Sub
''' End Class
'''
''' Class FooLib
''' Private Logger as NLog.Logger
'''
''' Public Sub New(LogConfig as LogConfig)
''' Logger = LogConfig.GetLogger()
''' End Sub
'''
''' Public Sub Bar()
''' Logger.Info("Baz")
''' End Sub
''' End Class
''' </example>
''' <remarks>
''' If logpath can not be written to, falls back to temp folder as defined in:
''' https://docs.microsoft.com/de-de/dotnet/api/system.io.path.gettemppath?view=netframework-4.7.2
'''
''' If used in a service, LogPath must be set to CustomPath, otherwise the Log will be written to System32!
'''
''' For NLog Troubleshooting, set the following Environment variables to write the NLog internal Log:
''' - NLOG_INTERNAL_LOG_LEVEL: Debug
''' - NLOG_INTERNAL_LOG_FILE: ex. C:\Temp\Nlog_Internal.log
''' </remarks>
Public Class LogConfig
#Region "Private Properties"
Private Const OPEN_FILE_CACHE_TIMEOUT As Integer = 30
Private Const OPEN_FILE_FLUSH_TIMEOUT As Integer = 5
Private Const AUTO_FLUSH As Boolean = False
Private Const KEEP_FILES_OPEN As Boolean = False
Private Const KEEP_FILES_OPEN_DEBUG As Boolean = True
' MAX_ARCHIVES_FILES works like this (in version 4.5.8):
' 0 = keep ALL archives files
' 1 = only keep latest logfile and NO archive files
' n = keep n archive files
Private Const MAX_ARCHIVE_FILES_DEFAULT As Integer = 0
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}${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_TRACE As String = "${shortdate}-${var:product}${var:suffix}${event-properties:item=ModuleName}-Trace.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"
Private Const TARGET_ERROR As String = "errorTarget"
Private Const TARGET_DEBUG As String = "debugTarget"
Private Const TARGET_TRACE As String = "traceTarget"
'Private Const TARGET_MEMORY As String = "memoryTarget"
Private Const LOG_FORMAT_BASE As String = "${time}|${logger:shortName=True}|${level:uppercase=true}"
Private Const LOG_FORMAT_CALLSITE As String = "${callsite:className=false:fileName=true:includeSourcePath=false:methodName=true}"
Private Const LOG_FORMAT_EXCEPTION As String = "${exception:format=Message,StackTrace:innerFormat=Message:maxInnerExceptionLevel=3}"
Private Const LOG_FORMAT_DEFAULT As String = LOG_FORMAT_BASE & " >> ${message}"
Private Const LOG_FORMAT_ERROR As String = LOG_FORMAT_BASE & " >> " & LOG_FORMAT_EXCEPTION
Private Const LOG_FORMAT_DEBUG As String = LOG_FORMAT_BASE & " >> " & LOG_FORMAT_CALLSITE & " -> " & "${message}"
Private Const FILE_NAME_ACCESS_TEST = "accessTest.txt"
Private Const FOLDER_NAME_LOG = "Log"
Private Const FILE_KEEP_RANGE As Integer = 30
'Private Const MAX_MEMORY_LOG_COUNT As Integer = 1000
Private ReadOnly _failSafePath As String = Path.GetTempPath()
Private ReadOnly _basePath As String = _failSafePath
Private _config As LoggingConfiguration
Private _isDebug As Boolean = False
Private _isTrace As Boolean = False
#End Region
#Region "Public Properties"
Public Enum PathType As Integer
AppData = 0
CurrentDirectory = 1
CustomPath = 2
Temp = 3
End Enum
''' <summary>
''' Returns the NLog.LogFactory object that is used to create Loggers
''' </summary>
''' <returns>LogFactory object</returns>
Public ReadOnly Property LogFactory As LogFactory
''' <summary>
''' Returns the path to the current default logfile
''' </summary>
''' <returns>Filepath to the logfile</returns>
Public ReadOnly Property LogFile As String
''' <summary>
''' Returns the path to the current log directory
''' </summary>
''' <returns>Directory path to the log directory</returns>
Public ReadOnly Property LogDirectory As String
''' <summary>
''' Determines if a debug log will be written
''' </summary>
''' <returns>True, if debug log will be written. False otherwise.</returns>
Public Property Debug As Boolean
Get
Return _isDebug
End Get
Set(isDebug As Boolean)
_isDebug = isDebug
ReloadConfig(isDebug, _isTrace)
End Set
End Property
Public Property Trace As Boolean
Get
Return _isTrace
End Get
Set(isTrace As Boolean)
_isTrace = isTrace
ReloadConfig(_isDebug, isTrace)
End Set
End Property
''' <summary>
''' Returns Logs in Memory as List(Of String) if Debug is enabled
''' Returns an empty list if debug is disabled
''' </summary>
''' <returns>A list of log messages</returns>
Public ReadOnly Property Logs As List(Of String)
Get
'Dim oTarget = _config.FindTargetByName(Of MemoryTarget)(TARGET_MEMORY)
'Return oTarget?.Logs.ToList()
Return New List(Of String)
End Get
End Property
Public ReadOnly Property NLogConfig As LoggingConfiguration
Get
Return _config
End Get
End Property
#End Region
''' <summary>
''' Initializes a new LogConfig object with the options supplied as a LogOptions object
''' </summary>
''' <param name="Options"></param>
Public Sub New(Options As LogOptions)
MyClass.New(Options.LogPath, Options.CustomLogPath, Options.Suffix, Options.CompanyName, Options.ProductName, Options.FileKeepInterval)
End Sub
''' <summary>
''' Initializes a new LogConfig object with a logpath and optinally a filename-suffix.
''' </summary>
''' <param name="LogPath">The basepath to write logs to. Can be AppData, CurrentDirectory or CustomPath.</param>
''' <param name="CustomLogPath">If `logPath` is set to custom, this defines the custom logPath.</param>
''' <param name="Suffix">If set to anything other than Nothing, extends the logfile name with this suffix.</param>
''' <param name="CompanyName">CompanyName is used to construct log-path in when LogPath is set to PathType:AppData</param>
''' <param name="ProductName">ProductName is used to construct log-path in when LogPath is set to PathType:AppData</param>
''' <param name="FileKeepRangeInDays">Amount of days where files are kept and not deleted.</param>
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 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!")
End If
If LogPath = PathType.CurrentDirectory Then
Throw New ArgumentException("Modules.Logging: LogPath.CurrentDirectory is deprecated. Please use LogPath.CustomPath!")
End If
If LogPath = PathType.AppData Then
Dim appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
_basePath = Path.Combine(appDataDir, CompanyName, ProductName, FOLDER_NAME_LOG)
ElseIf LogPath = PathType.Temp Then
_basePath = _failSafePath
Else 'Custom Path
_basePath = CustomLogPath
End If
' If directory does not exist, try to create it!
If Not Directory.Exists(_basePath) Then
Try
Directory.CreateDirectory(_basePath)
Catch ex As Exception
' If creation fails, use failSafe path
_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)
Using fs As FileStream = File.Create(fileAccessPath)
fs.WriteByte(0)
End Using
File.Delete(fileAccessPath)
Catch ex As Exception
' If creation fails, use failSafe path
_basePath = _failSafePath
End Try
' Set the suffix to the given string if it exists
Dim logFileSuffix As String = String.Empty
If Suffix IsNot Nothing AndAlso Suffix.Count > 0 Then
logFileSuffix = $"-{Suffix}"
End If
Dim oProductName As String = "Main"
If ProductName IsNot Nothing Then
oProductName = ProductName
End If
' Create config object and initalize it
_config = GetConfig(oProductName, logFileSuffix)
' Save config
LogFactory = New LogFactory With {
.Configuration = _config
}
' Save log paths for files/directory
LogDirectory = _basePath
LogFile = GetCurrentLogFilePath()
Dim oLogger = GetLogger()
oLogger.Info("Logging started for [{0}{1}] in [{2}]", oProductName, logFileSuffix, LogFile)
oLogger.Info("Logging Version [{0}]", Assembly.GetExecutingAssembly().GetName().Version)
' Clear old Logfiles as defined in `FileKeepInterval`
ClearOldLogfiles(FileKeepRangeInDays)
End Sub
''' <summary>
''' Clears old LogFiles from the configured logpath for compliance with the GDPR
''' </summary>
''' <param name="FileKeepRange">Days in which logfiles should be kept. All files older than `Now - FileKeepInterval` will be deleted.</param>
''' <returns>True, if files were deleted as expected or no files were deleted. Otherwise false.</returns>
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
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
''' <summary>
''' Returns the Logger for the calling class
''' </summary>
''' <returns>An object of Logging.Logger</returns>
<DebuggerStepThrough()>
Public Function GetLogger() As Logger
Dim oClassName As String = GetClassFullName()
Return GetLogger(oClassName, String.Empty)
End Function
''' <summary>
''' Returns the Logger for the specified classname
''' </summary>
''' <returns>An object of Logging.Logger</returns>
<DebuggerStepThrough()>
Public Function GetLogger(ClassName As String) As Logger
Return GetLogger(ClassName, String.Empty)
End Function
''' <summary>
''' Returns the Logger for the specified module using event-properties
''' </summary>
''' <remarks>
''' https://github.com/NLog/NLog/wiki/EventProperties-Layout-Renderer
''' https://stackoverflow.com/questions/31337030/separate-log-file-for-specific-class-instance-using-nlog/32065824#32065824
''' </remarks>
''' <returns>An object of Logging.Logger</returns>
<DebuggerStepThrough()>
Public Function GetLoggerFor(ModuleName As String) As Logger
Dim oClassName As String = GetClassFullName()
Return GetLogger(oClassName, ModuleName)
End Function
''' <summary>
''' Returns the Logger for a class specified by `ClassName`
''' </summary>
''' <param name="ClassName">The name of the class the logger belongs to</param>
''' <returns>An object of Logging.Logger</returns>
Public Function GetLogger(ClassName As String, ModuleName As String) As Logger
Dim oLogger = LogFactory.GetLogger(Of Logger)(ClassName)
If ModuleName IsNot Nothing AndAlso ModuleName.Length > 0 Then
Return oLogger.WithProperty("ModuleName", $"-{ModuleName}")
End If
Return oLogger
End Function
''' <summary>
''' Clears the internal log
''' </summary>
Public Sub ClearLogs()
'Dim oTarget = _config.FindTargetByName(Of MemoryTarget)(TARGET_MEMORY)
'oTarget?.Logs.Clear()
End Sub
''' <summary>
''' Gets the fully qualified name of the class invoking the calling method,
''' including the namespace but Not the assembly.
''' </summary>
''' <returns>The fully qualified class name</returns>
''' <remarks>This method is very resource-intensive!</remarks>
<DebuggerStepThrough()>
Public Shared Function GetClassFullName(Optional IncludeMethodNames As Boolean = False, Optional Parts As Integer = 0) As String
Dim oFramesToSkip As Integer = 2
Dim oClassName As String = String.Empty
Dim oStackTrace = Environment.StackTrace
Dim oStackTraceLines = oStackTrace.Replace(vbCr, "").Split({vbLf}, StringSplitOptions.RemoveEmptyEntries)
For i As Integer = 0 To oStackTraceLines.Length - 1
Dim oCallingClassAndMethod = oStackTraceLines(i).Split({" ", "<>", "(", ")"}, StringSplitOptions.RemoveEmptyEntries)(1)
Dim oMethodStartIndex As Integer = oCallingClassAndMethod.LastIndexOf(".", StringComparison.Ordinal)
If oMethodStartIndex > 0 Then
If IncludeMethodNames Then
oMethodStartIndex = oCallingClassAndMethod.Count
End If
Dim oCallingClass = oCallingClassAndMethod.Substring(0, oMethodStartIndex)
oClassName = oCallingClass.TrimEnd("."c)
If Not oClassName.StartsWith("System.Environment") AndAlso oFramesToSkip <> 0 Then
i += oFramesToSkip - 1
oFramesToSkip = 0
Continue For
End If
If Not oClassName.StartsWith("System.") Then Exit For
End If
Next
If Parts > 0 Then
Dim oParts = oClassName.
Split(".").
Reverse().
Take(Parts).
Reverse()
oClassName = String.Join(".", oParts)
End If
Return oClassName
End Function
''' <summary>
''' Returns the initial log configuration
''' </summary>
''' <param name="productName">The chosen productname</param>
''' <param name="logFileSuffix">The chosen suffix</param>
''' <returns>A NLog.LoggingConfiguration object</returns>
Private Function GetConfig(productName As String, logFileSuffix As String) As LoggingConfiguration
_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_TRACE, GetTraceLogTarget(_basePath))
'_config.AddTarget(TARGET_MEMORY, GetMemoryDebugTarget())
' Add default rules
AddDefaultRules(_config)
Return _config
End Function
''' <summary>
''' Adds the default rules
''' </summary>
''' <param name="config">A NLog.LoggingConfiguration object</param>
Private Sub AddDefaultRules(ByRef config As LoggingConfiguration)
config.AddRuleForOneLevel(LogLevel.Error, TARGET_ERROR_EX)
config.AddRuleForOneLevel(LogLevel.Fatal, TARGET_ERROR_EX)
config.AddRuleForOneLevel(LogLevel.Warn, TARGET_DEFAULT)
config.AddRuleForOneLevel(LogLevel.Info, TARGET_DEFAULT)
'config.AddRuleForAllLevels(TARGET_MEMORY)
End Sub
''' <summary>
''' Returns the full path of the current default log file.
''' </summary>
''' <returns>Full path of the current default log file</returns>
Private Function GetCurrentLogFilePath()
Dim logEventInfo As New LogEventInfo() With {.TimeStamp = Date.Now}
Dim target As FileTarget = _config.FindTargetByName(TARGET_DEFAULT)
Dim fileName As String = target.FileName.Render(logEventInfo)
Return fileName
End Function
''' <summary>
''' Reconfigures and re-adds all loggers, optionally adding the debug rule.
''' </summary>
''' <param name="Debug">Adds the Debug rule if true.</param>
''' <param name="Trace">Adds the Trace rule if true.</param>
Private Sub ReloadConfig(Optional Debug As Boolean = False, Optional Trace As Boolean = False)
' Clear Logging Rules
_config.LoggingRules.Clear()
' Add default rules
AddDefaultRules(_config)
' Add debug rule, if configured
If Debug = True Then
_config.AddRule(LogLevel.Debug, LogLevel.Error, TARGET_DEBUG)
End If
If Trace = True Then
_config.AddRule(LogLevel.Trace, LogLevel.Error, TARGET_TRACE)
End If
' Reload all running loggers
LogFactory.ReconfigExistingLoggers()
End Sub
#Region "Log Targets"
Private Function GetDefaultLogTarget(basePath As String) As FileTarget
Dim defaultLog As New FileTarget() With {
.FileName = Path.Combine(basePath, FILE_NAME_FORMAT_DEFAULT),
.Name = TARGET_DEFAULT,
.Layout = LOG_FORMAT_DEFAULT,
.MaxArchiveFiles = MAX_ARCHIVE_FILES_DEFAULT,
.ArchiveEvery = ARCHIVE_EVERY,
.KeepFileOpen = KEEP_FILES_OPEN,
.Encoding = Text.Encoding.Unicode
}
Return defaultLog
End Function
Private Function GetErrorExceptionLogTarget(basePath As String) As FileTarget
Dim errorLogWithExceptions As New FileTarget() With {
.FileName = Path.Combine(basePath, FILE_NAME_FORMAT_ERROR),
.Name = TARGET_ERROR_EX,
.Layout = LOG_FORMAT_ERROR,
.MaxArchiveFiles = MAX_ARCHIVE_FILES_DEFAULT,
.ArchiveEvery = ARCHIVE_EVERY,
.KeepFileOpen = KEEP_FILES_OPEN,
.Encoding = Text.Encoding.Unicode
}
Return errorLogWithExceptions
End Function
Private Function GetErrorLogTarget(basePath As String) As FileTarget
Dim errorLog As New FileTarget() With {
.FileName = Path.Combine(basePath, FILE_NAME_FORMAT_ERROR),
.Name = TARGET_ERROR,
.Layout = LOG_FORMAT_DEFAULT,
.MaxArchiveFiles = MAX_ARCHIVE_FILES_DEFAULT,
.ArchiveEvery = ARCHIVE_EVERY,
.KeepFileOpen = KEEP_FILES_OPEN,
.Encoding = Text.Encoding.Unicode
}
Return errorLog
End Function
Private Function GetDebugLogTarget(basePath As String) As FileTarget
Dim debugLog As New FileTarget() With {
.FileName = Path.Combine(basePath, FILE_NAME_FORMAT_DEBUG),
.Name = TARGET_DEBUG,
.Layout = LOG_FORMAT_DEBUG,
.MaxArchiveFiles = MAX_ARCHIVE_FILES_DEBUG_DETAIL,
.ArchiveEvery = ARCHIVE_EVERY,
.KeepFileOpen = KEEP_FILES_OPEN_DEBUG,
.OpenFileCacheTimeout = OPEN_FILE_CACHE_TIMEOUT,
.AutoFlush = AUTO_FLUSH,
.OpenFileFlushTimeout = OPEN_FILE_FLUSH_TIMEOUT,
.Encoding = Text.Encoding.Unicode
}
Return debugLog
End Function
Private Function GetTraceLogTarget(basePath As String) As FileTarget
Dim debugLog As New FileTarget() With {
.FileName = Path.Combine(basePath, FILE_NAME_FORMAT_TRACE),
.Name = TARGET_DEBUG,
.Layout = LOG_FORMAT_DEBUG,
.MaxArchiveFiles = MAX_ARCHIVE_FILES_DEBUG_DETAIL,
.ArchiveEvery = ARCHIVE_EVERY,
.KeepFileOpen = KEEP_FILES_OPEN_DEBUG,
.OpenFileCacheTimeout = OPEN_FILE_CACHE_TIMEOUT,
.AutoFlush = AUTO_FLUSH,
.OpenFileFlushTimeout = OPEN_FILE_FLUSH_TIMEOUT,
.Encoding = Text.Encoding.Unicode
}
Return debugLog
End Function
'Private Function GetMemoryDebugTarget() As MemoryTarget
' Dim memoryLog As New MemoryTarget() With {
' .Layout = LOG_FORMAT_DEBUG,
' .Name = TARGET_MEMORY,
' .OptimizeBufferReuse = True,
' .MaxLogsCount = MAX_MEMORY_LOG_COUNT
' }
' Return memoryLog
'End Function
#End Region
End Class

10
Logging/LogOptions.vb Normal file
View File

@@ -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

27
Logging/Logger.vb Normal file
View File

@@ -0,0 +1,27 @@
Imports NLog
Public Class Logger
Inherits NLog.Logger
<DebuggerStepThrough()>
Public Sub NewBlock(blockId As String)
Dim message As String = $"-----> Start of Block {blockId}"
Dim logEventInfo As New LogEventInfo(LogLevel.Info, Name, message)
Dim logEventDebug As New LogEventInfo(LogLevel.Debug, Name, message)
Dim WrapperType As Type = GetType(Logger)
Log(WrapperType, logEventDebug)
Log(WrapperType, logEventInfo)
End Sub
<DebuggerStepThrough()>
Public Sub EndBlock()
Dim message As String = $"<----- End of Block"
Dim logEventInfo As New LogEventInfo(LogLevel.Info, Name, message)
Dim logEventDebug As New LogEventInfo(LogLevel.Debug, Name, message)
Dim WrapperType As Type = GetType(Logger)
Log(WrapperType, logEventDebug)
Log(WrapperType, logEventInfo)
End Sub
End Class

116
Logging/Logging.vbproj Normal file
View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{903B2D7D-3B80-4BE9-8713-7447B704E1B0}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>DigitalData.Modules.Logging</RootNamespace>
<AssemblyName>DigitalData.Modules.Logging</AssemblyName>
<FileAlignment>512</FileAlignment>
<MyType>Windows</MyType>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>DigitalData.Modules.Logging.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DocumentationFile>DigitalData.Modules.Logging.xml</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup>
<OptionExplicit>On</OptionExplicit>
</PropertyGroup>
<PropertyGroup>
<OptionCompare>Binary</OptionCompare>
</PropertyGroup>
<PropertyGroup>
<OptionStrict>Off</OptionStrict>
</PropertyGroup>
<PropertyGroup>
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.7.15\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="System" />
<Import Include="System.Collections" />
<Import Include="System.Collections.Generic" />
<Import Include="System.Data" />
<Import Include="System.Diagnostics" />
<Import Include="System.Linq" />
<Import Include="System.Xml.Linq" />
<Import Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Compile Include="LogConfig.vb" />
<Compile Include="Logger.vb" />
<Compile Include="LogOptions.vb" />
<Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Application.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Application.myapp</DependentUpon>
</Compile>
<Compile Include="My Project\Resources.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="My Project\Settings.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="TraceListener.vb" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="My Project\Resources.resx">
<Generator>VbMyResourcesResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.vb</LastGenOutput>
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="My Project\Application.myapp">
<Generator>MyApplicationCodeGenerator</Generator>
<LastGenOutput>Application.Designer.vb</LastGenOutput>
</None>
<None Include="My Project\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<CustomToolNamespace>My</CustomToolNamespace>
<LastGenOutput>Settings.Designer.vb</LastGenOutput>
</None>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project>

View File

@@ -0,0 +1,13 @@
'------------------------------------------------------------------------------
' <auto-generated>
' Dieser Code wurde von einem Tool generiert.
' Laufzeitversion:4.0.30319.42000
'
' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
' der Code erneut generiert wird.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MySubMain>false</MySubMain>
<SingleInstance>false</SingleInstance>
<ShutdownMode>0</ShutdownMode>
<EnableVisualStyles>true</EnableVisualStyles>
<AuthenticationMode>0</AuthenticationMode>
<ApplicationType>1</ApplicationType>
<SaveMySettingsOnExit>true</SaveMySettingsOnExit>
</MyApplicationData>

View File

@@ -0,0 +1,35 @@
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
' Allgemeine Informationen über eine Assembly werden über die folgenden
' Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
' die einer Assembly zugeordnet sind.
' Werte der Assemblyattribute überprüfen
<Assembly: AssemblyTitle("Modules.Logging")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyCompany("Digital Data")>
<Assembly: AssemblyProduct("Modules.Logging")>
<Assembly: AssemblyCopyright("Copyright © 2022")>
<Assembly: AssemblyTrademark("2.5.4.2")>
<Assembly: ComVisible(False)>
'Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird.
<Assembly: Guid("fd9ac226-1f78-499e-909c-f0b4d74bbfaf")>
' Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
'
' Hauptversion
' Nebenversion
' Buildnummer
' Revision
'
' Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
' übernehmen, indem Sie "*" eingeben:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2.5.4.2")>
<Assembly: AssemblyFileVersion("2.5.4.2")>

63
Logging/My Project/Resources.Designer.vb generated Normal file
View File

@@ -0,0 +1,63 @@
'------------------------------------------------------------------------------
' <auto-generated>
' Dieser Code wurde von einem Tool generiert.
' Laufzeitversion:4.0.30319.42000
'
' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
' der Code erneut generiert wird.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On
Imports System
Namespace My.Resources
'Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
'-Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
'Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
'mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
'''<summary>
''' Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
'''</summary>
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0"), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _
Friend Module Resources
Private resourceMan As Global.System.Resources.ResourceManager
Private resourceCulture As Global.System.Globalization.CultureInfo
'''<summary>
''' Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
Get
If Object.ReferenceEquals(resourceMan, Nothing) Then
Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("DigitalData.Modules.Logging.Resources", GetType(Resources).Assembly)
resourceMan = temp
End If
Return resourceMan
End Get
End Property
'''<summary>
''' Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
''' Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend Property Culture() As Global.System.Globalization.CultureInfo
Get
Return resourceCulture
End Get
Set
resourceCulture = value
End Set
End Property
End Module
End Namespace

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

73
Logging/My Project/Settings.Designer.vb generated Normal file
View File

@@ -0,0 +1,73 @@
'------------------------------------------------------------------------------
' <auto-generated>
' Dieser Code wurde von einem Tool generiert.
' Laufzeitversion:4.0.30319.42000
'
' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
' der Code erneut generiert wird.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On
Namespace My
<Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0"), _
Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Partial Friend NotInheritable Class MySettings
Inherits Global.System.Configuration.ApplicationSettingsBase
Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings)
#Region "Automatische My.Settings-Speicherfunktion"
#If _MyType = "WindowsForms" Then
Private Shared addedHandler As Boolean
Private Shared addedHandlerLockObject As New Object
<Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs)
If My.Application.SaveMySettingsOnExit Then
My.Settings.Save()
End If
End Sub
#End If
#End Region
Public Shared ReadOnly Property [Default]() As MySettings
Get
#If _MyType = "WindowsForms" Then
If Not addedHandler Then
SyncLock addedHandlerLockObject
If Not addedHandler Then
AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
addedHandler = True
End If
End SyncLock
End If
#End If
Return defaultInstance
End Get
End Property
End Class
End Namespace
Namespace My
<Global.Microsoft.VisualBasic.HideModuleNameAttribute(), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute()> _
Friend Module MySettingsProperty
<Global.System.ComponentModel.Design.HelpKeywordAttribute("My.Settings")> _
Friend ReadOnly Property Settings() As Global.DigitalData.Modules.Logging.My.MySettings
Get
Return Global.DigitalData.Modules.Logging.My.MySettings.Default
End Get
End Property
End Module
End Namespace

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" UseMySettingsClassName="true">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

249
Logging/TraceListener.vb Normal file
View File

@@ -0,0 +1,249 @@
Imports System
Imports System.Configuration
Imports System.Diagnostics
Imports System.Globalization
Imports System.IO
Imports System.Security.Permissions
Imports System.Text.RegularExpressions
<HostProtection(Synchronization:=True)>
Public Class RollingXmlWriterTraceListener
Inherits XmlWriterTraceListener
Private Shared ReadOnly LogFileNumberCaptureName As String = "LogFileNumber"
Private attributesLoaded As Boolean = False
Private logfileSuffixExpression As Regex = New Regex("_(?<" & LogFileNumberCaptureName & ">\d*)\.", RegexOptions.Compiled)
Private currentFileSuffixNumber As Integer = 0
Private _maxTraceFileSize As Long = 128 * 1024 * 1024
Private basicTraceFileName As String = String.Empty
Public Sub New(ByVal filename As String)
MyBase.New(filename)
Me.basicTraceFileName = filename
Me.currentFileSuffixNumber = Me.GetTraceFileNumber()
Me.StartNewTraceFile()
End Sub
Public Sub New(ByVal filename As String, ByVal name As String)
MyBase.New(filename, name)
Me.basicTraceFileName = filename
Me.StartNewTraceFile()
End Sub
Public ReadOnly Property CurrentTraceFileName As String
Get
Return Path.Combine(Path.GetDirectoryName(Me.basicTraceFileName), Path.GetFileNameWithoutExtension(Me.basicTraceFileName) & "_" & Me.currentFileSuffixNumber.ToString().PadLeft(4, "0"c) & Path.GetExtension(Me.basicTraceFileName))
End Get
End Property
Public Property MaxTraceFileSize As Long
Get
If Not Me.attributesLoaded Then
Me.LoadAttributes()
End If
Return Me._maxTraceFileSize
End Get
Set(ByVal value As Long)
If Not Me.attributesLoaded Then
Me.LoadAttributes()
End If
Me._maxTraceFileSize = value
End Set
End Property
Protected ReadOnly Property IsRollingConditionReached As Boolean
Get
Dim streamWriter As StreamWriter = CType(Me.Writer, StreamWriter)
Dim fileStream As FileStream = CType(streamWriter.BaseStream, FileStream)
Dim traceFileName As String = fileStream.Name
Dim traceFileInfo As FileInfo = New FileInfo(traceFileName)
If traceFileInfo.Length > Me._maxTraceFileSize Then
Return True
Else
Return False
End If
End Get
End Property
Public Overrides Sub Fail(ByVal message As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.Fail(message)
End Sub
Public Overrides Sub Fail(ByVal message As String, ByVal detailMessage As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.Fail(message, detailMessage)
End Sub
Public Overrides Sub TraceData(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer, ByVal data As Object)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.TraceData(eventCache, source, eventType, id, data)
End Sub
Public Overrides Sub TraceData(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer, ParamArray data As Object())
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.TraceData(eventCache, source, eventType, id, data)
End Sub
Public Overrides Sub TraceEvent(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.TraceEvent(eventCache, source, eventType, id)
End Sub
Public Overrides Sub TraceEvent(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer, ByVal message As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.TraceEvent(eventCache, source, eventType, id, message)
End Sub
Public Overrides Sub TraceEvent(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal eventType As TraceEventType, ByVal id As Integer, ByVal format As String, ParamArray args As Object())
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.TraceEvent(eventCache, source, eventType, id, format, args)
End Sub
Public Overrides Sub TraceTransfer(ByVal eventCache As TraceEventCache, ByVal source As String, ByVal id As Integer, ByVal message As String, ByVal relatedActivityId As Guid)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.TraceTransfer(eventCache, source, id, message, relatedActivityId)
End Sub
Public Overrides Sub Write(ByVal o As Object)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.Write(o)
End Sub
Public Overrides Sub Write(ByVal o As Object, ByVal category As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.Write(o, category)
End Sub
Public Overrides Sub Write(ByVal message As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.Write(message)
End Sub
Public Overrides Sub Write(ByVal message As String, ByVal category As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.Write(message, category)
End Sub
Public Overrides Sub WriteLine(ByVal o As Object)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.WriteLine(o)
End Sub
Public Overrides Sub WriteLine(ByVal o As Object, ByVal category As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.WriteLine(o, category)
End Sub
Public Overrides Sub WriteLine(ByVal message As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.WriteLine(message)
End Sub
Public Overrides Sub WriteLine(ByVal message As String, ByVal category As String)
If Me.IsRollingConditionReached Then
Me.StartNewTraceFile()
End If
MyBase.WriteLine(message, category)
End Sub
Protected Overrides Function GetSupportedAttributes() As String()
Return New String(0) {"MaxTraceFileSize"}
End Function
Private Sub StartNewTraceFile()
Dim streamWriter As StreamWriter = CType(Me.Writer, StreamWriter)
Dim fileStream As FileStream = CType(streamWriter.BaseStream, FileStream)
fileStream.Close()
Me.currentFileSuffixNumber += 1
Me.Writer = New StreamWriter(New FileStream(Me.CurrentTraceFileName, FileMode.Create))
End Sub
Private Function GetTraceFileNumber() As Integer
Dim directoryName As String = Path.GetDirectoryName(Me.basicTraceFileName)
Dim basicTraceFileNameWithoutExtension As String = Path.GetFileNameWithoutExtension(Me.basicTraceFileName)
Dim basicTraceFileNameExtension As String = Path.GetExtension(Me.basicTraceFileName)
Dim existingLogFiles As String() = Directory.GetFiles(directoryName, basicTraceFileNameWithoutExtension & "*")
Dim highestNumber As Integer = -1
For Each existingLogFile As String In existingLogFiles
Dim match As Match = Me.logfileSuffixExpression.Match(existingLogFile)
If match IsNot Nothing Then
Dim tempInt As Integer
If match.Groups.Count >= 1 AndAlso Integer.TryParse(match.Groups(LogFileNumberCaptureName).Value, tempInt) AndAlso tempInt >= highestNumber Then
highestNumber = tempInt
End If
End If
Next
Return highestNumber
End Function
Private Sub LoadAttributes()
If Attributes.ContainsKey("MaxTraceFileSize") AndAlso Not String.IsNullOrEmpty(Attributes("MaxTraceFileSize")) Then
Dim tempLong As Long = 0
Dim attributeValue As String = Attributes("MaxTraceFileSize")
If Long.TryParse(attributeValue, tempLong) Then
Me._maxTraceFileSize = Long.Parse(Attributes("MaxTraceFileSize"), NumberFormatInfo.InvariantInfo)
Else
Throw New ConfigurationErrorsException(String.Format("Trace listener {0} has an unparseable configuration attribute ""MaxTraceFileSize"". The value ""{1}"" cannot be parsed to a long value.", Me.Name, attributeValue))
End If
End If
Me.attributesLoaded = True
End Sub
End Class

4
Logging/packages.config Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.7.15" targetFramework="net461" />
</packages>