From 14ade7a01d8df75dc4a31346ac7bdda3717c771e Mon Sep 17 00:00:00 2001 From: mdaneri Date: Sat, 14 Dec 2024 15:47:59 -0800 Subject: [PATCH] Added server metrics --- docs/Getting-Started/Console.md | 67 ++++++++++++------- examples/server.psd1 | 28 ++++---- src/Locales/ar/Pode.psd1 | 6 ++ src/Locales/de/Pode.psd1 | 6 ++ src/Locales/en-us/Pode.psd1 | 6 ++ src/Locales/en/Pode.psd1 | 6 ++ src/Locales/es/Pode.psd1 | 6 ++ src/Locales/fr/Pode.psd1 | 6 ++ src/Locales/it/Pode.psd1 | 6 ++ src/Locales/ja/Pode.psd1 | 6 ++ src/Locales/ko/Pode.psd1 | 6 ++ src/Locales/nl/Pode.psd1 | 6 ++ src/Locales/pl/Pode.psd1 | 6 ++ src/Locales/pt/Pode.psd1 | 6 ++ src/Locales/zh/Pode.psd1 | 6 ++ src/Private/Console.ps1 | 111 +++++++++++++++++++++++++++++--- src/Private/Context.ps1 | 12 ++-- src/Private/Helpers.ps1 | 109 +++++++++++++++++++++++++++++++ src/Public/Metrics.ps1 | 87 ++++++++++++++++++++++--- 19 files changed, 434 insertions(+), 58 deletions(-) diff --git a/docs/Getting-Started/Console.md b/docs/Getting-Started/Console.md index 61f5a0193..6d6b5d4b9 100644 --- a/docs/Getting-Started/Console.md +++ b/docs/Getting-Started/Console.md @@ -48,6 +48,7 @@ Server Control Commands: Ctrl+h : Hide Help Ctrl+b : Open the first HTTP endpoint in the default browser. ---- + Ctrl+m : Show Metrics Ctrl+e : Hide Endpoints Ctrl+t : Hide OpenAPI Ctrl+l : Clear the Console @@ -62,15 +63,15 @@ The behavior, appearance, and functionality of the console are highly customizab ### Configurable Settings via `Start-PodeServer` -| **Parameter** | **Description** | -|-------------------------|-----------------------------------------------------------------------------------------------------| -| `DisableTermination` | Prevents termination, suspension, or resumption of the server via keyboard interactive commands. | -| `DisableConsoleInput` | Disables all console keyboard interactions for the server. | -| `ClearHost` | Clears the console whenever the server changes state (e.g., running → suspend → resume). | -| `Quiet` | Suppresses all console output for a clean execution experience. | -| `HideOpenAPI` | Hides OpenAPI details such as specification and documentation URLs in the console output. | -| `HideEndpoints` | Hides the list of active endpoints in the console output. | -| `ShowHelp` | Displays a help menu in the console with available control commands. | +| **Parameter** | **Description** | +|-----------------------|--------------------------------------------------------------------------------------------------| +| `DisableTermination` | Prevents termination, suspension, or resumption of the server via keyboard interactive commands. | +| `DisableConsoleInput` | Disables all console keyboard interactions for the server. | +| `ClearHost` | Clears the console whenever the server changes state (e.g., running → suspend → resume). | +| `Quiet` | Suppresses all console output for a clean execution experience. | +| `HideOpenAPI` | Hides OpenAPI details such as specification and documentation URLs in the console output. | +| `HideEndpoints` | Hides the list of active endpoints in the console output. | +| `ShowHelp` | Displays a help menu in the console with available control commands. | #### Example Usage @@ -120,6 +121,9 @@ Here is the default `Server.Console` configuration: HelpDescription = 'White' # Descriptions for each Help section key binding. HelpDivider = 'Gray' # Dividers used in the Help section. Divider = 'DarkGray' # Dividers between console sections. + MetricsHeader = 'Yellow' # Header for the Metric section. + MetricsLabel = 'White' # Labels for values displayed in the Metrics section. + MetricsValue = 'Green' # The actual values displayed in the Metrics section. } KeyBindings = @{ # Define custom key bindings for controls. @@ -133,6 +137,7 @@ Here is the default `Server.Console` configuration: Restart = 'r' # Restart the server. Disable = 'd' # Disable the server. Suspend = 'u' # Suspend the server. + Metrics = 'm' # Show Metrics. } } } @@ -190,20 +195,23 @@ The console colors are fully customizable via the `Colors` section of the config ### Color Settings -| **Key** | **Default Value** | **Description** | -|-----------------------|-------------------|---------------------------------------------------------------------------------| -| `Header` | `White` | The server's header section, including the Pode version and timestamp. | -| `EndpointsHeader` | `Yellow` | The header for the endpoints list. | -| `Endpoints` | `Cyan` | The endpoints themselves, including protocol and URLs. | -| `OpenApiUrls` | `Cyan` | URLs listed under the OpenAPI information section. | -| `OpenApiHeaders` | `Yellow` | Section headers for OpenAPI information. | -| `OpenApiTitles` | `White` | The OpenAPI "default" title. | -| `OpenApiSubtitles` | `Yellow` | Subtitles under OpenAPI (e.g., Specification, Documentation). | -| `HelpHeader` | `Yellow` | Header for the Help section. | -| `HelpKey` | `Green` | Key bindings listed in the Help section (e.g., `Ctrl+c`). | -| `HelpDescription` | `White` | Descriptions for each Help section key binding. | -| `HelpDivider` | `Gray` | Dividers used in the Help section. | -| `Divider` | `DarkGray` | Dividers between console sections. | +| **Key** | **Default Value** | **Description** | +|--------------------|-------------------|------------------------------------------------------------------------| +| `Header` | `White` | The server's header section, including the Pode version and timestamp. | +| `EndpointsHeader` | `Yellow` | The header for the endpoints list. | +| `Endpoints` | `Cyan` | The endpoints themselves, including protocol and URLs. | +| `OpenApiUrls` | `Cyan` | URLs listed under the OpenAPI information section. | +| `OpenApiHeaders` | `Yellow` | Section headers for OpenAPI information. | +| `OpenApiTitles` | `White` | The OpenAPI "default" title. | +| `OpenApiSubtitles` | `Yellow` | Subtitles under OpenAPI (e.g., Specification, Documentation). | +| `HelpHeader` | `Yellow` | Header for the Help section. | +| `HelpKey` | `Green` | Key bindings listed in the Help section (e.g., `Ctrl+c`). | +| `HelpDescription` | `White` | Descriptions for each Help section key binding. | +| `HelpDivider` | `Gray` | Dividers used in the Help section. | +| `Divider` | `DarkGray` | Dividers between console sections. | +| `MetricsHeader` | `Yellow` | Header for the Metrics section. | +| `MetricsLabel` | `White` | Labels for values displayed in the Metrics section. | +| `MetricsValue` | `Green` | The actual values displayed in the Metrics section. | > **Tip:** Test your chosen colors against your terminal's background to ensure readability. @@ -230,6 +238,7 @@ The console colors are fully customizable via the `Colors` section of the config - **Ctrl+d**: Disable the server, preventing new requests. - **Ctrl+u**: Suspend the server temporarily. - **Ctrl+h**: Display or hide help instructions. + - **Ctrl+m**: Display the server metrics. ### **4. OpenAPI Integration** @@ -282,3 +291,15 @@ Reduce the use of vibrant colors for a subtle appearance: } } ``` + +### **Change Metrics Section Colors** + +```powershell +@{ + Colors = @{ + MetricsHeader = 'Blue' # Change the header color to Blue + MetricsLabel = 'Gray' # Use Gray for the value labels + MetricsValue = 'Red' # Display metric values in Red + } +} +``` diff --git a/examples/server.psd1 b/examples/server.psd1 index db00bebcf..810278d89 100644 --- a/examples/server.psd1 +++ b/examples/server.psd1 @@ -84,18 +84,21 @@ DividerLength = 75 # Length of dividers in the console. ShowTimeStamp = $true # Display timestamp in the header. Colors = @{ # Customize console colors. - Header = 'White' - EndpointsHeader = 'Yellow' - Endpoints = 'Cyan' - OpenApiUrls = 'Cyan' - OpenApiHeaders = 'Yellow' - OpenApiTitles = 'White' - OpenApiSubtitles = 'Yellow' - HelpHeader = 'Yellow' - HelpKey = 'Green' - HelpDescription = 'White' - HelpDivider = 'Gray' - Divider = 'DarkGray' + Header = 'White' # The server's header section, including the Pode version and timestamp. + EndpointsHeader = 'Yellow' # The header for the endpoints list. + Endpoints = 'Cyan' # The endpoints themselves, including protocol and URLs. + OpenApiUrls = 'Cyan' # URLs listed under the OpenAPI information section. + OpenApiHeaders = 'Yellow' # Section headers for OpenAPI information. + OpenApiTitles = 'White' # The OpenAPI "default" title. + OpenApiSubtitles = 'Yellow' # Subtitles under OpenAPI (e.g., Specification, Documentation). + HelpHeader = 'Yellow' # Header for the Help section. + HelpKey = 'Green' # Key bindings listed in the Help section (e.g., Ctrl+c). + HelpDescription = 'White' # Descriptions for each Help section key binding. + HelpDivider = 'Gray' # Dividers used in the Help section. + Divider = 'DarkGray' # Dividers between console sections. + MetricsHeader = 'Yellow' # Header for the Metric section. + MetricsLabel = 'White' # Labels for values displayed in the Metrics section. + MetricsValue = 'Green' # The actual values displayed in the Metrics section. } KeyBindings = @{ # Define custom key bindings for controls. Browser = 'b' # Open the default browser. @@ -108,6 +111,7 @@ Restart = 'r' # Restart the server. Disable = 'd' # Disable the server. Suspend = 'u' # Suspend the server. + Metrics = 'm' # Show Metrics. } } } diff --git a/src/Locales/ar/Pode.psd1 b/src/Locales/ar/Pode.psd1 index 0d8c9ae90..a0f6cc2de 100644 --- a/src/Locales/ar/Pode.psd1 +++ b/src/Locales/ar/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'انتظار الإيقاف...' waitingforResumingMessage = 'انتظار الاستئناف...' terminatedMessage = 'تم الإنهاء' + showMetricsMessage = 'عرض المقاييس' + clearConsoleMessage = 'مسح وحدة التحكم' + serverMetricsMessage = 'مقاييس الخادم' + totalUptimeMessage = 'إجمالي وقت التشغيل:' + uptimeSinceLastRestartMessage = 'وقت التشغيل منذ آخر إعادة تشغيل:' + totalRestartMessage = 'إجمالي عدد عمليات إعادة التشغيل:' } diff --git a/src/Locales/de/Pode.psd1 b/src/Locales/de/Pode.psd1 index 58bdbf0da..0306491f3 100644 --- a/src/Locales/de/Pode.psd1 +++ b/src/Locales/de/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'Warten auf das Suspendieren ...' waitingforResumingMessage = 'Warten auf das Fortsetzen ...' terminatedMessage = 'Beendet' + showMetricsMessage = 'Metriken anzeigen' + clearConsoleMessage = 'Konsole löschen' + serverMetricsMessage = 'Servermetriken' + totalUptimeMessage = 'Gesamtlaufzeit:' + uptimeSinceLastRestartMessage = 'Laufzeit seit dem letzten Neustart:' + totalRestartMessage = 'Gesamtanzahl der Neustarts:' } \ No newline at end of file diff --git a/src/Locales/en-us/Pode.psd1 b/src/Locales/en-us/Pode.psd1 index 124a913a5..2248e7772 100644 --- a/src/Locales/en-us/Pode.psd1 +++ b/src/Locales/en-us/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'Waiting for suspending ...' waitingforResumingMessage = 'Waiting for resuming ...' terminatedMessage = 'Terminated' + showMetricsMessage = 'Show Metrics' + clearConsoleMessage = 'Clear the Console' + serverMetricsMessage = 'Server Metrics' + totalUptimeMessage = 'Total Uptime:' + uptimeSinceLastRestartMessage = 'Uptime Since Last Restart:' + totalRestartMessage = 'Total Number of Restart:' } \ No newline at end of file diff --git a/src/Locales/en/Pode.psd1 b/src/Locales/en/Pode.psd1 index 93f30ddb9..d5d901537 100644 --- a/src/Locales/en/Pode.psd1 +++ b/src/Locales/en/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'Waiting for suspending ...' waitingforResumingMessage = 'Waiting for resuming ...' terminatedMessage = 'Terminated' + showMetricsMessage = 'Show Metrics' + clearConsoleMessage = 'Clear the Console' + serverMetricsMessage = 'Server Metrics' + totalUptimeMessage = 'Total Uptime:' + uptimeSinceLastRestartMessage = 'Uptime Since Last Restart:' + totalRestartMessage = 'Total Number of Restart:' } \ No newline at end of file diff --git a/src/Locales/es/Pode.psd1 b/src/Locales/es/Pode.psd1 index c800506bd..06530011f 100644 --- a/src/Locales/es/Pode.psd1 +++ b/src/Locales/es/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'Esperando para suspender ...' waitingforResumingMessage = 'Esperando para reanudar ...' terminatedMessage = 'Terminado' + showMetricsMessage = 'Mostrar métricas' + clearConsoleMessage = 'Limpiar la consola' + serverMetricsMessage = 'Métricas del servidor' + totalUptimeMessage = 'Tiempo total de actividad:' + uptimeSinceLastRestartMessage = 'Tiempo de actividad desde el último reinicio:' + totalRestartMessage = 'Número total de reinicios:' } \ No newline at end of file diff --git a/src/Locales/fr/Pode.psd1 b/src/Locales/fr/Pode.psd1 index acbebea57..c4ff5cc12 100644 --- a/src/Locales/fr/Pode.psd1 +++ b/src/Locales/fr/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'En attente de la suspension ...' waitingforResumingMessage = 'En attente de la reprise ...' terminatedMessage = 'Terminé' + showMetricsMessage = 'Afficher les métriques' + clearConsoleMessage = 'Effacer la console' + serverMetricsMessage = 'Métriques du serveur' + totalUptimeMessage = 'Temps de fonctionnement total :' + uptimeSinceLastRestartMessage = 'Temps de fonctionnement depuis le dernier redémarrage :' + totalRestartMessage = 'Nombre total de redémarrages :' } \ No newline at end of file diff --git a/src/Locales/it/Pode.psd1 b/src/Locales/it/Pode.psd1 index 359e88fc5..4e8036432 100644 --- a/src/Locales/it/Pode.psd1 +++ b/src/Locales/it/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'Attesa per sospensione ...' waitingforResumingMessage = 'Attesa per ripresa ...' terminatedMessage = 'Terminato' + showMetricsMessage = 'Mostra metriche' + clearConsoleMessage = 'Cancella la console' + serverMetricsMessage = 'Metriche del server' + totalUptimeMessage = 'Tempo totale di attività:' + uptimeSinceLastRestartMessage = "Tempo di attività dall'ultimo riavvio:" + totalRestartMessage = 'Numero totale di riavvii:' } \ No newline at end of file diff --git a/src/Locales/ja/Pode.psd1 b/src/Locales/ja/Pode.psd1 index c3334c457..9346f8552 100644 --- a/src/Locales/ja/Pode.psd1 +++ b/src/Locales/ja/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = '停止待機中...' waitingforResumingMessage = '再開待機中...' terminatedMessage = '終了しました' + showMetricsMessage = 'メトリクスを表示' + clearConsoleMessage = 'コンソールをクリア' + serverMetricsMessage = 'サーバーメトリクス' + totalUptimeMessage = '総稼働時間:' + uptimeSinceLastRestartMessage = '最後の再起動からの稼働時間:' + totalRestartMessage = '再起動の総数:' } \ No newline at end of file diff --git a/src/Locales/ko/Pode.psd1 b/src/Locales/ko/Pode.psd1 index 922084822..830c13f39 100644 --- a/src/Locales/ko/Pode.psd1 +++ b/src/Locales/ko/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = '일시 정지 대기 중...' waitingforResumingMessage = '재개 대기 중...' terminatedMessage = '종료됨' + showMetricsMessage = '메트릭 표시' + clearConsoleMessage = '콘솔 지우기' + serverMetricsMessage = '서버 메트릭' + totalUptimeMessage = '총 가동 시간:' + uptimeSinceLastRestartMessage = '마지막 재시작 이후 가동 시간:' + totalRestartMessage = '총 재시작 횟수:' } \ No newline at end of file diff --git a/src/Locales/nl/Pode.psd1 b/src/Locales/nl/Pode.psd1 index 03964e386..ab1920990 100644 --- a/src/Locales/nl/Pode.psd1 +++ b/src/Locales/nl/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'Oczekiwanie na zawieszenie ...' waitingforResumingMessage = 'Oczekiwanie na wznowienie ...' terminatedMessage = 'Beëindigd' + showMetricsMessage = 'Toon statistieken' + clearConsoleMessage = 'Console wissen' + serverMetricsMessage = 'Serverstatistieken' + totalUptimeMessage = 'Totale uptime:' + uptimeSinceLastRestartMessage = 'Uptime sinds laatste herstart:' + totalRestartMessage = 'Totaal aantal herstarts:' } \ No newline at end of file diff --git a/src/Locales/pl/Pode.psd1 b/src/Locales/pl/Pode.psd1 index 0a572aa8d..179f5b1af 100644 --- a/src/Locales/pl/Pode.psd1 +++ b/src/Locales/pl/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'Oczekiwanie na zawieszenie ...' waitingforResumingMessage = 'Oczekiwanie na wznowienie ...' terminatedMessage = 'Zakończono' + showMetricsMessage = 'Pokaż metryki' + clearConsoleMessage = 'Wyczyść konsolę' + serverMetricsMessage = 'Metryki serwera' + totalUptimeMessage = 'Całkowity czas działania:' + uptimeSinceLastRestartMessage = 'Czas działania od ostatniego restartu:' + totalRestartMessage = 'Całkowita liczba restartów:' } \ No newline at end of file diff --git a/src/Locales/pt/Pode.psd1 b/src/Locales/pt/Pode.psd1 index ffba3bd94..d6f0605fe 100644 --- a/src/Locales/pt/Pode.psd1 +++ b/src/Locales/pt/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = 'Esperando para suspender ...' waitingforResumingMessage = 'Esperando para retomar ...' terminatedMessage = 'Terminado' + showMetricsMessage = 'Mostrar métricas' + clearConsoleMessage = 'Limpar o console' + serverMetricsMessage = 'Métricas do servidor' + totalUptimeMessage = 'Tempo total de atividade:' + uptimeSinceLastRestartMessage = 'Tempo de atividade desde o último reinício:' + totalRestartMessage = 'Número total de reinicializações:' } \ No newline at end of file diff --git a/src/Locales/zh/Pode.psd1 b/src/Locales/zh/Pode.psd1 index 9ee535716..3bd5d61d1 100644 --- a/src/Locales/zh/Pode.psd1 +++ b/src/Locales/zh/Pode.psd1 @@ -309,4 +309,10 @@ waitingforSuspendingMessage = '等待暂停...' waitingforResumingMessage = '等待恢复...' terminatedMessage = '已终止' + showMetricsMessage = '显示指标' + clearConsoleMessage = '清除控制台' + serverMetricsMessage = '服务器指标' + totalUptimeMessage = '总运行时间:' + uptimeSinceLastRestartMessage = '自上次重启后的运行时间:' + totalRestartMessage = '重启总次数:' } \ No newline at end of file diff --git a/src/Private/Console.ps1 b/src/Private/Console.ps1 index 4c41ddfa5..9a0f944a5 100644 --- a/src/Private/Console.ps1 +++ b/src/Private/Console.ps1 @@ -215,10 +215,10 @@ function Show-PodeConsoleHelp { [switch] $Hide ) - # Centralized key mapping + # Retrieve centralized key mapping for keyboard shortcuts $KeyBindings = $PodeContext.Server.Console.KeyBindings - # Define help section color variables + # Define help section color variables with fallback defaults $helpHeaderColor = if ($null -ne $PodeContext.Server.Console.Colors.HelpHeader) { $PodeContext.Server.Console.Colors.HelpHeader } @@ -247,13 +247,14 @@ function Show-PodeConsoleHelp { [System.ConsoleColor]::Gray } - # Show concise "Show Help" option if $Hide is true + # Display the "Show Help" option if the $Hide parameter is specified if ($Hide) { Write-PodeHost " Ctrl+$($KeyBindings.Help) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force Write-PodeHost 'Show Help' -ForegroundColor $helpDescriptionColor -Force:$Force } else { - # Determine resume or suspend message based on server state + # Determine the text for resuming or suspending the server based on its state + $resumeOrSuspend = if ($serverState -eq 'Suspended') { $Podelocale.ResumeServerMessage } @@ -261,10 +262,10 @@ function Show-PodeConsoleHelp { $Podelocale.SuspendServerMessage } - # Enable or disable server state message + # Determine whether to display "Enable" or "Disable Server" based on the server state $enableOrDisable = if (Test-PodeServerIsEnabled) { 'Disable Server' } else { 'Enable Server' } - # Display help header + # Display the header for the help section Write-PodeHost $Podelocale.serverControlCommandsTitle -ForegroundColor $helpHeaderColor -Force:$Force if ($headerSeparator) { @@ -272,7 +273,7 @@ function Show-PodeConsoleHelp { Write-PodeHostDivider -Force $true } - # Display help commands + # Display key bindings and their descriptions if (!$PodeContext.Server.Console.DisableTermination) { Write-PodeHost " Ctrl+$($KeyBindings.Terminate) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force Write-PodeHost "$($Podelocale.GracefullyTerminateMessage)" -ForegroundColor $helpDescriptionColor -Force:$Force @@ -296,26 +297,36 @@ function Show-PodeConsoleHelp { Write-PodeHost " Ctrl+$($KeyBindings.Help) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force Write-PodeHost 'Hide Help' -ForegroundColor $helpDescriptionColor -Force:$Force + # If an HTTP endpoint exists and the server is running, display the browser shortcut if ((Get-PodeEndpointUrl) -and ($serverState -ne 'Suspended')) { Write-PodeHost " Ctrl+$($KeyBindings.Browser) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force - Write-PodeHost "$($Podelocale.OpenHttpEndpointMessage)" -ForegroundColor $helpDescriptionColor -Force:$Force + Write-PodeHost $Podelocale.OpenHttpEndpointMessage -ForegroundColor $helpDescriptionColor -Force:$Force } + # Display a divider for grouping commands Write-PodeHost ' ----' -ForegroundColor $helpDividerColor -Force:$Force + # Show metrics only if the server is running or suspended + if (('Running', 'Suspended') -contains $serverState ) { + Write-PodeHost " Ctrl+$($KeyBindings.Metrics) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force + Write-PodeHost $Podelocale.showMetricsMessage -ForegroundColor $helpDescriptionColor -Force:$Force + } + + # Show endpoints and OpenAPI only if the server is running if ($serverState -eq 'Running') { Write-PodeHost " Ctrl+$($KeyBindings.Endpoints) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force Write-PodeHost "$(if ($PodeContext.Server.Console.ShowEndpoints) { 'Hide' } else { 'Show' }) Endpoints" -ForegroundColor $helpDescriptionColor -Force:$Force - # Check if OpenApi are in use + # Check if OpenAPI is enabled and display its toggle option if (Test-PodeOAEnabled) { Write-PodeHost " Ctrl+$($KeyBindings.Quiet) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force Write-PodeHost "$(if ($PodeContext.Server.Console.ShowOpenAPI) { 'Hide' } else { 'Show' }) OpenAPI" -ForegroundColor $helpDescriptionColor -Force:$Force } } + # Display the Clear Console and Quiet Mode options Write-PodeHost " Ctrl+$($KeyBindings.Clear) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force - Write-PodeHost 'Clear the Console' -ForegroundColor $helpDescriptionColor -Force:$Force + Write-PodeHost $Podelocale.clearConsoleMessage -ForegroundColor $helpDescriptionColor -Force:$Force Write-PodeHost " Ctrl+$($KeyBindings.Quiet) : " -ForegroundColor $helpKeyColor -NoNewLine -Force:$Force Write-PodeHost "$(if ($PodeContext.Server.Console.Quiet) { 'Disable' } else { 'Enable' }) Quiet Mode" -ForegroundColor $helpDescriptionColor -Force:$Force @@ -478,6 +489,78 @@ function Show-PodeConsoleEndpointsInfo { Write-PodeHostDivider -Force $true } +<# +.SYNOPSIS + Displays metrics for the Pode server in the console. + +.DESCRIPTION + This function outputs various server metrics, such as uptime and restart counts, + to the Pode console with styled colors based on the Pode context. The function + ensures a visually clear representation of the metrics for quick monitoring. + +.EXAMPLE + Show-PodeConsoleMetric + + This command displays the Pode server metrics in the console with the + appropriate headers, labels, and values styled using Pode-defined colors. + +.NOTES + This function depends on the PodeContext and related server configurations + for retrieving metrics and console colors. Ensure that Pode is running and + configured correctly. + +.OUTPUTS + None. This function writes output directly to the console. + +#> +function Show-PodeConsoleMetric { + # Determine the color for the metrics header + if ($null -ne $PodeContext.Server.Console.Colors.EndpointsHeader) { + $headerColor = $PodeContext.Server.Console.Colors.MetricsHeader + } + else { + $headerColor = [System.ConsoleColor]::White + } + + # Determine the color for the metrics labels + if ($null -ne $PodeContext.Server.Console.Colors.Endpoints) { + $labelColor = $PodeContext.Server.Console.Colors.MetricsLabel + } + else { + $labelColor = [System.ConsoleColor]::Yellow + } + + # Determine the color for the metrics values + if ($null -ne $PodeContext.Server.Console.Colors.Endpoints) { + $valueColor = $PodeContext.Server.Console.Colors.MetricsValue + } + else { + $valueColor = [System.ConsoleColor]::Green + } + + # Write a horizontal divider line to separate the header + Write-PodeHostDivider -Force $true + + # Write the metrics header with the current timestamp + Write-PodeHost "$($Podelocale.serverMetricsMessage) [$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')]" -ForegroundColor $headerColor + + # Write another horizontal divider line for separation + Write-PodeHostDivider -Force $true + + # Display the total uptime + Write-PodeHost $Podelocale.totalUptimeMessage -ForegroundColor $labelColor -NoNewLine + Write-PodeHost (Get-PodeServerUptime -Total -Readable -OutputType Verbose -ExcludeMilliseconds) -ForegroundColor $valueColor + + # If uptime exceeds 1000 seconds, display uptime since last restart + if ((Get-PodeServerUptime) -gt 1000) { + Write-PodeHost $Podelocale.uptimeSinceLastRestartMessage -ForegroundColor $labelColor -NoNewLine + Write-PodeHost (Get-PodeServerUptime -Readable -OutputType Verbose -ExcludeMilliseconds) -ForegroundColor $valueColor + } + + # Display the total number of server restarts + Write-PodeHost $Podelocale.totalRestartMessage -ForegroundColor $labelColor -NoNewLine + Write-PodeHost (Get-PodeServerRestartCount) -ForegroundColor $valueColor +} <# @@ -771,6 +854,10 @@ function Invoke-PodeConsoleAction { Set-PodeCancellationTokenRequest -Type Terminate return } + elseif ( (('Running', 'Suspended') -contains $serverState ) -and (Test-PodeKeyPressed -Key $Key -Character $KeyBindings.Metrics)) { + Clear-PodeKeyPressed + Show-PodeConsoleMetric + } # Handle restart actions if ($PodeContext.Server.AllowedActions.Restart) { @@ -878,6 +965,9 @@ function Get-PodeDefaultConsole { HelpDescription = 'White' # Descriptions for each Help section key binding. HelpDivider = 'Gray' # Dividers used in the Help section. Divider = 'DarkGray' # Dividers between console sections. + MetricsHeader = 'Yellow' # Header for the Metric section. + MetricsLabel = 'White' # Labels for values displayed in the Metrics section. + MetricsValue = 'Green' # The actual values displayed in the Metrics section. } KeyBindings = @{ # Define custom key bindings for controls. @@ -891,6 +981,7 @@ function Get-PodeDefaultConsole { Restart = 'r' # Restart the server. Disable = 'd' # Disable the server. Suspend = 'u' # Suspend the server. + Metrics = 'm' # Show Metrics. } } } \ No newline at end of file diff --git a/src/Private/Context.ps1 b/src/Private/Context.ps1 index 363742b38..e6f792630 100644 --- a/src/Private/Context.ps1 +++ b/src/Private/Context.ps1 @@ -388,8 +388,8 @@ function New-PodeContext { Restart = $true Disable = $true DisableSettings = @{ - RetryAfter = 3600 - MiddlewareName = '__Pode_Midleware_Code_503' + RetryAfter = 3600 + MiddlewareName = '__Pode_Midleware_Code_503' } Timeout = @{ Suspend = 30 @@ -936,8 +936,8 @@ function Set-PodeServerConfiguration { Restart = [bool](Protect-PodeValue -Value $Configuration.AllowedActions.Restart -Default $Context.Server.AllowedActions.Restart) Disable = [bool](Protect-PodeValue -Value $Configuration.AllowedActions.Disable -Default $Context.Server.AllowedActions.Disable) DisableSettings = @{ - RetryAfter = [int](Protect-PodeValue -Value $Configuration.AllowedActions.DisableSettings.RetryAfter -Default $Context.Server.AllowedActions.DisableSettings.RetryAfter) - MiddlewareName = (Protect-PodeValue -Value $Configuration.AllowedActions.DisableSettings.MiddlewareName -Default $Context.Server.AllowedActions.DisableSettings.MiddlewareName) + RetryAfter = [int](Protect-PodeValue -Value $Configuration.AllowedActions.DisableSettings.RetryAfter -Default $Context.Server.AllowedActions.DisableSettings.RetryAfter) + MiddlewareName = (Protect-PodeValue -Value $Configuration.AllowedActions.DisableSettings.MiddlewareName -Default $Context.Server.AllowedActions.DisableSettings.MiddlewareName) } Timeout = @{ Suspend = [int](Protect-PodeValue -Value $Configuration.AllowedActions.Timeout.Suspend -Default $Context.Server.AllowedActions.Timeout.Suspend) @@ -968,6 +968,9 @@ function Set-PodeServerConfiguration { HelpDescription = [System.ConsoleColor]::parse([System.ConsoleColor], (Protect-PodeValue -Value $Configuration.Console.Colors.HelpDescription -Default $Context.Server.Console.Colors.HelpDescription), $true) HelpDivider = [System.ConsoleColor]::parse([System.ConsoleColor], (Protect-PodeValue -Value $Configuration.Console.Colors.HelpDivider -Default $Context.Server.Console.Colors.HelpDivider), $true) Divider = [System.ConsoleColor]::parse([System.ConsoleColor], (Protect-PodeValue -Value $Configuration.Console.Colors.Divider -Default $Context.Server.Console.Colors.Divider), $true) + MetricsHeader = [System.ConsoleColor]::parse([System.ConsoleColor], (Protect-PodeValue -Value $Configuration.Console.Colors.MetricsHeader -Default $Context.Server.Console.Colors.MetricsHeader), $true) + MetricsLabel = [System.ConsoleColor]::parse([System.ConsoleColor], (Protect-PodeValue -Value $Configuration.Console.Colors.MetricsLabel -Default $Context.Server.Console.Colors.MetricsLabel), $true) + MetricsValue = [System.ConsoleColor]::parse([System.ConsoleColor], (Protect-PodeValue -Value $Configuration.Console.Colors.MetricsValue -Default $Context.Server.Console.Colors.MetricsValue), $true) } KeyBindings = @{ Browser = (Protect-PodeValue -Value $Configuration.Console.KeyBindings.Browser -Default $Context.Server.Console.KeyBindings.Browser) @@ -980,6 +983,7 @@ function Set-PodeServerConfiguration { Restart = (Protect-PodeValue -Value $Configuration.Console.KeyBindings.Restart -Default $Context.Server.Console.KeyBindings.Restart) Disable = (Protect-PodeValue -Value $Configuration.Console.KeyBindings.Disable -Default $Context.Server.Console.KeyBindings.Disable) Suspend = (Protect-PodeValue -Value $Configuration.Console.KeyBindings.Suspend -Default $Context.Server.Console.KeyBindings.Suspend) + Metrics = (Protect-PodeValue -Value $Configuration.Console.KeyBindings.Metrics -Default $Context.Server.Console.KeyBindings.Metrics) } } diff --git a/src/Private/Helpers.ps1 b/src/Private/Helpers.ps1 index cfb7ffed7..e49cc8890 100644 --- a/src/Private/Helpers.ps1 +++ b/src/Private/Helpers.ps1 @@ -3760,3 +3760,112 @@ function Copy-PodeObjectDeepClone { return [System.Management.Automation.PSSerializer]::Deserialize($xmlSerializer) } } + + +<# +.SYNOPSIS + Converts a duration in milliseconds into a human-readable time format. + +.DESCRIPTION + This function takes an input duration in milliseconds and converts it into + a readable time format. It supports multiple output styles, such as verbose + (detailed text), compact (`dd:hh:mm:ss`), and concise (short notation). + Optionally, milliseconds can be excluded from the output. + +.PARAMETER Milliseconds + The duration in milliseconds to be converted. + +.PARAMETER VerboseOutput + If specified, outputs a detailed, descriptive format (e.g., "1 day, 2 hours, 3 minutes"). + +.PARAMETER CompactOutput + If specified, outputs a compact format (e.g., "dd:hh:mm:ss"). + +.PARAMETER ExcludeMilliseconds + If specified, excludes milliseconds from the output. + +.EXAMPLE + Convert-PodeMillisecondsToReadable -Milliseconds 123456789 + + Output: + 1d 10h 17m 36s + +.EXAMPLE + Convert-PodeMillisecondsToReadable -Milliseconds 123456789 -VerboseOutput + + Output: + 1 day, 10 hours, 17 minutes, 36 seconds, 789 milliseconds + +.EXAMPLE + Convert-PodeMillisecondsToReadable -Milliseconds 123456789 -CompactOutput -ExcludeMilliseconds + + Output: + 01:10:17:36 + +.NOTES + This is an internal function and may change in future releases of Pode. +#> + +function Convert-PodeMillisecondsToReadable { + param ( + [Parameter(Mandatory)] + [long]$Milliseconds, + + [switch]$VerboseOutput, # Provide detailed descriptions + [switch]$CompactOutput, # Provide compact format like dd:hh:mm:ss or mm:ss:ms + [switch]$ExcludeMilliseconds # Exclude milliseconds from the output + ) + + $timeSpan = [timespan]::FromMilliseconds($Milliseconds) + + if ($CompactOutput) { + # Dynamically build compact format + $components = @() + + # Include days only if greater than 0 + if ($timeSpan.Days -gt 0) { $components += '{0:D2}' -f $timeSpan.Days } + + # Include hours only if greater than 0 or days are included + if ($timeSpan.Hours -gt 0 -or $components.Count -gt 0) { $components += '{0:D2}' -f $timeSpan.Hours } + + # Include minutes if relevant + if ($timeSpan.Minutes -gt 0 -or $components.Count -gt 0) { $components += '{0:D2}' -f $timeSpan.Minutes } + + # Add seconds as the final required time component + $components += '{0:D2}' -f $timeSpan.Seconds + + # Append milliseconds if not excluded + if (-not $ExcludeMilliseconds) { + $components[-1] += ':{0:D3}' -f $timeSpan.Milliseconds + } + + # Join with ":" and return + return $components -join ':' + } + + # Default or verbose format + if ($VerboseOutput) { + $verboseParts = @() + if ($timeSpan.Days -gt 0) { $verboseParts += "$($timeSpan.Days) day$(if ($timeSpan.Days -ne 1) { 's' })" } + if ($timeSpan.Hours -gt 0) { $verboseParts += "$($timeSpan.Hours) hour$(if ($timeSpan.Hours -ne 1) { 's' })" } + if ($timeSpan.Minutes -gt 0) { $verboseParts += "$($timeSpan.Minutes) minute$(if ($timeSpan.Minutes -ne 1) { 's' })" } + if ($timeSpan.Seconds -gt 0) { $verboseParts += "$($timeSpan.Seconds) second$(if ($timeSpan.Seconds -ne 1) { 's' })" } + if (-not $ExcludeMilliseconds -and $timeSpan.Milliseconds -gt 0) { + $verboseParts += "$($timeSpan.Milliseconds) millisecond$(if ($timeSpan.Milliseconds -ne 1) { 's' })" + } + + return $verboseParts -join ' ' + } + + # Default concise format + $parts = @() + if ($timeSpan.Days -gt 0) { $parts += "$($timeSpan.Days)d" } + if ($timeSpan.Hours -gt 0 -or $parts.Count -gt 0) { $parts += "$($timeSpan.Hours)h" } + if ($timeSpan.Minutes -gt 0 -or $parts.Count -gt 0) { $parts += "$($timeSpan.Minutes)m" } + if ($timeSpan.Seconds -gt 0 -or $parts.Count -gt 0) { $parts += "$($timeSpan.Seconds)s" } + if (-not $ExcludeMilliseconds -and $timeSpan.Milliseconds -gt 0 -or $parts.Count -gt 0) { + $parts += "$($timeSpan.Milliseconds)ms" + } + + return $parts -join ':' +} diff --git a/src/Public/Metrics.ps1 b/src/Public/Metrics.ps1 index 0d47243d4..2d42b13e4 100644 --- a/src/Public/Metrics.ps1 +++ b/src/Public/Metrics.ps1 @@ -1,35 +1,104 @@ <# .SYNOPSIS -Returns the uptime of the server in milliseconds. + Returns the uptime of the server in milliseconds or in a human-readable format. .DESCRIPTION -Returns the uptime of the server in milliseconds. You can optionally return the total uptime regardless of server restarts. + Returns the uptime of the server in milliseconds by default. You can optionally return the total uptime regardless of server restarts or convert the uptime to a human-readable format with selectable output styles (e.g., Verbose, Compact). + Additionally, milliseconds can be excluded from the output if desired. .PARAMETER Total -If supplied, the total uptime of the server will be returned, regardless of restarts. + If supplied, the total uptime of the server will be returned, regardless of restarts. + +.PARAMETER Readable + If supplied, the uptime will be returned in a human-readable format instead of milliseconds. + +.PARAMETER OutputType + Specifies the format for the human-readable output. Valid options are: + - 'Verbose' for detailed descriptions (e.g., "1 day, 2 hours, 3 minutes"). + - 'Compact' for a compact format (e.g., "dd:hh:mm:ss"). + - Default is concise format (e.g., "1d 2h 3m"). + +.PARAMETER ExcludeMilliseconds + If supplied, milliseconds will be excluded from the human-readable output. + +.EXAMPLE + $currentUptime = Get-PodeServerUptime + # Output: 123456789 (milliseconds) + +.EXAMPLE + $totalUptime = Get-PodeServerUptime -Total + # Output: 987654321 (milliseconds) .EXAMPLE -$currentUptime = Get-PodeServerUptime + $readableUptime = Get-PodeServerUptime -Readable + # Output: "1d 10h 17m 36s" .EXAMPLE -$totalUptime = Get-PodeServerUptime -Total + $verboseUptime = Get-PodeServerUptime -Readable -OutputType Verbose + # Output: "1 day, 10 hours, 17 minutes, 36 seconds, 789 milliseconds" + +.EXAMPLE + $compactUptime = Get-PodeServerUptime -Readable -OutputType Compact + # Output: "01:10:17:36" + +.EXAMPLE + $compactUptimeNoMs = Get-PodeServerUptime -Readable -OutputType Compact -ExcludeMilliseconds + # Output: "01:10:17:36" #> function Get-PodeServerUptime { - [CmdletBinding()] - [OutputType([long])] + [CmdletBinding(DefaultParameterSetName = 'Milliseconds')] + [OutputType([long], [string])] param( + # Common to all parameter sets [switch] - $Total + $Total, + + # Default set: Milliseconds output + [Parameter(ParameterSetName = 'Readable')] + [switch] + $Readable, + + # Available only when -Readable is specified + [Parameter(ParameterSetName = 'Readable')] + [ValidateSet("Verbose", "Compact", "Default")] + [string] + $OutputType = "Default", + + # Available only when -Readable is specified + [Parameter(ParameterSetName = 'Readable')] + [switch] + $ExcludeMilliseconds ) + # Determine the appropriate start time $time = $PodeContext.Metrics.Server.StartTime if ($Total) { $time = $PodeContext.Metrics.Server.InitialLoadTime } - return [long]([datetime]::UtcNow - $time).TotalMilliseconds + # Calculate uptime in milliseconds + $uptimeMilliseconds = [long]([datetime]::UtcNow - $time).TotalMilliseconds + + # Handle readable output + if ($PSCmdlet.ParameterSetName -eq 'Readable') { + switch ($OutputType) { + "Verbose" { + return Convert-PodeMillisecondsToReadable -Milliseconds $uptimeMilliseconds -VerboseOutput -ExcludeMilliseconds:$ExcludeMilliseconds + } + "Compact" { + return Convert-PodeMillisecondsToReadable -Milliseconds $uptimeMilliseconds -CompactOutput -ExcludeMilliseconds:$ExcludeMilliseconds + } + "Default" { + return Convert-PodeMillisecondsToReadable -Milliseconds $uptimeMilliseconds -ExcludeMilliseconds:$ExcludeMilliseconds + } + } + } + + # Default to milliseconds if no readable output is requested + return $uptimeMilliseconds } + <# .SYNOPSIS Returns the number of times the server has restarted.