Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Enhanced Diagnostic Dump Feature, Suspend-PodeServer and Resume-PodeServer #1443

Open
wants to merge 32 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7ffe56c
Add Enhanced Diagnostic Dump Feature with Configurable Output Format…
mdaneri Nov 2, 2024
8cc7d02
add pipe example
mdaneri Nov 2, 2024
6121cb5
various improvements
mdaneri Nov 3, 2024
d362833
Merge branch 'develop' into invoke-podedump
mdaneri Nov 3, 2024
dbf54d9
Fixes and improvements
mdaneri Nov 4, 2024
14afc55
Update Helpers.ps1
mdaneri Nov 4, 2024
34d7b6b
added variables from runspace
mdaneri Nov 5, 2024
f05adda
Update Dump.ps1
mdaneri Nov 5, 2024
b64da21
Added suspend and resume server
mdaneri Nov 5, 2024
7db258a
internazioanalization+ example
mdaneri Nov 5, 2024
88642e8
Update PetData.json
mdaneri Nov 5, 2024
65836b5
Documentation
mdaneri Nov 6, 2024
52574f9
upgrade powershell to '7.2.24'
mdaneri Nov 6, 2024
0bd3f8a
Update Server.Tests.ps1
mdaneri Nov 6, 2024
7dd5f96
Merge branch 'develop' into invoke-podedump
mdaneri Nov 6, 2024
6135e17
fixes
mdaneri Nov 8, 2024
82a6020
Fix CTRL B issue
mdaneri Nov 11, 2024
49f962b
fixes
mdaneri Nov 13, 2024
70a8696
Merge branch 'develop' into invoke-podedump
Badgerati Nov 22, 2024
82c2a28
add net9
mdaneri Nov 22, 2024
6ec63d9
Merge remote-tracking branch 'upstream/develop' into invoke-podedump
mdaneri Nov 23, 2024
e41c207
Merge remote-tracking branch 'upstream/develop' into invoke-podedump
mdaneri Nov 25, 2024
f0c8b45
doc fixes
mdaneri Nov 25, 2024
dc76df3
first review
mdaneri Nov 30, 2024
c8ab57c
second update
mdaneri Dec 1, 2024
79fdf46
suspend http and tcp
mdaneri Dec 1, 2024
900001a
fix tests
mdaneri Dec 2, 2024
526325c
WSS suspension
mdaneri Dec 2, 2024
ce5cc01
Fix suspend
mdaneri Dec 2, 2024
9cdaf4e
Update DebuggerHandler.cs
mdaneri Dec 2, 2024
414d785
minor changes
mdaneri Dec 4, 2024
68f14b0
tentative to use a new terminate token
mdaneri Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,6 @@ examples/PetStore/data/PetData.json
packers/choco/pode.nuspec
packers/choco/tools/ChocolateyInstall.ps1
docs/Getting-Started/Samples.md

