GinShell.Logging/Public/Write-GsLokiLog.ps1

function Write-GsLokiLog {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [ValidateSet('verbose', 'debug', 'action', 'success', 'info', 'warning', 'error', 'critical')]
        [string]$Type = 'info',

        [Parameter()]
        [hashtable]$Labels,

        [Parameter()]
        [string]$LokiUri
    )

    $resolvedUri = $LokiUri
    if ([string]::IsNullOrWhiteSpace($resolvedUri)) { $resolvedUri = $global:GsLokiUri }
    if ([string]::IsNullOrWhiteSpace($resolvedUri)) { $resolvedUri = $env:GS_LOKI_URI }
    if ([string]::IsNullOrWhiteSpace($resolvedUri)) { Write-Error "The loki endpoint is not set yet, hence unable to send log to loki endpoint"; return }

    $finalLabels = @{}

    if ($global:GsLokiLabels -is [hashtable]) {
        $global:GsLokiLabels.GetEnumerator() | ForEach-Object { $finalLabels[$_.Key] = $_.Value }
    }

    if (-not [string]::IsNullOrWhiteSpace($env:GS_LOKI_LABELS)) {
        try {
            $envLabels = $env:GS_LOKI_LABELS | ConvertFrom-Json -AsHashtable
            $envLabels.GetEnumerator() | ForEach-Object { $finalLabels[$_.Key] = $_.Value }
        }
        catch {
            Write-Warning "Invalid JSON in GS_LOKI_LABELS environment variable: $($env:GS_LOKI_LABELS)"
        }
    }

    if ($Labels -is [hashtable]) {
        $Labels.GetEnumerator() | ForEach-Object { $finalLabels[$_.Key] = $_.Value }
    }

    if (-not $finalLabels.ContainsKey('type')) { $finalLabels['type'] = $Type }
    if (-not $finalLabels.ContainsKey('hostname')) { $finalLabels['hostname'] = $env:COMPUTERNAME }
    if (-not $finalLabels.ContainsKey('script_path')) { $finalLabels['script'] = if ($MyInvocation.ScriptName) { $MyInvocation.ScriptName } else { 'Interactive_powershell' } }
    if (-not $finalLabels.ContainsKey('run_as')) { $finalLabels['run_as'] = $env:USERNAME }
    if (-not $finalLabels.ContainsKey('powershell_version')) { $finalLabels['powershell_version'] = $PSVersionTable.PSVersion.ToString() }

    if (-not $finalLabels.ContainsKey('app')) {
        if ($MyInvocation.ScriptName) {
            $finalLabels['app'] = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.ScriptName)
        }
        else {
            $finalLabels['app'] = 'Interactive_PowerShell'
        }
    }

    # Getting Time Stamp in Nanoseconds
    $timestampNs = if ($PSVersionTable.PSVersion.Major -ge 6) {
        [bigint](([datetimeoffset]::UtcNow) - ([datetimeoffset]::UnixEpoch)).Ticks * 100
    }
    else {
        $unixEpoch = New-Object DateTime(1970, 1, 1, 0, 0, 0, [System.DateTimeKind]::Utc)
        $timeSpan = (Get-Date).ToUniversalTime() - $unixEpoch
        ($timeSpan.Ticks * 100).ToString('F0')
    }

    $payload = @{
        streams = @(
            @{
                stream = $finalLabels
                values = @(
                    , @([string]$timestampNs, $Message)
                )
            }
        )
    }

    $jsonPayload = $payload | ConvertTo-Json -Depth 5 -Compress
    $pushUrl = "$($resolvedUri.TrimEnd('/'))/loki/api/v1/push"

    try {
        $output = Invoke-RestMethod -Uri $pushUrl -Method Post -Body $jsonPayload -ContentType 'application/json' -ErrorAction Stop
        Write-Verbose "Loki log sent to $pushUrl and received response: $($output | ConvertTo-Json -Depth 5)"
    }
    catch {
        Write-Error "Loki log failed: $($_.Exception.Message)"
        Write-Debug "Payload: $jsonPayload"
    }
}