Imports System.Net.Http Imports System.IO Imports Microsoft.Extensions.Logging Imports System.Threading.Tasks Imports System.Configuration '// Ejemplo de uso de la extensión LogVariable(): '// '// ' Registrar información de una variable '// Dim peassoVariable As String = "Hello, World!" '// logger.LogVariable("peassoVariable", peassoVariable, LogLevel.Debug) '// El uso del destino Pushover está limitado a danmun. Public NotInheritable Class TsLoggerConfiguration '// Identificador del evento a registrar (0 para todos). Public Property EventId As Integer = 0 '// Nivel mínimo de registro. Private Property _minimumLogLevel As LogLevel = LogLevel.Trace Public Property MinimumLogLevel As String Get Return _minimumLogLevel End Get Set(logLevelString As String) If Not [Enum].TryParse(logLevelString, True, _minimumLogLevel) Then '// Valor predeterminado en caso de que no se pueda '// interpretar la configuración. _minimumLogLevel = LogLevel.Trace End If End Set End Property '// Indica si se debe registrar en la consola. Public Property LogToConsole As Boolean = False '// Indica si se debe registrar en la salida de depuración. Public Property LogToDebug As Boolean = True '// Indica si se debe registrar en un archivo. Public Property LogToFile As Boolean = False '// Ruta base para los archivos de registro (debe ser válida y accesible). Public Property LogFilePath As String Public Property LogToPushover As Boolean = False Public Property PushoverMinimumLogLevel As LogLevel = LogLevel.Error Public Property LogToSlack As Boolean = False Public Property SlackDestination As String = "#notificaciones" Public Property NetworkInSeparateThread As Boolean = False Public Property IncludeSourceInfo As Boolean = False '// Mapeo entre los niveles de registro y los colores de la consola. Friend ReadOnly Property LogLevelToColorMap As Dictionary(Of LogLevel, ConsoleColor) = New Dictionary(Of LogLevel, ConsoleColor) From { {LogLevel.Trace, ConsoleColor.DarkCyan}, {LogLevel.Debug, ConsoleColor.Cyan}, {LogLevel.Information, ConsoleColor.Green}, {LogLevel.Warning, ConsoleColor.DarkYellow}, {LogLevel.Error, ConsoleColor.Red}, {LogLevel.Critical, ConsoleColor.Magenta} } End Class Public NotInheritable Class TsLogger Implements ILogger Private ReadOnly _name As String Private ReadOnly _getCurrentConfig As Func(Of TsLoggerConfiguration) Private ReadOnly _config As TsLoggerConfiguration '// Constructor con nombre y función para obtener la configuración actual. Public Sub New(name As String, getCurrentConfig As Func(Of TsLoggerConfiguration)) If String.IsNullOrEmpty(name) Then Throw New ArgumentException("El nombre no puede estar vacío", NameOf(name)) If getCurrentConfig Is Nothing Then Throw New ArgumentNullException(NameOf(getCurrentConfig)) _name = name _getCurrentConfig = getCurrentConfig End Sub '// Constructor con nombre y configuración directa. Public Sub New(name As String, config As TsLoggerConfiguration) If String.IsNullOrEmpty(name) Then Throw New ArgumentException("El nombre no puede estar vacío", NameOf(name)) If config Is Nothing Then Throw New ArgumentNullException(NameOf(config)) _name = name _config = config End Sub '// Obtener la configuración actual. Private Function GetCurrentConfiguration() As TsLoggerConfiguration Return If(_getCurrentConfig IsNot Nothing, _getCurrentConfig.Invoke(), _config) End Function '// Verifica si el nivel de registro está habilitado. Public Function IsEnabled(logLevel As LogLevel) As Boolean Implements ILogger.IsEnabled Return logLevel >= GetCurrentConfiguration().MinimumLogLevel End Function '// Registra un mensaje con la configuración dada. Public Sub Log(Of TState)(logLevel As LogLevel, eventId As EventId, state As TState, exception As Exception, formatter As Func(Of TState, Exception, String)) Implements ILogger.Log Try Dim config As TsLoggerConfiguration = GetCurrentConfiguration() '// Obtener la configuración. If Not IsEnabled(logLevel) Then Return '// Obtener la información del archivo de código fuente si está habilitado. Dim sourceInfo As String = "" If config.IncludeSourceInfo Then Dim stackTrace As New Diagnostics.StackTrace(True) Dim frame As Diagnostics.StackFrame = stackTrace.GetFrame(1) Dim fileName As String = frame.GetFileName() Dim method As String = frame.GetMethod().Name Dim line As Integer = frame.GetFileLineNumber() sourceInfo = $" [Archivo: {fileName}, Método: {method}, Línea: {line}]" End If '// Mensaje básico sin la fecha y hora. Dim basicMessage As String = $"[{eventId.Id,2}: {logLevel,-12}] {_name} - {formatter(state, exception)}{sourceInfo}" '// Agregar la fecha y hora actual al inicio del mensaje para consola y archivo. Dim timestamp As String = DateTime.Now.ToString("yyyy-MM-dd_HH·mm·sszz") Dim messageWithTimestamp As String = $"{timestamp} {basicMessage}" '// Registrar en la consola si está habilitado. If config.LogToConsole Then Console.ForegroundColor = config.LogLevelToColorMap(logLevel) Console.WriteLine(messageWithTimestamp) End If '// Registrar en la salida de depuración si está habilitado. If config.LogToDebug Then Debug.WriteLine(basicMessage) End If '// Registrar en un archivo si está habilitado. If config.LogToFile Then If String.IsNullOrEmpty(config.LogFilePath) Then Debug.WriteLine($"La ruta del archivo de registro no está especificada. Se omite el registro en archivos para el mensaje: {basicMessage}") Else Try '// Intentar crear el directorio si no existe. Directory.CreateDirectory(config.LogFilePath) Dim folderPath As String = Path.Combine(config.LogFilePath, DateTime.Now.ToString("yyyy\\MM")) Directory.CreateDirectory(folderPath) Dim fileName As String = $"{DateTime.Now:yyyy-MM-dd_HH·mm·sszz}_{_name}.log" Dim filePath As String = Path.Combine(folderPath, fileName) Using writer As New StreamWriter(filePath, append:=True) writer.WriteLine(messageWithTimestamp) End Using Catch ex As IOException Debug.WriteLine($"Error al intentar crear o acceder a la ruta del archivo de registro '{config.LogFilePath}'. Error: {ex}") End Try End If End If '// Enviar notificación a Pushover si está habilitado. If config.LogToPushover AndAlso logLevel >= config.PushoverMinimumLogLevel Then Dim parameters As New Dictionary(Of String, String) From { {"token", "a42g7oaz2t4u7unbdg5nm7qaqocht6"}, {"user", "uxzAV6NcPoxkAGLSNWSsZX9SfPeUo5"}, {"message", basicMessage}, {"title", $"{_name}"}, '// Utilizar el nombre del registrador como título. {"priority", GetPushoverPriority(logLevel)} '// Establecer la prioridad en función del nivel de registro. } If config.NetworkInSeparateThread Then Task.Run(Sub() Using client As New HttpClient() Dim response = client.PostAsync("https://api.pushover.net/1/messages.json", New FormUrlEncodedContent(parameters)).Result Debug.WriteLine(response.ToString) End Using End Sub) Else Using client As New HttpClient() Dim response = client.PostAsync("https://api.pushover.net/1/messages.json", New FormUrlEncodedContent(parameters)).Result Debug.WriteLine(response.ToString) End Using End If End If '// Enviar notificación a Slack si está habilitado. If config.LogToSlack Then If config.NetworkInSeparateThread Then Task.Run(Sub() tsl5.Utilidades.EnviarNotificacionSlack(basicMessage, otroTexto:=timestamp, descripcionRemitente:=$"TsLogger {_name}", destinatario:=config.SlackDestination) End Sub) Else tsl5.Utilidades.EnviarNotificacionSlack(basicMessage, otroTexto:=timestamp, descripcionRemitente:=$"TsLogger {_name}", destinatario:=config.SlackDestination) End If End If Catch ex As Exception Debug.WriteLine($"Excepción en TsLogger: {ex}") End Try End Sub '// Implementación básica de BeginScope (no se necesita un manejo detallado de alcance en esta implementación). Private Function ILogger_BeginScope(Of TState)(state As TState) As IDisposable Implements ILogger.BeginScope Return New EmptyDisposable() End Function '// Clase auxiliar para cumplir con la interfaz IDisposable requerida por BeginScope. Private Class EmptyDisposable Implements IDisposable Public Sub Dispose() Implements IDisposable.Dispose '// No se requiere ninguna acción aquí. End Sub End Class ' Método auxiliar para convertir el nivel de registro en una prioridad para Pushover. Private Function GetPushoverPriority(logLevel As LogLevel) As String Select Case logLevel Case LogLevel.Critical Return "1" ' Prioridad alta Case LogLevel.Error Return "1" ' Prioridad alta Case LogLevel.Warning Return "0" ' Prioridad normal Case LogLevel.Information Return "-1" ' Prioridad baja Case LogLevel.Debug Return "-1" ' Prioridad baja Case LogLevel.Trace Return "-2" ' Prioridad mínima Case Else Return "0" ' Prioridad normal End Select End Function End Class