# Dump Folder
Dump
98 changes: 98 additions & 0 deletions docs/Getting-Started/Debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,27 @@ Start-PodeServer -EnableBreakpoints {
The steps to attach to the Pode process are as follows:

1. In a PowerShell console, start the above Pode server. You will see the following output, and you'll need the PID that is shown:

```plain
Pode v2.10.0 (PID: 28324)
Listening on the following 1 endpoint(s) [1 thread(s)]:
- http://localhost:8080/
```

2. In a browser or a new PowerShell console, invoke the `[GET] http://localhost:8080` Route to hit the breakpoint.

```powershell
Invoke-RestMethod -Uri 'http://localhost:8080/'
```

3. Open another new PowerShell console, and run the following command to enter the first PowerShell console running Pode - you'll need the PID as well:

```powershell
Enter-PSHostProcess -Id '<PID_HERE>'
```

4. Once you have entered the PowerShell console running Pode, run the below command to attach to the breakpoint:

```powershell
Get-Runspace |
Where-Object { $_.Debugger.InBreakpoint } |
Expand All @@ -101,6 +105,7 @@ The steps to attach to the Pode process are as follows:
7. When you are done debugging the current request, hit the `d` key.

8. When you're done with debugging altogether, you can exit the entered process as follows:

```powershell
exit
```
Expand All @@ -110,6 +115,7 @@ The steps to attach to the Pode process are as follows:
If you're using [`Wait-PodeDebugger`](../../Functions/Core/Wait-PodeDebugger) then you can leave these breakpoint lines in place, and toggle them in non-developer environments by passing `-EnableBreakpoints` to [`Start-PodeServer`](../../Functions/Core/Start-PodeServer). If you don't supply `-EnableBreakpoints`, or you explicitly pass `-EnableBreakpoints:$false`, then this will disable the breakpoints from being set.

You can also toggle breakpoints via the `server.psd1` [configuration file](../../Tutorials/Configuration):

```powershell
@{
Server = @{
Expand Down Expand Up @@ -158,6 +164,7 @@ The steps to attach to the Pode process are as follows:
1. In a PowerShell console, start the above Pode server.

2. In a browser or a new PowerShell console, invoke the `[GET] http://localhost:8080` Route to hit the breakpoint.

```powershell
Invoke-RestMethod -Uri 'http://localhost:8080/'
```
Expand Down Expand Up @@ -246,3 +253,94 @@ Add-PodeSchedule -Name 'TestSchedule' -Cron '@hourly' -ScriptBlock {
```

In this example, the schedule outputs the name of the runspace executing the script block every hour. This can be useful for logging and monitoring purposes when dealing with multiple schedules or tasks.


## Memory Dump for Diagnostics

Pode provides a powerful memory dump feature to capture detailed information during critical failures or fatal exceptions. This feature, triggered by the `Invoke-PodeDump` function, captures the state of your application, including memory usage, runspace details, variables, and stack traces. You can configure the dump format, enable or disable it, and specify the save location. By default, Pode saves the dump in JSON format, but you can also choose from other supported formats.

### Configuring Dump Defaults

To set up default options for the memory dump feature in Pode, you can configure them in the `server.psd1` configuration file. Under the `Server.Debug.Dump` section, you can specify whether to enable the dump, the default format, and the save path. Below is an example configuration for setting up defaults:

```powershell
@{
Server = @{
Debug = @{
Breakpoints = @{
Enable = $true
}
mdaneri marked this conversation as resolved.
Show resolved Hide resolved
Dump = @{
Enable = $true
Format = 'Yaml' # Options: 'json', 'clixml', 'txt', 'bin', 'yaml'
Path = './Dump' # Path to save the dump files
MaxDepth = 6
}
}
}
}
```

- **Enable**: Boolean value to enable or disable the memory dump feature.
- **Format**: Specifies the default format for the dump file. Supported formats are `json`, `clixml`, `txt`, `bin`, and `yaml`.
- **Path**: Specifies the directory where the dump file will be saved. If the directory does not exist, it will be created.
- **MaxDepth**: Specifies the maximum depth to traverse when collecting information.


### Overriding Default Settings at Runtime

The `Invoke-PodeDump` function allows you to override these defaults at runtime by passing parameters to specify the format and path. This can be useful for debugging in specific cases without altering the default configuration.

```powershell
try {
# Simulate a critical error
throw [System.OutOfMemoryException] "Simulated out of memory error"
}
catch {
# Capture the dump with custom options
Invoke-PodeDump -ErrorRecord $_ -Format 'clixml' -Path 'C:\CustomDump' -Halt
}
```

In this example:

- The memory dump is saved in CLIXML format instead of the default.
- The dump file is saved in the specified directory (`C:\CustomDump`) instead of the default path.
- The `-Halt` switch will terminate the application after the dump is saved.

### Using the Dump Feature in Pode

To use the dump feature effectively in your Pode server, you may want to include it in specific places within your code to capture state information when critical errors occur.

Example usage in a Pode server:

```powershell
Start-PodeServer -EnableBreakpoints {
Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http

Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
try {
# Simulate an operation that could fail
$processes = Get-Process -ErrorAction Stop
Write-PodeJsonResponse -Value @{ Process = $processes[0] }
}
catch {
# Invoke a memory dump when a critical error occurs
$_ | Invoke-PodeDump -Halt
}
}
}
```

In this setup, if an error occurs in the route, `Invoke-PodeDump` is called, capturing the current state and halting the application if the `-Halt` switch is set.

### Dumping Options and Best Practices
mdaneri marked this conversation as resolved.
Show resolved Hide resolved

- **Enabling Dump in Production**: Enabling the dump in production can be valuable for diagnosing unexpected failures. However, use it selectively, especially if you are using binary or CLIXML formats, as they can produce large files.
- **Specifying Formats**: Choose a format based on your needs:
- **JSON** and **YAML** are human-readable and suitable for quick inspection.
- **CLIXML** is ideal for preserving object structures, especially when analyzing PowerShell-specific data.
- **Binary** is compact and suitable for raw state captures but requires deserialization for inspection.
- **Setting the Path**: Use a dedicated folder for dump files (e.g., `./Dump`) to keep diagnostic files organized. The default path in the configuration can be overridden at runtime if needed.

With these configurations and usage practices, the memory dump feature in Pode can provide a powerful tool for diagnostics and debugging, capturing critical state information at the time of failure.
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ There are 4 ways to restart a running Pode server:
4. [`Restart-PodeServer`](../../../Functions/Core/Restart-PodeServer): This function lets you manually restart Pode from within the server.

When the server restarts, it will re-invoke the `-ScriptBlock` supplied to the [`Start-PodeServer`](../../../Functions/Core/Start-PodeServer) function. This means the best approach to reload new modules/scripts it to dot-source/[`Use-PodeScript`](../../../Functions/Utilities/Use-PodeScript) your scripts into your server, as any changes to the main `scriptblock` will **not** take place.

Certainly! Here’s an explanation on how to achieve suspending and resuming a Pode server using `Suspend-PodeServerInternal` and `Resume-PodeServerInternal`.
mdaneri marked this conversation as resolved.
Show resolved Hide resolved
53 changes: 53 additions & 0 deletions docs/Tutorials/ServerOperation/Suspending/Overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Overview

In addition to restarting, Pode provides a way to temporarily **suspend** and **resume** the server, allowing you to pause all activities and connections without completely stopping the server. This can be especially useful for debugging, troubleshooting, or performing maintenance tasks where you don’t want to fully restart the server.

## Suspending

To suspend a running Pode server, use the `Suspend-PodeServerInternal` function. This function will pause all active server runspaces, effectively putting the server into a suspended state. Here’s how to do it:

1. **Run the Suspension Command**:
- Simply call `Suspend-PodeServerInternal` from within your Pode environment or script.

```powershell
Suspend-PodeServerInternal -Timeout 60
```

The `-Timeout` parameter specifies how long the function should wait (in seconds) for each runspace to be fully suspended. This is optional, with a default timeout of 30 seconds.

2. **Suspension Process**:
- When you run `Suspend-PodeServerInternal`, Pode will:
- Pause all runspaces associated with the server, putting them into a debug state.
- Trigger a "Suspend" event to signify that the server is paused.
- Update the server’s status to reflect that it is now suspended.

3. **Outcome**:
- After suspension, all server operations are halted, and the server will not respond to incoming requests until it is resumed.

## Resuming

Once you’ve completed any tasks or troubleshooting, you can resume the server using `Resume-PodeServerInternal`. This will restore the Pode server to its normal operational state:

1. **Run the Resume Command**:
- Call `Resume-PodeServerInternal` to bring the server back online.

```powershell
Resume-PodeServerInternal
```

2. **Resumption Process**:
- When `Resume-PodeServerInternal` is executed, Pode will:
- Restore all paused runspaces back to their active states.
- Trigger a "Resume" event, marking the server as active again.
- Clear the console, providing a refreshed view of the server status.

3. **Outcome**:
- The server resumes normal operations and can now handle incoming requests again.

## When to Use Suspend and Resume

These functions are particularly useful when:

- **Debugging**: If you encounter an issue, you can pause the server to inspect state or troubleshoot without a full restart.
- **Maintenance**: Suspend the server briefly during configuration changes, and then resume when ready.
- **Performance Management**: Temporarily pause during high load or for throttling purposes if required by your application logic.
2 changes: 1 addition & 1 deletion docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ Date: 24th May 2020
### Enhancements
* #533: Support on states for inclusion/exlcusions when saving, and scopes on states
* #538: Support to batch log items together before sending them off to be recorded
* #540: Adds a Ctrl+B shortcutto open the server in the default browser
* #540: Adds a Ctrl+B shortcut to open the server in the default browser
* #542: Add new switch to help toggling of Status Page exception message
* #548: Adds new `Get-PodeSchedule` and `Get-PodeTimer` functions
* #549: Support for calculating a schedule's next trigger datetime
Expand Down
2 changes: 1 addition & 1 deletion examples/PetStore/data/PetData.json
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,4 @@
"Users"
]
}
}
}
12 changes: 12 additions & 0 deletions examples/PetStore/server.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,22 @@
Server = @{
Timeout = 60
BodySize = 100MB
Debug = @{
Breakpoints = @{
Enable = $true
}
Dump = @{
Enabled = $true
Format = 'json'
Path = './Dump'
MaxDepth = 6
}
}
}
Web = @{
OpenApi = @{
DefaultDefinitionTag = 'v3.0.3'
}
}

}
140 changes: 140 additions & 0 deletions examples/Web-Dump.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<#
.SYNOPSIS
Initializes and starts a Pode server with OpenAPI support and error logging.

.DESCRIPTION
This script sets up a Pode server with HTTP endpoints, error logging, and OpenAPI documentation.
It also includes a sample route to simulate a critical error and dump the server's memory state.

.EXAMPLE
To run the sample: ./Web-Dump.ps1

OpenAPI Info:
Specification:
http://localhost:8081/openapi
Documentation:
http://localhost:8081/docs

.LINK
https://github.com/Badgerati/Pode/blob/develop/examples/Web-Dump.ps1

.NOTES
Author: Pode Team
License: MIT License
#>

try {
# Determine the script path and Pode module path
$ScriptPath = (Split-Path -Parent -Path $MyInvocation.MyCommand.Path)
$podePath = Split-Path -Parent -Path $ScriptPath

# Import the Pode module from the source path if it exists, otherwise from installed modules
if (Test-Path -Path "$($podePath)/src/Pode.psm1" -PathType Leaf) {
Import-Module "$($podePath)/src/Pode.psm1" -Force -ErrorAction Stop
}
else {
Import-Module -Name 'Pode' -MaximumVersion 2.99 -ErrorAction Stop
}
}
catch { throw }

# Start Pode server with specified script block
Start-PodeServer -Threads 4 -ScriptBlock {

# listen on localhost:8081
Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http
Add-PodeEndpoint -Address localhost -Port 8082 -Protocol Http
Add-PodeEndpoint -Address localhost -Port 8083 -Protocol Http
Add-PodeEndpoint -Address localhost -Port 8025 -Protocol Smtp
Add-PodeEndpoint -Address localhost -Port 8091 -Protocol Ws -Name 'WS1'
Add-PodeEndpoint -Address localhost -Port 8091 -Protocol Http -Name 'WS'
Add-PodeEndpoint -Address localhost -Port 8100 -Protocol Tcp


# set view engine to pode renderer
Set-PodeViewEngine -Type Html

# Enable error logging
New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging


# Enable OpenAPI documentation

Enable-PodeOpenApi -Path '/docs/openapi' -OpenApiVersion '3.0.3' -DisableMinimalDefinitions -NoDefaultResponses

Add-PodeOAInfo -Title 'Dump - OpenAPI 3.0.3' -Version 1.0.1
Add-PodeOAServerEndpoint -url '/api/v3' -Description 'default endpoint'

# Enable OpenAPI viewers
Enable-PodeOAViewer -Type Swagger -Path '/docs/swagger'
Enable-PodeOAViewer -Type ReDoc -Path '/docs/redoc' -DarkMode
Enable-PodeOAViewer -Type RapiDoc -Path '/docs/rapidoc' -DarkMode
Enable-PodeOAViewer -Type StopLight -Path '/docs/stoplight' -DarkMode
Enable-PodeOAViewer -Type Explorer -Path '/docs/explorer' -DarkMode
Enable-PodeOAViewer -Type RapiPdf -Path '/docs/rapipdf' -DarkMode

# Enable OpenAPI editor and bookmarks
Enable-PodeOAViewer -Editor -Path '/docs/swagger-editor'
Enable-PodeOAViewer -Bookmarks -Path '/docs'

# Setup session details
Enable-PodeSessionMiddleware -Duration 120 -Extend

# Define API routes
Add-PodeRouteGroup -Path '/api/v3' -Routes {

Add-PodeRoute -PassThru -Method Get -Path '/dump' -ScriptBlock {
$format = $WebEvent.Query['format']
try {
# Simulate a critical error
throw [System.DivideByZeroException] 'Simulated divide by zero error'
}
catch {
$_ | Invoke-PodeDump -Format $format
}
} | Set-PodeOARouteInfo -Summary 'Dump state' -Description 'Dump the memory state of the server.' -Tags 'dump' -OperationId 'dump'-PassThru |
Set-PodeOARequest -Parameters (New-PodeOAStringProperty -Name 'format' -Description 'Dump export format.' -Enum 'json', 'clixml', 'txt', 'bin', 'yaml' -Default 'json' | ConvertTo-PodeOAParameter -In Query )
}

Add-PodeVerb -Verb 'HELLO' -ScriptBlock {
Write-PodeTcpClient -Message 'HI'
'here' | Out-Default
}

# setup an smtp handler
Add-PodeHandler -Type Smtp -Name 'Main' -ScriptBlock {
Write-PodeHost '- - - - - - - - - - - - - - - - - -'
Write-PodeHost $SmtpEvent.Email.From
Write-PodeHost $SmtpEvent.Email.To
Write-PodeHost '|'
Write-PodeHost $SmtpEvent.Email.Body
Write-PodeHost '|'
# Write-PodeHost $SmtpEvent.Email.Data
# Write-PodeHost '|'
$SmtpEvent.Email.Attachments | Out-Default
if ($SmtpEvent.Email.Attachments.Length -gt 0) {
#$SmtpEvent.Email.Attachments[0].Save('C:\temp')
}
Write-PodeHost '|'
$SmtpEvent.Email | Out-Default
$SmtpEvent.Request | out-default
$SmtpEvent.Email.Headers | out-default
Write-PodeHost '- - - - - - - - - - - - - - - - - -'
}

# GET request for web page
Add-PodeRoute -Method Get -Path '/' -EndpointName 'WS' -ScriptBlock {
Write-PodeViewResponse -Path 'websockets'
}

# SIGNAL route, to return current date
Add-PodeSignalRoute -Path '/' -ScriptBlock {
$msg = $SignalEvent.Data.Message

if ($msg -ieq '[date]') {
$msg = [datetime]::Now.ToString()
}

Send-PodeSignal -Value @{ message = $msg }
}
}
Loading