Assets/OpsUtils.psm1

# OpsUtils.psm1

$script:OpsLogLevels = @{
    "DEBUG"   = 0
    "INFO"    = 1
    "WARNING" = 2
    "ERROR"   = 3
}

function Write-OpsLog {
    <#
    .SYNOPSIS
        Writes a message to the transcript and to the job's NDJSON history file.
    .PARAMETER Message
        The message to log.
    .PARAMETER Level
        Severity. Messages below $env:OPS_LOG_LEVEL are dropped.
    #>

    param (
        [Parameter(Mandatory=$true)]
        [AllowEmptyString()]
        [string]$Message,

        [Parameter(Mandatory=$false)]
        [ValidateSet("DEBUG", "INFO", "WARNING", "ERROR")]
        [string]$Level = "INFO"
    )

    # An unrecognised OPS_LOG_LEVEL used to index to $null, which made every
    # comparison true (PowerShell) or false (Node) instead of falling back.
    $currentLevel = $env:OPS_LOG_LEVEL
    if ([string]::IsNullOrWhiteSpace($currentLevel) -or -not $script:OpsLogLevels.ContainsKey($currentLevel.ToUpperInvariant())) {
        $currentLevel = "INFO"
    }
    else {
        $currentLevel = $currentLevel.ToUpperInvariant()
    }

    if ($script:OpsLogLevels[$Level] -lt $script:OpsLogLevels[$currentLevel]) {
        return
    }

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMsg = "[$timestamp] [$Level] $Message"
    Write-Host $logMsg

    # JSON Logging (newline-delimited JSON, one object per line)
    if (-not [string]::IsNullOrWhiteSpace($env:OPS_LOG_FILE)) {
        $jsonLog = [pscustomobject]@{
            Timestamp = $timestamp
            Level     = $Level
            Message   = $Message
        } | ConvertTo-Json -Compress

        # Add-Content defaults to ASCII on PS 5.1, which mangles non-ASCII
        # messages into '?'. Retry briefly: concurrent runs of different jobs
        # share the directory and a reader may hold the file open.
        $written = $false
        for ($attempt = 0; $attempt -lt 5 -and -not $written; $attempt++) {
            try {
                Add-Content -Path $env:OPS_LOG_FILE -Value $jsonLog -Encoding UTF8 -ErrorAction Stop
                $written = $true
            }
            catch {
                Start-Sleep -Milliseconds 100
            }
        }
        if (-not $written) {
            Write-Warning "Could not append to history log '$env:OPS_LOG_FILE'."
        }
    }
}

Export-ModuleMember -Function Write-OpsLog