mirror of
https://github.com/NexusOne23/noid-privacy.git
synced 2026-02-07 12:11:53 +01:00
366 lines
11 KiB
PowerShell
366 lines
11 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Unified logging system for NoID Privacy Framework
|
|
|
|
.DESCRIPTION
|
|
Provides centralized logging functionality with multiple severity levels,
|
|
file output, and optional console output with color coding.
|
|
|
|
.NOTES
|
|
Author: NexusOne23
|
|
Version: 2.2.1
|
|
Requires: PowerShell 5.1+
|
|
#>
|
|
|
|
# Log severity levels
|
|
enum LogLevel {
|
|
DEBUG = 0
|
|
INFO = 1
|
|
WARNING = 2
|
|
ERROR = 3
|
|
SUCCESS = 4
|
|
}
|
|
|
|
# Global logger configuration - MUST be $global: for cross-module session sharing
|
|
# Using $script: would create separate log files per Import-Module call!
|
|
# NOTE: Must use Get-Variable to check existence (direct access fails in Strict Mode)
|
|
if (-not (Get-Variable -Name 'LoggerConfig' -Scope Global -ErrorAction SilentlyContinue)) {
|
|
$global:LoggerConfig = @{
|
|
LogFilePath = ""
|
|
MinimumLevel = [LogLevel]::INFO
|
|
EnableConsole = $true
|
|
EnableFile = $true
|
|
TimestampFormat = "yyyy-MM-dd HH:mm:ss"
|
|
}
|
|
}
|
|
|
|
function Initialize-Logger {
|
|
<#
|
|
.SYNOPSIS
|
|
Initialize the logging system
|
|
|
|
.PARAMETER LogDirectory
|
|
Directory path for log files
|
|
|
|
.PARAMETER MinimumLevel
|
|
Minimum log level to record (DEBUG, INFO, WARNING, ERROR, SUCCESS)
|
|
|
|
.PARAMETER EnableConsole
|
|
Enable console output
|
|
|
|
.PARAMETER EnableFile
|
|
Enable file output
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$LogDirectory = (Join-Path $PSScriptRoot "..\Logs"),
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[LogLevel]$MinimumLevel = [LogLevel]::INFO,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[bool]$EnableConsole = $true,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[bool]$EnableFile = $true
|
|
)
|
|
|
|
# Reuse existing session if already initialized
|
|
if ($global:LoggerConfig.LogFilePath -and (Test-Path -Path $global:LoggerConfig.LogFilePath)) {
|
|
Write-Host "[Logger] Reusing existing log session: $($global:LoggerConfig.LogFilePath)" -ForegroundColor DarkGray
|
|
return
|
|
}
|
|
|
|
# Create log directory if it doesn't exist
|
|
if ($EnableFile) {
|
|
if (-not (Test-Path -Path $LogDirectory)) {
|
|
try {
|
|
New-Item -ItemType Directory -Path $LogDirectory -Force -ErrorAction Stop | Out-Null
|
|
}
|
|
catch {
|
|
Write-Host "[ERROR] Failed to create log directory: $LogDirectory" -ForegroundColor Red
|
|
Write-Host "[ERROR] Exception: $_" -ForegroundColor Red
|
|
$EnableFile = $false
|
|
}
|
|
}
|
|
}
|
|
|
|
# Generate log file name with timestamp
|
|
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
|
$logFileName = "NoIDPrivacy_$timestamp.log"
|
|
$global:LoggerConfig.LogFilePath = Join-Path $LogDirectory $logFileName
|
|
$global:LoggerConfig.MinimumLevel = $MinimumLevel
|
|
$global:LoggerConfig.EnableConsole = $EnableConsole
|
|
$global:LoggerConfig.EnableFile = $EnableFile
|
|
|
|
# Test if we can write to the log file
|
|
if ($EnableFile) {
|
|
try {
|
|
"# NoID Privacy Log File" | Out-File -FilePath $global:LoggerConfig.LogFilePath -Encoding UTF8 -ErrorAction Stop
|
|
}
|
|
catch {
|
|
Write-Host "[ERROR] Failed to create log file: $($global:LoggerConfig.LogFilePath)" -ForegroundColor Red
|
|
Write-Host "[ERROR] Exception: $_" -ForegroundColor Red
|
|
$global:LoggerConfig.EnableFile = $false
|
|
}
|
|
}
|
|
|
|
# Write initial log entry
|
|
Write-Log -Level INFO -Message "Logger initialized" -Module "Logger"
|
|
Write-Log -Level INFO -Message "Log file: $($global:LoggerConfig.LogFilePath)" -Module "Logger"
|
|
}
|
|
|
|
function Write-Log {
|
|
<#
|
|
.SYNOPSIS
|
|
Write a log entry
|
|
|
|
.PARAMETER Level
|
|
Log severity level
|
|
|
|
.PARAMETER Message
|
|
Log message content
|
|
|
|
.PARAMETER Module
|
|
Module or component name generating the log
|
|
|
|
.PARAMETER Exception
|
|
Optional exception object for error logging
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[LogLevel]$Level,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Message,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$Module = "Framework",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[System.Exception]$Exception = $null
|
|
)
|
|
|
|
# Check if level meets minimum threshold
|
|
if ($Level -lt $global:LoggerConfig.MinimumLevel) {
|
|
return
|
|
}
|
|
|
|
# Format timestamp
|
|
$timestamp = Get-Date -Format $global:LoggerConfig.TimestampFormat
|
|
|
|
# Build log entry
|
|
$logEntry = "[$timestamp] [$Level] [$Module] $Message"
|
|
|
|
# Add exception details if present
|
|
if ($null -ne $Exception) {
|
|
$logEntry += "`n Exception: $($Exception.Message)"
|
|
$logEntry += "`n StackTrace: $($Exception.StackTrace)"
|
|
}
|
|
|
|
# Write to file with robust retry logic
|
|
if ($global:LoggerConfig.EnableFile -and $global:LoggerConfig.LogFilePath) {
|
|
$maxRetries = 5
|
|
$retryDelayMs = 100
|
|
$writeSuccess = $false
|
|
$lastError = $null
|
|
|
|
for ($i = 0; $i -lt $maxRetries; $i++) {
|
|
try {
|
|
Add-Content -Path $global:LoggerConfig.LogFilePath -Value $logEntry -Encoding UTF8 -ErrorAction Stop
|
|
$writeSuccess = $true
|
|
break
|
|
}
|
|
catch {
|
|
$lastError = $_
|
|
Start-Sleep -Milliseconds $retryDelayMs
|
|
# Exponential backoff
|
|
$retryDelayMs *= 2
|
|
}
|
|
}
|
|
|
|
if (-not $writeSuccess) {
|
|
# Write error to console with detailed info only after all retries failed
|
|
Write-Host "[FILE WRITE ERROR] Failed to write to log file after $maxRetries attempts: $($global:LoggerConfig.LogFilePath)" -ForegroundColor Red
|
|
Write-Host "[FILE WRITE ERROR] Last Exception: $lastError" -ForegroundColor Red
|
|
# Disable file logging to prevent spam
|
|
$global:LoggerConfig.EnableFile = $false
|
|
}
|
|
}
|
|
|
|
# Write to console with color coding (suppress DEBUG-level on console)
|
|
if ($global:LoggerConfig.EnableConsole -and $Level -ge [LogLevel]::INFO) {
|
|
$consoleColor = switch ($Level) {
|
|
([LogLevel]::DEBUG) { "Gray" }
|
|
([LogLevel]::INFO) { "White" }
|
|
([LogLevel]::WARNING) { "Yellow" }
|
|
([LogLevel]::ERROR) { "Red" }
|
|
([LogLevel]::SUCCESS) { "Green" }
|
|
default { "White" }
|
|
}
|
|
|
|
Write-Host $logEntry -ForegroundColor $consoleColor
|
|
}
|
|
}
|
|
|
|
function Get-LogFilePath {
|
|
<#
|
|
.SYNOPSIS
|
|
Get the current log file path
|
|
|
|
.OUTPUTS
|
|
String containing the log file path
|
|
#>
|
|
return $global:LoggerConfig.LogFilePath
|
|
}
|
|
|
|
function Get-ErrorContext {
|
|
<#
|
|
.SYNOPSIS
|
|
Extract detailed error context from PowerShell error record
|
|
|
|
.DESCRIPTION
|
|
Provides comprehensive error information including message, location,
|
|
line number, command, and stack trace for better debugging.
|
|
|
|
.PARAMETER ErrorRecord
|
|
The error record to analyze (defaults to $_ in catch block)
|
|
|
|
.OUTPUTS
|
|
Hashtable with error details
|
|
|
|
.EXAMPLE
|
|
catch {
|
|
$errorContext = Get-ErrorContext -ErrorRecord $_
|
|
Write-Log -Level ERROR -Message $errorContext.Summary -Module "MyModule"
|
|
}
|
|
#>
|
|
[CmdletBinding()]
|
|
[OutputType([hashtable])]
|
|
param(
|
|
[Parameter(Mandatory = $false)]
|
|
[System.Management.Automation.ErrorRecord]$ErrorRecord = $_
|
|
)
|
|
|
|
$context = @{
|
|
Message = ""
|
|
Exception = ""
|
|
Category = ""
|
|
TargetObject = ""
|
|
ScriptName = ""
|
|
LineNumber = 0
|
|
Command = ""
|
|
StackTrace = ""
|
|
Summary = ""
|
|
}
|
|
|
|
if ($null -eq $ErrorRecord) {
|
|
$context.Summary = "No error record available"
|
|
return $context
|
|
}
|
|
|
|
# Extract basic error information
|
|
$context.Message = $ErrorRecord.Exception.Message
|
|
$context.Exception = $ErrorRecord.Exception.GetType().FullName
|
|
$context.Category = $ErrorRecord.CategoryInfo.Category.ToString()
|
|
$context.TargetObject = if ($ErrorRecord.TargetObject) { $ErrorRecord.TargetObject.ToString() } else { "N/A" }
|
|
|
|
# Extract script location information
|
|
if ($ErrorRecord.InvocationInfo) {
|
|
$context.ScriptName = if ($ErrorRecord.InvocationInfo.ScriptName) {
|
|
Split-Path -Leaf $ErrorRecord.InvocationInfo.ScriptName
|
|
} else {
|
|
"N/A"
|
|
}
|
|
$context.LineNumber = $ErrorRecord.InvocationInfo.ScriptLineNumber
|
|
$context.Command = if ($ErrorRecord.InvocationInfo.MyCommand) {
|
|
$ErrorRecord.InvocationInfo.MyCommand.Name
|
|
} else {
|
|
"N/A"
|
|
}
|
|
}
|
|
|
|
# Extract stack trace
|
|
if ($ErrorRecord.ScriptStackTrace) {
|
|
$context.StackTrace = $ErrorRecord.ScriptStackTrace
|
|
}
|
|
|
|
# Build comprehensive summary
|
|
$summary = "$($context.Message)"
|
|
|
|
if ($context.ScriptName -and $context.LineNumber -gt 0) {
|
|
$summary += " [File: $($context.ScriptName), Line: $($context.LineNumber)]"
|
|
}
|
|
|
|
if ($context.Command) {
|
|
$summary += " [Command: $($context.Command)]"
|
|
}
|
|
|
|
if ($context.Category -ne "NotSpecified") {
|
|
$summary += " [Category: $($context.Category)]"
|
|
}
|
|
|
|
$context.Summary = $summary
|
|
|
|
return $context
|
|
}
|
|
|
|
function Write-ErrorLog {
|
|
<#
|
|
.SYNOPSIS
|
|
Write a comprehensive error log entry with full context
|
|
|
|
.DESCRIPTION
|
|
Convenience function that combines error context extraction
|
|
and logging in one call. Provides detailed error information.
|
|
|
|
.PARAMETER Message
|
|
Custom error message (will be prefixed to error details)
|
|
|
|
.PARAMETER Module
|
|
Module or component name
|
|
|
|
.PARAMETER ErrorRecord
|
|
The error record to log (defaults to $_ in catch block)
|
|
|
|
.PARAMETER IncludeStackTrace
|
|
Include full stack trace in log (default: true)
|
|
|
|
.EXAMPLE
|
|
catch {
|
|
Write-ErrorLog -Message "Failed to apply security settings" -Module "SecurityBaseline" -ErrorRecord $_
|
|
}
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Message,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[string]$Module = "Framework",
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[System.Management.Automation.ErrorRecord]$ErrorRecord = $_,
|
|
|
|
[Parameter(Mandatory = $false)]
|
|
[bool]$IncludeStackTrace = $true
|
|
)
|
|
|
|
$errorContext = Get-ErrorContext -ErrorRecord $ErrorRecord
|
|
|
|
# Build comprehensive error message
|
|
$fullMessage = "$Message - $($errorContext.Summary)"
|
|
|
|
# Log error with basic info
|
|
Write-Log -Level ERROR -Message $fullMessage -Module $Module -Exception $ErrorRecord.Exception
|
|
|
|
# Log additional context if available
|
|
if ($IncludeStackTrace -and $errorContext.StackTrace) {
|
|
Write-Log -Level DEBUG -Message "Stack Trace: $($errorContext.StackTrace)" -Module $Module
|
|
}
|
|
}
|
|
|
|
# Note: Export-ModuleMember not used - this script is dot-sourced, not imported as module
|
|
# All functions are automatically available when dot-sourced
|