diff --git a/README.md b/README.md index 6e4e64a33..a602a844d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Start-PodeServer -ScriptBlock { Add-PodeEndPoint -Address localhost -port 32005 -Protocol Http Add-PodeRoute -Method Get -Path '/ping' -ScriptBlock { - Write-PodeJsonResponse -Value @{value = 'pong' } + Write-PodeJsonResponse -Value @{ value = 'pong' } } } diff --git a/docs/Getting-Started/Console.md b/docs/Getting-Started/Console.md index 4d1ab0564..f2b54ffd2 100644 --- a/docs/Getting-Started/Console.md +++ b/docs/Getting-Started/Console.md @@ -66,7 +66,7 @@ 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). | @@ -201,12 +201,12 @@ Redefine the key for terminating the server: ## Customizing Console Colors -The console colors are fully customizable via the `Colors` section of the configuration. Each element of the console can have its color defined using PowerShell color names. Here’s what each color setting controls: +The console colors are fully customizable via the `Colors` section of the configuration. Each element of the console can have its color defined using PowerShell color names. Here's what each color setting controls: ### 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. | diff --git a/docs/Getting-Started/Debug.md b/docs/Getting-Started/Debug.md index 83303eca1..6c6e67cdc 100644 --- a/docs/Getting-Started/Debug.md +++ b/docs/Getting-Started/Debug.md @@ -199,7 +199,7 @@ These default names are automatically assigned by Pode, making it easier to iden ### Customizing Runspace Names -By default, Pode’s Tasks, Schedules, and Timers label their associated runspaces with their own names (as shown above). This simplifies the identification of runspaces when debugging or reviewing logs. +By default, Pode's Tasks, Schedules, and Timers label their associated runspaces with their own names (as shown above). This simplifies the identification of runspaces when debugging or reviewing logs. However, if a different runspace name is needed, Pode allows you to customize it. Inside the script block of `Add-PodeTask`, `Add-PodeSchedule`, or `Add-PodeTimer`, you can use the `Set-PodeCurrentRunspaceName` cmdlet to assign any custom name you prefer. @@ -211,7 +211,7 @@ This cmdlet sets a custom name for the runspace, making it easier to track durin #### Example -Here’s an example that demonstrates how to set a custom runspace name in a Pode task: +Here's an example that demonstrates how to set a custom runspace name in a Pode task: ```powershell Add-PodeTask -Name 'Test2' -ScriptBlock { @@ -237,7 +237,7 @@ This cmdlet returns the name of the current runspace, allowing for easier tracki #### Example -Here’s an example that uses `Get-PodeCurrentRunspaceName` to output the runspace name during the execution of a schedule: +Here's an example that uses `Get-PodeCurrentRunspaceName` to output the runspace name during the execution of a schedule: ```powershell Add-PodeSchedule -Name 'TestSchedule' -Cron '@hourly' -ScriptBlock { diff --git a/docs/Getting-Started/Migrating/0X-to-1X.md b/docs/Getting-Started/Migrating/0X-to-1X.md index 8760d6d78..bd929b949 100644 --- a/docs/Getting-Started/Migrating/0X-to-1X.md +++ b/docs/Getting-Started/Migrating/0X-to-1X.md @@ -29,26 +29,26 @@ For inbuilt validators, like Windows AD, you can use [`Add-PodeAuthWindowsAd`](. In both cases, the `-Type` parameter comes from using [`New-PodeAuthType`](../../../Functions/Authentication/New-PodeAuthType). -| Functions | -| -------- | -| [`Add-PodeAuth`](../../../Functions/Authentication/Add-PodeAuth) | +| Functions | +| ---------------------------------------------------------------------------------- | +| [`Add-PodeAuth`](../../../Functions/Authentication/Add-PodeAuth) | | [`Add-PodeAuthWindowsAd`](../../../Functions/Authentication/Add-PodeAuthWindowsAd) | -| [`Remove-PodeAuth`](../../../Functions/Authentication/Remove-PodeAuth) | -| [`Clear-PodeAuth`](../../../Functions/Authentication/Clear-PodeAuth) | +| [`Remove-PodeAuth`](../../../Functions/Authentication/Remove-PodeAuth) | +| [`Clear-PodeAuth`](../../../Functions/Authentication/Clear-PodeAuth) | ## Cookies Cookies use to be done via actions following the `cookie` function, such as `cookie set`. These actions are now the following functions: -| Action | Function | -| ------ | -------- | -| cookie check | [`Test-PodeCookieSigned`](../../../Functions/Cookies/Test-PodeCookieSigned) | -| cookie exists | [`Test-PodeCookie`](../../../Functions/Cookies/Test-PodeCookie) | -| cookie extend | [`Update-PodeCookieExpiry`](../../../Functions/Cookies/Update-PodeCookieExpiry) | -| cookie get | [`Get-PodeCookie`](../../../Functions/Cookies/Get-PodeCookie) | -| cookie remove | [`Remove-PodeCookie`](../../../Functions/Cookies/Remove-PodeCookie) | +| Action | Function | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| cookie check | [`Test-PodeCookieSigned`](../../../Functions/Cookies/Test-PodeCookieSigned) | +| cookie exists | [`Test-PodeCookie`](../../../Functions/Cookies/Test-PodeCookie) | +| cookie extend | [`Update-PodeCookieExpiry`](../../../Functions/Cookies/Update-PodeCookieExpiry) | +| cookie get | [`Get-PodeCookie`](../../../Functions/Cookies/Get-PodeCookie) | +| cookie remove | [`Remove-PodeCookie`](../../../Functions/Cookies/Remove-PodeCookie) | | cookie secrets | [`Get-PodeCookieSecret`](../../../Functions/Cookies/Get-PodeCookieSecret) and [`Set-PodeCookieSecret`](../../../Functions/Cookies/Set-PodeCookieSecret) | -| cookie set | [`Set-PodeCookie`](../../../Functions/Cookies/Set-PodeCookie) | +| cookie set | [`Set-PodeCookie`](../../../Functions/Cookies/Set-PodeCookie) | ## Configuration @@ -92,14 +92,14 @@ would now be: Flash messages use to be done via actions following `flash` function, such as `flash add`. These actions are now the following functions: -| Action | Function | -| ------ | -------- | -| flash add | [`Add-PodeFlashMessage`](../../../Functions/Flash/Add-PodeFlashMessage) | -| flash clear | [`Clear-PodeFlashMessages`](../../../Functions/Flash/Clear-PodeFlashMessages) | -| flash get | [`Get-PodeFlashMessage`](../../../Functions/Flash/Get-PodeFlashMessage) | -| flash keys | [`Get-PodeFlashMessageNames`](../../../Functions/Flash/Get-PodeFlashMessageNames) | -| flash remove | [`Remove-PodeFlashMessage`](../../../Functions/Flash/Remove-PodeFlashMessage) | -| flash test | [`Test-PodeFlashMessage`](../../../Functions/Flash/Test-PodeFlashMessage) | +| Action | Function | +| ------------ | --------------------------------------------------------------------------------- | +| flash add | [`Add-PodeFlashMessage`](../../../Functions/Flash/Add-PodeFlashMessage) | +| flash clear | [`Clear-PodeFlashMessages`](../../../Functions/Flash/Clear-PodeFlashMessages) | +| flash get | [`Get-PodeFlashMessage`](../../../Functions/Flash/Get-PodeFlashMessage) | +| flash keys | [`Get-PodeFlashMessageNames`](../../../Functions/Flash/Get-PodeFlashMessageNames) | +| flash remove | [`Remove-PodeFlashMessage`](../../../Functions/Flash/Remove-PodeFlashMessage) | +| flash test | [`Test-PodeFlashMessage`](../../../Functions/Flash/Test-PodeFlashMessage) | ## GUI @@ -113,9 +113,9 @@ The `gui` function use to take a Name with a hashtable of options. This function The biggest change the Handlers is that you can now create multiple handlers for each of SMTP, TCP and Service - rather than just one. With this, the new [`Add-PodeHandler`](../../../Functions/Handlers/Add-PodeHandler) requires a `-Name` to be supplied. -| Functions | -| -------- | -| [`Add-PodeHandler`](../../../Functions/Handlers/Add-PodeHandler) | +| Functions | +| ---------------------------------------------------------------------- | +| [`Add-PodeHandler`](../../../Functions/Handlers/Add-PodeHandler) | | [`Remove-PodeHandler`](../../../Functions/Handlers/Remove-PodeHandler) | | [`Clear-PodeHandlers`](../../../Functions/Handlers/Clear-PodeHandlers) | @@ -123,11 +123,11 @@ The biggest change the Handlers is that you can now create multiple handlers for Headers use to be done via actions following the `header` function, such as `header add`. These actions are now the following functions: -| Action | Function | -| ------ | -------- | -| header add | [`Add-PodeHeader`](../../../Functions/Headers/Add-PodeHeader) | -| header get | [`Get-PodeHeader`](../../../Functions/Headers/Get-PodeHeader) | -| header set | [`Set-PodeHeader`](../../../Functions/Headers/Set-PodeHeader) | +| Action | Function | +| ------------- | --------------------------------------------------------------- | +| header add | [`Add-PodeHeader`](../../../Functions/Headers/Add-PodeHeader) | +| header get | [`Get-PodeHeader`](../../../Functions/Headers/Get-PodeHeader) | +| header set | [`Set-PodeHeader`](../../../Functions/Headers/Set-PodeHeader) | | header exists | [`Test-PodeHeader`](../../../Functions/Headers/Test-PodeHeader) | ## Logging @@ -140,30 +140,30 @@ The old `logging` used to only support logging Requests, whereas now it can log ### Methods -Logging methods define how a log item should be logged. The inbuilt File and Terminal logging methods are now reusable, with the ability to now more easily create custom logging methods using [`New-PodeLoggingMethod`](../../../Functions/Logging/New-PodeLoggingMethod). The output from this function can be used when enabling or adding logging types, such as with [`Add-PodeLogger`](../../../Functions/Logging/Add-PodeLogger). +Log Methods define how a log item should be logged. The inbuilt File and Terminal Log Methods are now reusable, with the ability to now more easily create custom Log Methods using [`New-PodeLoggingMethod`](../../../Functions/Logging/New-PodeLoggingMethod). The output from this function can be used when enabling or adding Log Types, such as with [`Add-PodeLogger`](../../../Functions/Logging/Add-PodeLogger). ### Types -Request and Error logging are inbuilt logging types that can be enabled using [`Enable-PodeRequestLogging`](../../../Functions/Logging/Enable-PodeRequestLogging) and [`Enable-PodeErrorLogging`](../../../Functions/Logging/Enable-PodeErrorLogging). To create a custom logger you can use the [`Add-PodeLogger`](../../../Functions/Logging/Add-PodeLogger) function. In each case, the `-Method` comes from using [`New-PodeLoggingMethod`](../../../Functions/Logging/New-PodeLoggingMethod). +Request and Error logging are inbuilt Log Types that can be enabled using [`Enable-PodeRequestLogging`](../../../Functions/Logging/Enable-PodeRequestLogging) and [`Enable-PodeErrorLogging`](../../../Functions/Logging/Enable-PodeErrorLogging). To create a custom logger you can use the [`Add-PodeLogger`](../../../Functions/Logging/Add-PodeLogger) function. In each case, the `-Method` comes from using [`New-PodeLoggingMethod`](../../../Functions/Logging/New-PodeLoggingMethod). -| Functions | -| -------- | -| [`Enable-PodeRequestLogging`](../../../Functions/Logging/Enable-PodeRequestLogging) | -| [`Enable-PodeErrorLogging`](../../../Functions/Logging/Enable-PodeErrorLogging) | -| [`Add-PodeLogger`](../../../Functions/Logging/Add-PodeLogger) | +| Functions | +| ------------------------------------------------------------------------------------- | +| [`Enable-PodeRequestLogging`](../../../Functions/Logging/Enable-PodeRequestLogging) | +| [`Enable-PodeErrorLogging`](../../../Functions/Logging/Enable-PodeErrorLogging) | +| [`Add-PodeLogger`](../../../Functions/Logging/Add-PodeLogger) | | [`Disable-PodeRequestLogging`](../../../Functions/Logging/Disable-PodeRequestLogging) | -| [`Disable-PodeErrorLogging`](../../../Functions/Logging/Disable-PodeErrorLogging) | -| [`Remove-PodeLogger`](../../../Functions/Logging/Remove-PodeLogger) | -| [`Clear-PodeLoggers`](../../../Functions/Logging/Clear-PodeLoggers) | +| [`Disable-PodeErrorLogging`](../../../Functions/Logging/Disable-PodeErrorLogging) | +| [`Remove-PodeLogger`](../../../Functions/Logging/Remove-PodeLogger) | +| [`Clear-PodeLoggers`](../../../Functions/Logging/Clear-PodeLoggers) | ### Writing Logs You can now write items from anywhere in your server to a custom logger, including the inbuilt Error log. -| Functions | -| -------- | +| Functions | +| --------------------------------------------------------------------- | | [`Write-PodeErrorLog`](../../../Functions/Logging/Write-PodeErrorLog) | -| [`Write-PodeLog`](../../../Functions/Logging/Write-PodeLog) | +| [`Write-PodeLog`](../../../Functions/Logging/Write-PodeLog) | ## Middleware @@ -175,12 +175,12 @@ Middleware has changed a fair bit, however, generally you'll just be using the [ There is a new [`New-PodeMiddleware`](../../../Functions/Middleware/New-PodeMiddleware) function which wil return a valid middleware object to re-use - such as piping into [`Add-PodeMiddleware`](../../../Functions/Middleware/Add-PodeMiddleware), or using as `-Middleware` for Routes. -| Functions | -| -------- | -| [`Add-PodeMiddleware`](../../../Functions/Middleware/Add-PodeMiddleware) | -| [`New-PodeMiddleware`](../../../Functions/Middleware/New-PodeMiddleware) | +| Functions | +| ------------------------------------------------------------------------------ | +| [`Add-PodeMiddleware`](../../../Functions/Middleware/Add-PodeMiddleware) | +| [`New-PodeMiddleware`](../../../Functions/Middleware/New-PodeMiddleware) | | [`Remove-PodeMiddleware`](../../../Functions/Middleware/Remove-PodeMiddleware) | -| [`Clear-PodeMiddlewares`](../../../Functions//Clear-PodeMiddlewares) | +| [`Clear-PodeMiddlewares`](../../../Functions//Clear-PodeMiddlewares) | ### Sessions @@ -194,12 +194,12 @@ The `session` function has now been replaced by the new [`Enable-PodeSessionMidd The `csrf` function used to take actions that defined what it did, such as `csrf token`. Now, each of these actions has been split up into their own functions: -| Action | Function | -| ------ | -------- | +| Action | Function | +| --------------- | -------------------------------------------------------------------------------------- | | csrf middleware | [`Enable-PodeCsrfMiddleware`](../../../Functions/Middleware/Enable-PodeCsrfMiddleware) | -| csrf setup | [`Initialize-PodeCsrf`](../../../Functions/Middleware/Initialize-PodeCsrf) | -| csrf check | [`Get-PodeCsrfMiddleware`](../../../Functions/Middleware/Get-PodeCsrfMiddleware) | -| csrf token | [`New-PodeCsrfToken`](../../../Functions/Middleware/New-PodeCsrfToken) | +| csrf setup | [`Initialize-PodeCsrf`](../../../Functions/Middleware/Initialize-PodeCsrf) | +| csrf check | [`Get-PodeCsrfMiddleware`](../../../Functions/Middleware/Get-PodeCsrfMiddleware) | +| csrf token | [`New-PodeCsrfToken`](../../../Functions/Middleware/New-PodeCsrfToken) | !!! note Similar to the old setup, the [`Initialize-PodeCsrf`](../../../Functions/Middleware/Initialize-PodeCsrf) function must be called before you can use [`Get-PodeCsrfMiddleware`](../../../Functions/Middleware/Get-PodeCsrfMiddleware) or [`New-PodeCsrfToken`](../../../Functions/Middleware/New-PodeCsrfToken). The [`Enable-PodeCsrfMiddleware`](../../../Functions/Middleware/Enable-PodeCsrfMiddleware) does automatically call [`Initialize-PodeCsrf`](../../../Functions/Middleware/Initialize-PodeCsrf) as well as configure CSRF globally. @@ -210,31 +210,31 @@ The `csrf` function used to take actions that defined what it did, such as `csrf The functions to import and load Modules, Scripts and SnapIns have all changed to the following: -| Old | Function | -| --- | -------- | +| Old | Function | +| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | | import | [`Import-PodeModule`](../../../Functions/Utilities/Import-PodeModule) and [`Import-PodeSnapIn`](../../../Functions/Utilities/Import-PodeSnapIn) | -| load | [`Use-PodeScript`](../../../Functions/Utilities/Use-PodeScript) | +| load | [`Use-PodeScript`](../../../Functions/Utilities/Use-PodeScript) | ## Response Helpers The old response helpers have all been updated: -| Old | Function | -| --- | -------- | -| engine | [`Set-PodeViewEngine`](../../../Functions/Responses/Set-PodeViewEngine) | -| view | [`Write-PodeViewResponse`](../../../Functions/Responses/Write-PodeViewResponse) | -| json | [`Write-PodeJsonResponse`](../../../Functions/Responses/Write-PodeJsonResponse) | -| text | [`Write-PodeTextResponse`](../../../Functions/Responses/Write-PodeTextResponse) | -| xml | [`Write-PodeXmlResponse`](../../../Functions/Responses/Write-PodeXmlResponse) | -| csv | [`Write-PodeCsvResponse`](../../../Functions/Responses/Write-PodeCsvResponse) | -| html | [`Write-PodeHtmlResponse`](../../../Functions/Responses/Write-PodeHtmlResponse) | -| file | [`Write-PodeFileResponse`](../../../Functions/Responses/Write-PodeFileResponse) | -| attach | [`Set-PodeResponseAttachment`](../../../Functions/Responses/Set-PodeResponseAttachment) | -| save | [`Save-PodeRequestFile`](../../../Functions/Responses/Save-PodeRequestFile) | -| status | [`Set-PodeResponseStatus`](../../../Functions/Responses/Set-PodeResponseStatus) | -| include | [`Use-PodePartialView`](../../../Functions/Responses/Use-PodePartialView) | -| redirect | [`Move-PodeResponseUrl`](../../../Functions/Responses/Move-PodeResponseUrl) | -| tcp | [`Write-PodeTcpClient`](../../../Functions/Responses/Write-PodeTcpClient) and [`Read-PodeTcpClient`](../../../Functions/Responses/Read-PodeTcpClient) | +| Old | Function | +| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| engine | [`Set-PodeViewEngine`](../../../Functions/Responses/Set-PodeViewEngine) | +| view | [`Write-PodeViewResponse`](../../../Functions/Responses/Write-PodeViewResponse) | +| json | [`Write-PodeJsonResponse`](../../../Functions/Responses/Write-PodeJsonResponse) | +| text | [`Write-PodeTextResponse`](../../../Functions/Responses/Write-PodeTextResponse) | +| xml | [`Write-PodeXmlResponse`](../../../Functions/Responses/Write-PodeXmlResponse) | +| csv | [`Write-PodeCsvResponse`](../../../Functions/Responses/Write-PodeCsvResponse) | +| html | [`Write-PodeHtmlResponse`](../../../Functions/Responses/Write-PodeHtmlResponse) | +| file | [`Write-PodeFileResponse`](../../../Functions/Responses/Write-PodeFileResponse) | +| attach | [`Set-PodeResponseAttachment`](../../../Functions/Responses/Set-PodeResponseAttachment) | +| save | [`Save-PodeRequestFile`](../../../Functions/Responses/Save-PodeRequestFile) | +| status | [`Set-PodeResponseStatus`](../../../Functions/Responses/Set-PodeResponseStatus) | +| include | [`Use-PodePartialView`](../../../Functions/Responses/Use-PodePartialView) | +| redirect | [`Move-PodeResponseUrl`](../../../Functions/Responses/Move-PodeResponseUrl) | +| tcp | [`Write-PodeTcpClient`](../../../Functions/Responses/Write-PodeTcpClient) and [`Read-PodeTcpClient`](../../../Functions/Responses/Read-PodeTcpClient) | ## Routes @@ -244,9 +244,9 @@ The old response helpers have all been updated: Normal routes defined via `route` can now be done using [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute). The parameters are practically the same, such as `-Method`, `-Path` and `-ScriptBlock`. -| Functions | -| -------- | -| [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute) | +| Functions | +| ---------------------------------------------------------------- | +| [`Add-PodeRoute`](../../../Functions/Routes/Add-PodeRoute) | | [`Remove-PodeRoute`](../../../Functions/Routes/Remove-PodeRoute) | | [`Clear-PodeRoutes`](../../../Functions/Routes/Clear-PodeRoutes) | @@ -254,9 +254,9 @@ Normal routes defined via `route` can now be done using [`Add-PodeRoute`](../../ Static routes that used to be setup using `route static` are now setup using the new [`Add-PodeStaticRoute`](../../../Functions/Routes/Add-PodeStaticRoute). -| Functions | -| -------- | -| [`Add-PodeStaticRoute`](../../../Functions/Routes/Add-PodeStaticRoute) | +| Functions | +| ---------------------------------------------------------------------------- | +| [`Add-PodeStaticRoute`](../../../Functions/Routes/Add-PodeStaticRoute) | | [`Remove-PodeStaticRoute`](../../../Functions/Routes/Remove-PodeStaticRoute) | | [`Clear-PodeStaticRoutes`](../../../Functions/Routes/Clear-PodeStaticRoutes) | @@ -266,9 +266,9 @@ Static routes that used to be setup using `route static` are now setup using the Schedules haven't changed too much, though there are now some new functions to remove and clear schedules. The main one is that `schedule` has been changed to [`Add-PodeSchedule`](../../../Functions/Schedules/Add-PodeSchedule). -| Functions | -| -------- | -| [`Add-PodeSchedule`](../../../Functions/Schedules/Add-PodeSchedule) | +| Functions | +| ------------------------------------------------------------------------- | +| [`Add-PodeSchedule`](../../../Functions/Schedules/Add-PodeSchedule) | | [`Remove-PodeSchedule`](../../../Functions/Schedules/Remove-PodeSchedule) | | [`Clear-PodeSchedules`](../../../Functions/Schedules/Clear-PodeSchedules) | @@ -286,14 +286,14 @@ The `server` function itself has been renamed to [`Start-PodeServer`](../../../F The shared state use to be done via actions following the `state` function, such as `state set`. These actions are now the following functions: -| Action | Function | -| ------ | -------- | -| state set | [`Set-PodeState`](../../../Functions/State/Set-PodeState) | -| state get | [`Get-PodeState`](../../../Functions/State/Get-PodeState) | -| state remove | [`Remove-PodeState`](../../../Functions/State/Remove-PodeState) | -| state save | [`Save-PodeState`](../../../Functions/State/Save-PodeState) | +| Action | Function | +| ------------- | ----------------------------------------------------------------- | +| state set | [`Set-PodeState`](../../../Functions/State/Set-PodeState) | +| state get | [`Get-PodeState`](../../../Functions/State/Get-PodeState) | +| state remove | [`Remove-PodeState`](../../../Functions/State/Remove-PodeState) | +| state save | [`Save-PodeState`](../../../Functions/State/Save-PodeState) | | state restore | [`Restore-PodeState`](../../../Functions/State/Restore-PodeState) | -| state test | [`Test-PodeState`](../../../Functions/State/Test-PodeState) | +| state test | [`Test-PodeState`](../../../Functions/State/Test-PodeState) | ## Timers @@ -301,8 +301,8 @@ The shared state use to be done via actions following the `state` function, such Timers haven't changed too much, though there are now some new functions to remove and clear timers. The main one is that `timer` has been changed to [`Add-PodeTimer`](../../../Functions/Timers/Add-PodeTimer). -| Functions | -| -------- | -| [`Add-PodeTimer`](../../../Functions/Timers/Add-PodeTimer) | +| Functions | +| ---------------------------------------------------------------- | +| [`Add-PodeTimer`](../../../Functions/Timers/Add-PodeTimer) | | [`Remove-PodeTimer`](../../../Functions/Timers/Remove-PodeTimer) | | [`Clear-PodeTimers`](../../../Functions/Timers/Clear-PodeTimers) | diff --git a/docs/Getting-Started/pwshsupportpolicy.md b/docs/Getting-Started/pwshsupportpolicy.md index a5d6a8fa6..22727cdb4 100644 --- a/docs/Getting-Started/pwshsupportpolicy.md +++ b/docs/Getting-Started/pwshsupportpolicy.md @@ -23,7 +23,7 @@ Pode is tested against the PowerShell versions it supports at the time of each r A warning mechanism within Pode detects the PowerShell version in use at runtime. If a version no longer supported by the current release of Pode is detected—due to reaching EOL—a warning will be issued, advising on the potential risks and recommending an update to a supported version. ## Version Updates and Communication -Updates to the PowerShell version support policy will be documented in Pode’s release notes and official documentation. Users are encouraged to review these sources to stay informed about which PowerShell versions are supported by their version of Pode. +Updates to the PowerShell version support policy will be documented in Pode's release notes and official documentation. Users are encouraged to review these sources to stay informed about which PowerShell versions are supported by their version of Pode. ## Feedback and Contributions Feedback on Pode's PowerShell version support policy is invaluable. We welcome suggestions and contributions from our community to help refine and improve our approach. Please share your thoughts through our GitHub repository or community forums. diff --git a/docs/Tutorials/Authentication/Inbuilt/AzureAD.md b/docs/Tutorials/Authentication/Inbuilt/AzureAD.md index 0c43fd9d4..f2dd285e6 100644 --- a/docs/Tutorials/Authentication/Inbuilt/AzureAD.md +++ b/docs/Tutorials/Authentication/Inbuilt/AzureAD.md @@ -61,7 +61,7 @@ When a user accesses your site unauthenticated, they will be to Azure to login, ### Password -To setup Azure AD authentcation, but using your own Form login, then you can use the `-InnerScheme` parameter on `New-PodeAuthAzureADScheme`: +To setup Azure AD authentication, but using your own Form login, then you can use the `-InnerScheme` parameter on `New-PodeAuthAzureADScheme`: ```powershell Start-PodeServer { diff --git a/docs/Tutorials/Basics.md b/docs/Tutorials/Basics.md index 137aa8b29..5fa81c0f4 100644 --- a/docs/Tutorials/Basics.md +++ b/docs/Tutorials/Basics.md @@ -1,15 +1,17 @@ # Basics - !!! Warning -You can initiate only one server per PowerShell instance. To run multiple servers, start additional PowerShell, or pwsh, sessions. Each session can run its own server. This is fundamental to how Pode operates, so consider it when designing your scripts and infrastructure. +!!! important + You can only initiate one Pode server per PowerShell instance. -While it’s not mandatory, we strongly recommend importing the Pode module with a specified maximum version. This practice helps to prevent potential issues arising from breaking changes introduced in new major versions: +## Importing + +While it's not mandatory, we strongly recommend importing the Pode module with a specified maximum version. This practice helps to prevent potential issues arising from breaking changes introduced in new major versions: ```powershell Import-Module -Name Pode -MaximumVersion 2.99.99 ``` -To further enhance the robustness of your code, consider wrapping the import statement within a try/catch block. This way, if the module fails to load, your script won’t proceed, preventing possible errors or unexpected behavior: +To further enhance the robustness of your code, consider wrapping the import statement within a try/catch block. This way, if the module fails to load, your script won't proceed, preventing possible errors or unexpected behaviour: ```powershell try { @@ -20,26 +22,32 @@ try { } ``` -The script for your server should be set in the [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) function, via the `-ScriptBlock` parameter. The following example will listen over HTTP on port 8080, and expose a simple HTML page of running processes at `http://localhost:8080/processes`: +## Running + +To start a new Pode server use [`Start-PodeServer`](../../Functions/Core/Start-PodeServer), and supply your server's logic via the `-ScriptBlock` parameter. + +The following example will listen over HTTP on port 8080, and expose a simple Route that returns the host's name at `http://localhost:8080/`: ```powershell Start-PodeServer { # attach to port 8080 for http - Add-PodeEndpoint -Address * -Port 8080 -Protocol Http + Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http - # a simple page for displaying services - Add-PodePage -Name 'processes' -ScriptBlock { Get-Process } + # a simple route that returns the host's name + Add-PodeRoute -Method Get -Path '/' -ScriptBlock { + @{ Name = $env:COMPUTERNAME } | Write-PodeJsonResponse + } } ``` To start the server you can either: * Directly run the `./server.ps1` script, or -* If you've created a `package.json` file, ensure the `./server.ps1` script is set as your `main` or `scripts/start`, then just run `pode start` (more [here](../../Getting-Started/CLI)) +* If you created a `package.json` file, ensure the `./server.ps1` script is set as your `main` or `scripts/start`, then just run `pode start` (more [here](../../Getting-Started/CLI)) ## Terminating -Once your Pode server has started, you can terminate it at any time using `Ctrl+C`. If you want to disable your server from being terminated then use the `-DisableTermination` switch on the [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) function. +Once your Pode server has started, you can terminate it at any time using `Ctrl+C`. If you want to disable your server from being terminated then use the `-DisableTermination` switch on [`Start-PodeServer`](../../Functions/Core/Start-PodeServer). ## Restarting @@ -47,7 +55,7 @@ You can restart your Pode server by using `Ctrl+R`, or on Unix you can also use ## Script from File -You can also define your server's scriptblock in a separate file, and use it via the `-FilePath` parameter on the [`Start-PodeServer`](../../Functions/Core/Start-PodeServer) function. +You can define your server's scriptblock in a separate file, and load it via the `-FilePath` parameter on [`Start-PodeServer`](../../Functions/Core/Start-PodeServer). Using this approach there are 2 ways to start you server: @@ -83,13 +91,17 @@ PS> Start-PodeServer -FilePath './File.ps1' !!! tip Normally when you restart your Pode server any changes to the main scriptblock don't reflect. However, if you reference a file instead, then restarting the server will reload the scriptblock from that file - so any changes will reflect. -## Internationalization +## App Name + +Primarily used by logging, the default application name of your server will be "Pode". You can customise this name via the `-AppName` parameter on [`Start-PodeServer`](../../Functions/Core/Start-PodeServer). + +## Localisation -Pode has built-in support for internationalization (i18n). By default, Pode uses the `$PsUICulture` variable to determine the User Interface Culture (UICulture). +Pode has built-in support for localisation. By default, Pode uses the `$PsUICulture` variable to determine the User Interface Culture (UICulture). -You can enforce a specific localization when importing the Pode module by using the UICulture argument. This argument accepts a culture code, which specifies the language and regional settings to use. +You can enforce a specific localisation when importing the Pode module by using the UICulture argument. This argument accepts a culture code, which specifies the language and regional settings to use. -Here’s an example of how to enforce Korean localization: +Here's an example of how to enforce Korean localisation: ```powershell Import-Module -Name Pode -ArgumentList 'ko-KR' diff --git a/docs/Tutorials/CORS.md b/docs/Tutorials/CORS.md index b4496cb55..f549a7f2a 100644 --- a/docs/Tutorials/CORS.md +++ b/docs/Tutorials/CORS.md @@ -25,7 +25,7 @@ Pode simplifies handling CORS by providing the `Set-PodeSecurityAccessControl` f ### Setting CORS Headers instead -The `Set-PodeSecurityAccessControl` function allows you to set these headers easily. Here’s how you can address common CORS challenges using this function: +The `Set-PodeSecurityAccessControl` function allows you to set these headers easily. Here's how you can address common CORS challenges using this function: 1. **Allowing All Origins** ```powershell diff --git a/docs/Tutorials/Logging/Formatting.md b/docs/Tutorials/Logging/Formatting.md new file mode 100644 index 000000000..117612e03 --- /dev/null +++ b/docs/Tutorials/Logging/Formatting.md @@ -0,0 +1,173 @@ +# Formatting + +You can customise the formatting and serialisation of your Log Types, by supplying the parameters as described below. + +Within the scope of a Log Type, ie the "Transform Phase", the steps are as follows: + +```mermaid +graph LR + Selection("`Selection`") + + Serialisation("`Serialisation`") + + Formatting("`Formatting`") + + Selection --> Serialisation + Serialisation --> Formatting +``` + +| Step | Description | Related Parameters | +| ------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------- | +| Selection | Required data from the original log item is selected, and returned (ie, as a hashtable) | `-ScriptBlock` | +| Serialisation | The resultant data, if required, is serialised (ie, as JSON, custom, etc.) | `-SerialiseFormat` and `-SerialiseScriptBlock` | +| Formatting | The resultant, or serialised, data is converted into some log format (ie, syslog) | `-LogFormat` and `-LogScriptBlock` | + +## Serialisation + +Log Types support the following serialisation methods, which can be supplied using the `-SerialiseFormat` parameter: + +* None +* Custom +* Json +* Xml +* Yaml + +The default for inbuilt Log Types like Error/Request is None, while the default for custom Log Types is None. + +### None + +When None is specified, no serialisation method is applied and the data returned by the Log Type's `-ScriptBlock` is passed straight to formatting. + +### Custom + +When Custom is specified then a `-SerialiseScriptBlock` is required to be supplied as well. + +!!! note + For inbuilt Log Types, like Error/Request, "Custom" is the default. Here a `-SerialiseScriptBlock` is optional as inbuilt logic will be used if not supplied. + +When serialisation occurs, this scriptblock will be invoked. Supplied to this scriptblock are the following parameters: + +1. The resultant data returned from the Log Type's main `-ScriptBlock` +2. The [Log Event](../Objects#log-event) object +3. Items supplied to `-ArgumentList`, splatted as individual parameters + +For example, the inbuilt Request Log Type's serialise scriptblock looks as follows; it will serialise the data into Combined Log Format: + +```powershell +$scriptblock = { + param($data) + $reqLine = "$($data.Method) $($data.Resource) $($data.Protocol)" + return "$($data.Host) $($data.Identifier) $($data.User) [$($data.Date)] `"$($reqLine)`" $($data.StatusCode) $($data.Size) `"$($data.Referrer)`" `"$($data.UserAgent)`"" +} +``` + +ie: +```plain +10.10.0.3 - - [14/Jun/2018:20:23:52 +01:00] "GET /api/users HTTP/1.1" 200 9001 "-" "" +``` + +### Others + +The other standard serialisation options: JSON; XML; and YAML, will all serialise the resultant data returned from the Log Type's main `-ScriptBlock`. + +For example, if you supply `-SerialiseFormat Json` to [`Enable-PodeLogRequestType`](../../../../Functions/Logging/Enable-PodeLogRequestType), then instead of Combined Log Format (the default) you'll get: + +```powershell +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -SerialiseFormat Json +``` + +```json +{ + "Host": "10.10.0.3", + "Identifier": null, + "User": null, + "Date": "14/Jun/2018:20:23:52 +01:00", + "Method": "GET", + "Resource": "/api/users", + "Protocol": "HTTP/1.1", + "StatusCode": 200, + "Size": 9001, + "Referrer": "", + "UserAgent": "" +} +``` + +#### XML + +When serialising custom Log Types into XML the default root element is ``, this can be customised via `-XmlRootName`. + +### Global + +You can configure a global serialisation format to use via [`Set-PodeLogDefaultSerialiseFormat`]. If you **don't** supply `-SerialiseFormat` then the global default will be used instead. + +By default there is no global default; not supplying `-SerialiseFormat` will default to the Log Type's local default value - format inbuilt type this is Custom, and for custom types this is None. + +## Log Format + +Log Types support the following formats, which can be supplied using the `-LogFormat` parameter: + +* None (default) +* Custom +* Syslog + +This occurs after serialisation, so the "message" will typically be the resultant data from the Log Type, and post any serialisation. For example, allowing you to have a syslog formatted message, where the message part is JSON. + +### None + +When None is specified, then no formatting is performed on the log item. Whatever resultant data was returned from the Log Type's `-ScriptBlock`, and optionally supplied to any serialisation, will remain as is. + +### Custom + +When Custom is specified then a `-LogScriptBlock` is required to be supplied as well. + +When log formatting occurs, after serialisation, this scriptblock will be invoked. Supplied to this scriptblock are the following parameters: + +1. The resultant data returned from the Log Type's main `-ScriptBlock`, and after any serialisation +2. The [Log Event](../Objects#log-event) object +3. Items supplied to `-ArgumentList`, splatted as individual parameters + +For example, a simple pipe-delimited format of `||: + +```powershell +$scriptblock = { + param($data, $logEvent) + return "$($logEvent.Level)|$($logEvent.Timestamp)|$($data) +} +``` + +### Syslog + +When Syslog is specified, then the resultant data - after serialisation - is set as the "message" component of a syslog formatted string ([more details](https://www.manageengine.com/products/eventlog/logging-guide/syslog/syslog-basics-logging.html)). Supported are the following formats: + +* RFC5424 (default) +* RFC3164 + +By default, this will be RFC5424 format with a facility value of 16 (local0). These values can be customised, and tags included, by creating a `SyslogInfo` object via [`New-PodeLogSyslogInfo`](../../../../Functions/Logging/New-PodeLogSyslogInfo); the result of which can then be supplied to a Log Type's `-SyslogInfo` parameter. + +For example if you specify `-LogFormat Syslog` on [`Enable-PodeLogRequestType`](../../../../Functions/Logging/Enable-PodeLogRequestType), with no `-SyslogInfo` object, then the result syslog message sent to a Log Method would be RFC5424 format: + +```powershell +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -LogFormat Syslog +``` + +```plain +<134>1 2018-06-14T20:23:52.000+01:00 APP-VM-1 Pode 6132 - - 10.10.0.3 - - [14/Jun/2018:20:23:52 +01:00] "GET /api/users HTTP/1.1" 200 9001 "-" "" +``` + +!!! note + The application name used, if not specified in a SyslogInfo object, will be the [Server's App Name](../../../../Basic#app-name). + +To change the format to RFC3164: + +```powershell +$syslogInfo = New-PodeLogSyslogInfo -Format RFC3164 +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -LogFormat Syslog -SyslogInfo $syslogInfo +``` + +### Global + +You can configure a global log format to use via [`Set-PodeLogDefaultFormat`]. If you **don't** supply `-LogFormat` then the global default will be used instead. + +By default there is no global default; not supplying `-LogFormat` will default to the Log Type's local default value - usually None. + +The same can also be done for syslog formatting using [`Set-PodeLogDefaultSyslogFormat`]. Similar to above this will apply when either no `-SyslogInfo` is supplied, or no `-Format` is supplied to [`New-PodeLogSyslogInfo`]. diff --git a/docs/Tutorials/Logging/Methods/API.md b/docs/Tutorials/Logging/Methods/API.md new file mode 100644 index 000000000..8cf8be7ee --- /dev/null +++ b/docs/Tutorials/Logging/Methods/API.md @@ -0,0 +1,91 @@ +# API + +The API Log Method allows you to send logs to custom API servers - self-hosted or cloud. + +## Creation + +To create a new API Log Method use [`New-PodeLogApiMethod`](../../../../Functions/Logging/New-PodeLogApiMethod), and supply the `-Url` of the API to call, along with a `-BodyScriptBlock` for generating the appropriate string payload to send to the API. The parameters passed to the scriptblock will be a list of [Log Items](../../Objects#log-item), and any arguments supplied via `-BodyArguments`. + +If you wish to ignore any certificate checks, you may supply `-SkipCertificateCheck`. + +By default Pode will send the payload uncompressed, if you require for it to be GZip compressed supply `-Compress` - this will automatically GZip compress the payloads returned from `-BodyScriptBlock`, and also add the `Content-Encoding` HTTP header. + +Typically most APIs require some form of authentication, usually via the `Authorization` HTTP header. You can add this header via the `-Headers` parameter, as a hashtable key. + +```powershell +$headers = @{ + Authorization = "Bearer $($your_token)" +} + +New-PodeLogApiMethod -Url '' -Headers $headers -Compress -BodyScriptBlock { + param($logItems) + + $events = @(foreach $logItem in $logItems) { + @{ + message = $logItem.Data + level = $logItem.Event.Level + timestamp = $item.Event.Timestamp.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } + } + + return $events | ConvertTo-Json -Compress -Depth 10 +} +``` + +### Content Type + +The default content type sent by Pode is `application/json`, if you wish to supply a different content type you can do so via the `-ContentType` parameter. + +### Method + +The default HTTP method used by Pode for calling the API is `POST`, if you wish to supply a different method you can do so via the `-Method` parameter. + +The `-Method` parameter accepts `POST`, `GET`, `PUT`, and `PATCH`. + +### Dynamic Headers + +The simplest way to supply headers to be added onto API requests is via `-Headers`: + +```powershell +New-PodeLogApiMethod ... -Headers @{ + Authorization = "Bearer $($your_token)" +} +``` + +However, at times you might need to generate headers dynamically - such as headers that require the current datetime, or headers that have to be signed using the generated body. To do so you can supply a scriptblock to `-HeadersScriptBlock`, which will be passed the generated body and any arguments from `-HeadersArguments`. This scriptblock should return a valid hashtable - or `$null`. + +```powershell +New-PodeLogApiMethod ... -HeadersScriptBlock { + param($body) + + return @{ + Timestamp = [datetime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ') + } +} +``` + +## Examples + +### Send Request Logs + +The following example will send Request logs to an API endpoint, using a bearer authentication token. The body will be sent as JSON, and will be GZip compressed. + +```powershell +$headers = @{ + Authorization = "Bearer $($your_token)" +} + +New-PodeLogApiMethod -Url 'http://api.example.com/logs' -Headers $headers -Compress -BodyScriptBlock { + param($logItems) + + $events = @(foreach $logItem in $logItems) { + @{ + message = $logItem.Data + level = $logItem.Event.Level + timestamp = $item.Event.Timestamp.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } + } + + return $events | ConvertTo-Json -Compress -Depth 10 +} +``` diff --git a/docs/Tutorials/Logging/Methods/AWS.md b/docs/Tutorials/Logging/Methods/AWS.md new file mode 100644 index 000000000..95c95cca4 --- /dev/null +++ b/docs/Tutorials/Logging/Methods/AWS.md @@ -0,0 +1,51 @@ +# AWS + +The AWS Log Method allows you to send logs to AWS CloudWatch. This Log Method uses the general [`New-PodeLogApiMethod`](../../../../Functions/Logging/New-PodeLogApiMethod) under-the-hood. + +## Creation + +To create a new AWS Log Method use [`New-PodeLogAwsMethod`](../../../../Functions/Logging/New-PodeLogAwsMethod), and supply the `-LogGroupName`, `-LogStreamName`, and `-Region` parameters. You'll also need to supply a Bearer `-Token` for authentication. + +If you wish to ignore any certificate checks, you may supply `-SkipCertificateCheck`. + +### Source Type + +The default source type sent by Pode is empty, allowing AWS to automatically select the best fit. However, if you wish to supply an explicit source type you can do so via the `-SourceType` parameter. + +### Source + +The default source for your logs sent by Pode is the [Server's App Name](../../../../Basic#app-name). If you wish to supply an explicit source different from the server's, then you can do so via the `-Source` parameter. + +### Index + +The default index supplied by Pode is empty, which usually equates to the main index in AWS. However, if you wish to supply an explicit index you can do so via the `-Index` parameter. + +## Examples + +### Send Request Logs + +The following example will send Request logs to AWS CloudWatch. + +```powershell +$method = New-PodeLogAwsMethod ` + -LogGroupName 'my-log-group' ` + -LogStreamName 'my-log-stream' ` + -Region 'us-east-1' ` + -Token '' + +$method | Enable-PodeLogRequestType +``` + +### Send Error Logs + +The following example will send JSON serialised Error logs to Aws CloudWatch. + +```powershell +$method = New-PodeLogAwsMethod ` + -LogGroupName 'my-log-group' ` + -LogStreamName 'my-log-stream' ` + -Region 'us-east-1' ` + -Token '' + +$method | Enable-PodeLogErrorType -SerialiseFormat Json +``` diff --git a/docs/Tutorials/Logging/Methods/Azure.md b/docs/Tutorials/Logging/Methods/Azure.md new file mode 100644 index 000000000..74569dd24 --- /dev/null +++ b/docs/Tutorials/Logging/Methods/Azure.md @@ -0,0 +1,74 @@ +# Azure + +The Azure Log Method allows you to send logs to Azure Log Analytics. This Log Method uses the general [`New-PodeLogApiMethod`](../../../../Functions/Logging/New-PodeLogApiMethod) under-the-hood. + +This Log Method supports 2 approaches in which to send logs to Azure: + +1. The legacy Workspace approach, requiring a WorkspaceId and SharedKey. +2. The current Data Collection approach, requiring Endpoint/ImmutableId and App Registration. + +## Creation + +To create a new Azure Log Method use [`New-PodeLogAzureMethod`](../../../../Functions/Logging/New-PodeLogAzureMethod), and then supply the appropriate parameters depending on you approach your Log Analytics supports. + +If you wish to ignore any certificate checks, you may supply `-SkipCertificateCheck`. + +**Workspace** + +The legacy approach requires a `-WorkspaceId`, `-SharedKey`, and `-LogType` from you Azure Analytics. + +**Data Collection** + +The current approach, whereby you setup Data Collection Endpoints and Rules, requires the DCE `-Endpoint`, and the DCR `-ImmutableId` and `-StreamName`. + +You'll also be required to supply the `-ClientId`, `-ClientSecret`, and `-TenantId` of an App Registration with the `Monitoring Metrics Publisher` role on your DCR. This App Registration will be used to automatically generate OAuth2 access tokens, and renew them, so that Pode can send logs to your Azure Log Analytics. + +Further info can be [found here](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/tutorial-logs-ingestion-portal). + +When sending logs via Data Collection, you can expect the top-level format of those logs to be JSON: + +```json +[ + { + "Data": "", + "Level": "Error", + "TimeGenerated": "2026-07-05T15:56:00Z", + "Source": "Pode" + } +] +``` + +### Source + +The default source for your logs sent by Pode is the [Server's App Name](../../../../Basic#app-name). If you wish to supply an explicit source different from the server's, then you can do so via the `-Source` parameter. + +## Examples + +### Workspace + +The following example will send Request logs to Azure Log Analytics using the Workspace approach. + +```powershell +$method = New-PodeLogAzureMethod ` + -WorkspaceId '' ` + -SharedKey '' ` + -LogType 'MyCustomLog' + +$method | Enable-PodeLogRequestType +``` + +### Data Collection + +The following example will send JSON serialised Error logs to Azure Log Analytics using the Data Collection approach. + +```powershell +$method = New-PodeLogAzureMethod ` + -Endpoint 'https://logs-ingestion-0000.eastus2-1.ingest.monitor.azure.com' ` + -ImmutableId 'dcr-0000000000000000000000000000000' ` + -StreamName 'Custom-Json-Test' ` + -ClientId '' ` + -ClientSecret '' ` + -TenantId '' + +$method | Enable-PodeLogErrorType -SerialiseFormat Json +``` diff --git a/docs/Tutorials/Logging/Methods/Custom.md b/docs/Tutorials/Logging/Methods/Custom.md index e4c778053..34ad57b8a 100644 --- a/docs/Tutorials/Logging/Methods/Custom.md +++ b/docs/Tutorials/Logging/Methods/Custom.md @@ -1,22 +1,32 @@ # Custom -Sometimes you don't want to log to a file, or the terminal; instead you want to log to different provider, like LogStash, Splunk, Athena, or any other central logging platform. Although Pode doesn't have these inbuilt (yet!) it is possible to create a custom logging Method, where you define a ScriptBlock with logic to send logs to these platforms. To do this you can use [`New-PodeLogCustomMethod`](../../../../Functions/Logging/New-PodeLogCustomMethod). +If you want to log to different provider not yet supported by Pode, you can create a custom Log Method where you define a ScriptBlock with logic to send logs to these platforms. !!! important The `New-PodeLoggingMethod` function is now deprecated, please use [`New-PodeLogCustomMethod`](../../../../Functions/Logging/New-PodeLogCustomMethod) instead. -These custom Methods can be used for any log Type - Requests, Error, or Custom Types. +## Creation -The ScriptBlock you create will be supplied two arguments: +To create a custom Log Method you use [`New-PodeLogCustomMethod`](../../../../Functions/Logging/New-PodeLogCustomMethod). These custom Log Methods can be used with any Log Type - Requests, Error, or Custom Types. -1. The transformed log item to be logged. This could be a string (from Requests/Errors), or any custom type. -2. The options you supplied on [`New-PodeLogCustomMethod`](../../../../Functions/Logging/New-PodeLogCustomMethod). +The scriptblock you provide will be supplied with the following parameters, depending on the `-Version` supplied (default: 1) + +**Version 1** + +1. The transformed log item(s) to be logged. This could be a string (from Requests/Errors), or any custom type. +2. The arguments that were supplied from [`New-PodeLogCustomMethod`](../../../../Functions/Logging/New-PodeLogCustomMethod)'s `-ArgumentList` parameter. +3. The original raw log data from, for example, [`Write-PodeLog`](../../../../Functions/Logging/Write-PodeLog). + +**Version 2** + +1. A list of [Log Items](../../Objects#log-item). +2. The arguments that were supplied from [`New-PodeLogCustomMethod`](../../../../Functions/Logging/New-PodeLogCustomMethod)'s `-ArgumentList` parameter. ## Examples ### Send to S3 Bucket -This example will take whatever transformed log item is supplied to it, convert it to a string, and then send it off to some S3 bucket in AWS. In this case, it will be logging Requests: +This example, which uses `-Version 2` for the supplied parameters, will the supplied [Log Item](../../Objects#log-item), convert it to a string, and then send it off to some S3 bucket in AWS. In this case, it will be logging Requests: ```powershell $s3_options = @{ @@ -24,15 +34,15 @@ $s3_options = @{ SecretKey = $SecretKey } -$s3_logging = New-PodeLogCustomType -ArgumentList $s3_options -ScriptBlock { - param($item, $s3_opts) +$s3_logging = New-PodeLogCustomType -ArgumentList $s3_options -Version 2 -ScriptBlock { + param($logItem, $s3_opts) Write-S3Object ` -BucketName '' ` - -Content $item.ToString() ` + -Content $logItem.ToString() ` -AccessKey $s3_opts.AccessKey ` -SecretKey $s3_opts.SecretKey } -$s3_logging | Enable-PodeRequestLogType +$s3_logging | Enable-PodeLogRequestType ``` diff --git a/docs/Tutorials/Logging/Methods/Datadog.md b/docs/Tutorials/Logging/Methods/Datadog.md new file mode 100644 index 000000000..83e6e0b2e --- /dev/null +++ b/docs/Tutorials/Logging/Methods/Datadog.md @@ -0,0 +1,43 @@ +# Datadog + +The Datadog Log Method allows you to send logs to Datadog. This Log Method uses the general [`New-PodeLogApiMethod`](../../../../Functions/Logging/New-PodeLogApiMethod) under-the-hood. + +## Creation + +To create a new Datadog Log Method use [`New-PodeLogDatadogMethod`](../../../../Functions/Logging/New-PodeLogDatadogMethod), and supply the `-BaseUrl` for the server you wish to send logs to, and your `-ApiKey` for authentication. + +The `-BaseUrl` should be like `https://http-intake.logs.datadoghq.eu`, Pode will append the `/api/v2/logs` for you. + +If you wish to ignore any certificate checks, you may supply `-SkipCertificateCheck`. + +### Service + +The default service for your logs sent by Pode is the [Server's App Name](../../../../Basic#app-name). If you wish to supply an explicit service different from the server's, then you can do so via the `-Service` parameter. + +### Source + +The default source type sent by Pode is empty, if you wish to supply a source you can do so via the `-Source` parameter. + +### Tags + +The default tags supplied by Pode is empty, if you wish to supply any tags you can do so via the `-Tags` parameter as a hashtable. + +## Examples + +### Send Request Logs + +The following example will send Request logs to Datadog. + +```powershell +$method = New-PodeLogDatadogMethod -BaseUrl 'https://http-intake.logs.datadoghq.eu' -ApiKey '' +$method | Enable-PodeLogRequestType +``` + +### Send Error Logs + +The following example will send JSON serialised Error logs to Datadog. + +```powershell +$method = New-PodeLogDatadogMethod -BaseUrl 'https://http-intake.logs.datadoghq.eu' -ApiKey '' +$method | Enable-PodeLogErrorType -SerialiseFormat Json +``` diff --git a/docs/Tutorials/Logging/Methods/EventViewer.md b/docs/Tutorials/Logging/Methods/EventViewer.md index da9431017..584037934 100644 --- a/docs/Tutorials/Logging/Methods/EventViewer.md +++ b/docs/Tutorials/Logging/Methods/EventViewer.md @@ -1,6 +1,6 @@ # Event Viewer -You can log items to the Windows Event Viewer, using Pode's unbuilt Event Viewer logging Method, via [`New-PodeLogEventViewerMethod`](../../../../Functions/Logging/New-PodeLogEventViewerMethod). You can log anything, but it's best to use this in conjunction with [`Enable-PodeErrorLogType`](../../../../Functions/Logging/Enable-PodeErrorLogType) and [`Write-PodeErrorLog`](../../../../Functions/Logging/Write-PodeErrorLog). +You can log items to the Windows Event Viewer, using Pode's unbuilt Event Viewer Log Method, via [`New-PodeLogEventViewerMethod`](../../../../Functions/Logging/New-PodeLogEventViewerMethod). You can log anything, but it's best to use this in conjunction with [`Enable-PodeLogErrorType`](../../../../Functions/Logging/Enable-PodeLogErrorType) and [`Write-PodeErrorLog`](../../../../Functions/Logging/Write-PodeErrorLog). !!! note Errors will be logged using an appropriate error level, but other log items will be logged as Informational. @@ -13,7 +13,7 @@ You can log items to the Windows Event Viewer, using Pode's unbuilt Event Viewer ## Usage -When using this log method, Pode will first check if the source exists, and will then attempt to create it. To do this, you will need to be running Pode as an administrator. +When using this Log Method, Pode will first check if the source exists, and will then attempt to create it. To do this, you will need to be running Pode as an administrator. If however you're running Pode locally, or in a situation where you can't run Pode as a full admin - like in IIS - then you will first have to create the source yourself manually. Assuming a source of `Pode` and in the `Application` log, you can use the following: @@ -26,7 +26,7 @@ Once the source is created, Pode can log to the Event Viewer without being an ad To enable and log errors to the Event Viewer, the following will work: ```powershell -New-PodeLogEventViewerMethod | Enable-PodeErrorLogType +New-PodeLogEventViewerMethod | Enable-PodeLogErrorType ``` This will log to the `Application` log using `Pode` as the source. @@ -36,7 +36,7 @@ This will log to the `Application` log using `Pode` as the source. To log to a different event log, other than Application, you can specify the log via `-EventLogName`: ```powershell -New-PodeLogEventViewerMethod -EventLogName SomeLogName | Enable-PodeErrorLogType +New-PodeLogEventViewerMethod -EventLogName SomeLogName | Enable-PodeLogErrorType ``` ## Event Source @@ -44,5 +44,5 @@ New-PodeLogEventViewerMethod -EventLogName SomeLogName | Enable-PodeErrorLogType To log using a different source, other than Pode, you can specify the source via `-Source`: ```powershell -New-PodeLogEventViewerMethod -Source WebsiteName | Enable-PodeErrorLogType +New-PodeLogEventViewerMethod -Source WebsiteName | Enable-PodeLogErrorType ``` diff --git a/docs/Tutorials/Logging/Methods/File.md b/docs/Tutorials/Logging/Methods/File.md index deb0c029d..76409ef83 100644 --- a/docs/Tutorials/Logging/Methods/File.md +++ b/docs/Tutorials/Logging/Methods/File.md @@ -1,6 +1,6 @@ # File -You can log items to a file using Pode's inbuilt file logging Method, via [`New-PodeLogFileMethod`](../../../../Functions/Logging/New-PodeLogFileMethod). This allows you to define a maximum number of days to keep files, as well as a maximum file size. +You can log items to a file using Pode's inbuilt file Log Method, via [`New-PodeLogFileMethod`](../../../../Functions/Logging/New-PodeLogFileMethod). This allows you to define a maximum number of days to keep files, as well as a maximum file size. !!! note This will convert the supplied transformed log items into a string, if it isn't one already. @@ -14,10 +14,10 @@ By default, Pode will store all log files in a `./logs` directory at the root of ### Basic -The following example will setup the file logging Method for logging Requests: +The following example will setup the file Log Method for logging Requests: ```powershell -New-PodeLogFileMethod -Name 'requests' | Enable-PodeRequestLogType +New-PodeLogFileMethod -Name 'requests' | Enable-PodeLogRequestType ``` ### Maximum Days @@ -25,7 +25,7 @@ New-PodeLogFileMethod -Name 'requests' | Enable-PodeRequestLogType The following example will configure file logging to only keep a maximum number of days of logs. Ie, if you set `-MaxDays` to 4, then Pode will only store the last 4 days worth of logs. ```powershell -New-PodeLogFileMethod -Name 'requests' -MaxDays 4 | Enable-PodeRequestLogType +New-PodeLogFileMethod -Name 'requests' -MaxDays 4 | Enable-PodeLogRequestType ``` ### Maximum Size @@ -35,7 +35,7 @@ The following example will configure file logging to keep logging to a file unti In this example, the maximum size it limited to 10MB: ```powershell -New-PodeLogFileMethod -Name 'requests' -MaxSize 10MB | Enable-PodeRequestLogType +New-PodeLogFileMethod -Name 'requests' -MaxSize 10MB | Enable-PodeLogRequestType ``` ### Custom Path @@ -43,5 +43,5 @@ New-PodeLogFileMethod -Name 'requests' -MaxSize 10MB | Enable-PodeRequestLogType By default Pode puts all logs in the `./logs` directory. You can use a custom path by using `-Path`: ```powershell -New-PodeLogFileMethod -Name 'requests' -Path 'E:/logs' | Enable-PodeRequestLogType +New-PodeLogFileMethod -Name 'requests' -Path 'E:/logs' | Enable-PodeLogRequestType ``` diff --git a/docs/Tutorials/Logging/Methods/Network.md b/docs/Tutorials/Logging/Methods/Network.md new file mode 100644 index 000000000..ac91a0339 --- /dev/null +++ b/docs/Tutorials/Logging/Methods/Network.md @@ -0,0 +1,45 @@ +# Network + +The Network Log Method allows you to send logs over basic UDP, TCP, or TLS options. + +## Creation + +To create a new Network Log Method use [`New-PodeLogNetworkMethod`](../../../../Functions/Logging/New-PodeLogNetworkMethod), and supply the `-Server` IP/Hostname, some `-Transport` option, and some `-Port` number. + +The default transport is UDP, and the default port is 514 (ie, syslog). + +If you wish to ignore any certificate checks, in the case of TLS, you may supply `-SkipCertificateCheck`. + +## Examples + +### Send via UDP + +The following example will send Request logs to some UDP endpoint. + +```powershell +$method = New-PodeLogNetworkMethod -Server 'udp.example.com' -Port 8514 +$method | Enable-PodeLogRequestType +``` + +### Send via TCP + +The following example will send syslog formatted Error logs to some TCP endpoint. + +```powershell +$method = New-PodeLogNetworkMethod -Server 'tcp.example.com' -Transport Tcp +$method | Enable-PodeLogErrorType -LogFormat Syslog +``` + +### Send via TLS + +The following example will send JSON serialised Request logs to some TLS endpoint, and ignore any certificate checks. + +```powershell +$method = New-PodeLogNetworkMethod ` + -Server 'tls.localhost.com' ` + -Transport Tls ` + -Port 8514 ` + -SkipCertificateCheck + +$method | Enable-PodeLogRequestType -SerialiseFormat Json +``` diff --git a/docs/Tutorials/Logging/Methods/Splunk.md b/docs/Tutorials/Logging/Methods/Splunk.md new file mode 100644 index 000000000..183401633 --- /dev/null +++ b/docs/Tutorials/Logging/Methods/Splunk.md @@ -0,0 +1,43 @@ +# Splunk + +The Splunk Log Method allows you to send logs to your Splunk server - self-hosted or cloud. This Log Method uses the general [`New-PodeLogApiMethod`](../../../../Functions/Logging/New-PodeLogApiMethod) under-the-hood. + +## Creation + +To create a new Splunk Log Method use [`New-PodeLogSplunkMethod`](../../../../Functions/Logging/New-PodeLogSplunkMethod), and supply the `-BaseUrl` for the server you wish to send logs to, and your HTTP Endpoint Collection's `-Token` for authentication. + +The `-BaseUrl` should be like `http://localhost:8088`, Pode will append the `/services/collector` for you. + +If you wish to ignore any certificate checks, you may supply `-SkipCertificateCheck`. + +### Source Type + +The default source type sent by Pode is empty, allowing Splunk to automatically select the best fit. However, if you wish to supply an explicit source type you can do so via the `-SourceType` parameter. + +### Source + +The default source for your logs sent by Pode is the [Server's App Name](../../../../Basic#app-name). If you wish to supply an explicit source different from the server's, then you can do so via the `-Source` parameter. + +### Index + +The default index supplied by Pode is empty, which usually equates to the main index in Splunk. However, if you wish to supply an explicit index you can do so via the `-Index` parameter. + +## Examples + +### Send Request Logs + +The following example will send Request logs to your Splunk server. + +```powershell +$method = New-PodeLogSplunkMethod -BaseUrl 'http://localhost:8088' -Token '' +$method | Enable-PodeLogRequestType +``` + +### Send Error Logs + +The following example will send non-serialised and non-formatted Error logs to your Splunk server (ie, a raw hashtable which will be "serialised" as JSON when sending to the Splunk API). + +```powershell +$method = New-PodeLogSplunkMethod -BaseUrl 'http://localhost:8088' -Token '' +$method | Enable-PodeLogErrorType -SerialiseFormat None +``` diff --git a/docs/Tutorials/Logging/Methods/Terminal.md b/docs/Tutorials/Logging/Methods/Terminal.md index 6530013d9..229d3ead4 100644 --- a/docs/Tutorials/Logging/Methods/Terminal.md +++ b/docs/Tutorials/Logging/Methods/Terminal.md @@ -12,8 +12,8 @@ You can log items to the terminal using Pode's inbuilt terminal Method, via [`Ne ### Basic -The following example will setup the terminal logging Method for logging Requests: +The following example will setup the terminal Log Method for logging Requests: ```powershell -New-PodeLogTerminalMethod | Enable-PodeRequestLogType +New-PodeLogTerminalMethod | Enable-PodeLogRequestType ``` diff --git a/docs/Tutorials/Logging/Objects.md b/docs/Tutorials/Logging/Objects.md new file mode 100644 index 000000000..673a49fc6 --- /dev/null +++ b/docs/Tutorials/Logging/Objects.md @@ -0,0 +1,28 @@ +# Objects + +This page describes the various .NET objects that will be supplied to Log Type and Method scriptblocks. + +## Log Event + +This is typically supplied to custom Log Type scriptblocks. It will be an `IPodeLogEvent` object with the following properties. + +| Name | Type | Description | +| --------- | -------------- | --------------------------------------------------- | +| Data | `object` | The raw log data from, for example, `Write-PodeLog` | +| Name | `string` | The name of the Log Type this event is for | +| Level | `PodeLogLevel` | The log level of this event | +| Metadata | `hashtable` | Any optionally supplied metadata | +| Timestamp | `datetime` | The timestamp of this event | + +## Log Item + +This is typically supplied, as a list, to custom Log Method scriptblocks. It will be an `IPodeLogItem` object with the following properties. + +| Name | Type | Description | +| ---------- | --------------- | ------------------------------------------------------- | +| Data | `object` | The transformed log data returned by the Log Type | +| Event | `IPodeLogEvent` | The original Log Event for this Log Item | +| ToString() | `method` | A utility method return the Log Item's data as a string | + +!!! tip + `Data` here will be the transformed/serialised data from the Log Type. However, if you need access to the original raw data you'll find this under `Event.Data` - along with original Log Level and Timestamp. diff --git a/docs/Tutorials/Logging/Overview.md b/docs/Tutorials/Logging/Overview.md index 73bfcb3bf..01afebe5f 100644 --- a/docs/Tutorials/Logging/Overview.md +++ b/docs/Tutorials/Logging/Overview.md @@ -2,10 +2,10 @@ There are two aspects to logging in Pode: Types and Methods. -* **Types** define how log items are transformed, and what should be supplied to the Method, such as Error or Request. +* **Types** define how log items are transformed, serialised, and/or formatted, and what should be supplied to the Method, such as Error or Request. * **Methods** define how the transformed log items should be recorded, such as to a file, terminal, or event viewer. -Think of it like an ETL data pipeline: +Think of it like the phases of an ETL data pipeline: ```mermaid graph LR @@ -22,18 +22,22 @@ graph LR Transform --> Load ``` -While we're technically not "extracting" data, the log item is the initial entity/data in the pipeline. After which it is transformed by the Log Type (Error, Request, etc.), and then loaded - or "outputted" - to an appropriate log source like file/etc. +| Phase | Description | +| --------- | ------------------------------------------------------------------------------ | +| Extract | The originating log item, for example from `Write-PodeErrorLog` | +| Transform | The log item is transformed by the Log Type, for example serialisation | +| Load | The resultant log message is outputted via a Log Method, for example to a file | -For example when you supply an Exception to [`Write-PodeErrorLog`](../../../Functions/Logging/Write-PodeErrorLog), the Exception passed to Pode's inbuilt Error logging Type which transforms it into a string; which is then passed to a logging Method - like a File or Terminal - to be outputted/recorded. +For example when you supply an Exception to [`Write-PodeErrorLog`](../../../Functions/Logging/Write-PodeErrorLog), the Exception is passed to Pode's inbuilt Error Log Type which transforms it into a string; which is then passed to a Log Method - like a File or Terminal - to be outputted/recorded. -Pode has several built-in logging Methods for you to use: +Pode has several built-in Log Methods for you to use: * [Terminal](../Methods/Terminal) * [File](../Methods/File) * [Event Viewer](../Methods/EventViewer) * [Custom](../Methods/Custom) -As well as some built-in logging Types: +As well as some built-in Log Types: * [Error](../Types/Errors) * [Requests](../Types/Requests) @@ -41,7 +45,7 @@ As well as some built-in logging Types: ## Masking Values -When logging items you have the ability to mask sensitive information. This is supported in all inbuilt log Methods by default (except Custom) - it can be supported in Custom methods via [`Protect-PodeLogItem`](../../../Functions/Logging/Protect-PodeLogItem). +When logging items you have the ability to mask sensitive information. This is supported in all inbuilt Log Methods by default (except Custom) - it can be supported in Custom methods via [`Protect-PodeLogItem`](../../../Functions/Logging/Protect-PodeLogItem). Information to mask is determined using regex defined within the `server.psd1` configuration file. You can supply multiple patterns, and even define what the mask is - the default being `********`. @@ -126,13 +130,13 @@ To specify a custom mask, you can do this in the configuration file: By default all log items are recorded one-by-one, but this can obviously become very slow if a lot of log items are being processed. -To help speed this up you can create a batching info object using [`New-PodeLogBatchInfo`](../../../Functions/Logging/New-PodeLogBatchInfo), and supply it to your logging Method: +To help speed this up you can create a batching info object using [`New-PodeLogBatchInfo`](../../../Functions/Logging/New-PodeLogBatchInfo), and supply it to your Log Method: ```powershell $batchInfo = New-PodeLogBatchInfo -Size 10 -New-PodeLogTerminalMethod -BatchInfo $batchInfo | Enable-PodeRequestLogType +New-PodeLogTerminalMethod -BatchInfo $batchInfo | Enable-PodeLogRequestType ``` -Instead of writing logs one-by-one, the above will cache transformed log items. Once the appropriate number of cached log items is met (in this case 10), all of the log items will be sent to the log Method at once. This means that the log Method's scriptblock will receive an array of items, rather than a single item. +Instead of writing logs one-by-one, the above will cache transformed log items. Once the appropriate number of cached log items is met (in this case 10), all of the log items will be sent to the Log Method at once. This means that the Log Method's scriptblock will receive an array of items, rather than a single item. You can also specify a `-Timeout` value, in seconds, so that if your batch size is 10 but only 5 log items are added, then after the timeout value the log items will be sent to your method regardless the current number of cached log items. diff --git a/docs/Tutorials/Logging/Types/Custom.md b/docs/Tutorials/Logging/Types/Custom.md index 05bbea1c6..c9d5818df 100644 --- a/docs/Tutorials/Logging/Types/Custom.md +++ b/docs/Tutorials/Logging/Types/Custom.md @@ -1,26 +1,43 @@ # Custom -You can define a Custom logging Type in Pode by using [`Add-PodeLogType`](../../../../Functions/Logging/Add-PodeLogType). Much like Requests and Errors, this function too accepts one or more logging Methods - such as the [Terminal](../../Methods/Terminal) Method. +You can define a Custom Log Type in Pode by using [`Add-PodeLogType`](../../../../Functions/Logging/Add-PodeLogType). Much like Requests and Errors, this function too accepts one or more Log Methods - such as the [Terminal](../../Methods/Terminal) Method. !!! important - The `Add-PodeLogger` function is now deprecated, please use [`Add-PodeLogType`](../../../../Functions/Logging/Add-PodeLogType) instead. + The `Add-PodeLogger` function is now deprecated, please use [`Add-PodeLogType`](../../../../Functions/Logging/Add-PodeLogType) instead. The former is aliased to the latter for now. -When adding a Custom logging Type, you supply a `-ScriptBlock` plus an array of optional arguments in `-ArgumentList`. The function also requires a unique `-Name`, so that it can be referenced from [`Write-PodeLog`](../../../../Functions/Logging/Write-PodeLog). +## Creation -The ScriptBlock will be supplied with the following arguments: +When adding a Custom Log Type, you supply a `-ScriptBlock` plus an array of optional arguments in `-ArgumentList`. The function also requires a unique `-Name`, so that it can be referenced from [`Write-PodeLog`](../../../../Functions/Logging/Write-PodeLog). -1. A transformed, or raw, log item to log that was supplied via [`Write-PodeLog`](../../../../Functions/Logging/Write-PodeLog). +The scriptblock will be supplied with the following parameters, depending on the `-Version` supplied (default: 1) + +**Version 1** + +1. The raw log item that was supplied via [`Write-PodeLog`](../../../../Functions/Logging/Write-PodeLog). +2. The arguments that were supplied from [`Add-PodeLogType`](../../../../Functions/Logging/Add-PodeLogType)'s `-ArgumentList` parameter. + +**Version 2** + +1. The [Log Event](../../Objects#log-event) object, with references to the raw data from [`Write-PodeLog`](../../../../Functions/Logging/Write-PodeLog), the log Level, Timestamp, any Metadata, and the Log Type's Name. 2. The arguments that were supplied from [`Add-PodeLogType`](../../../../Functions/Logging/Add-PodeLogType)'s `-ArgumentList` parameter. +## Formatting + +More information on formatting can be [found here](../../Formatting). + ## Log Levels -The Custom logging Type uses the following log levels (Informational is the default): +The Custom Log Type uses the following log levels: -* `Error` -* `Warning` -* `Informational` -* `Verbose` -* `Debug` +* Emergency +* Alert +* Critical +* Error +* Warning +* Notice +* Informational (default) +* Verbose +* Debug You can alter the log level by supplying `-Levels` to [`Add-PodeLogType`](../../../../Functions/Logging/Add-PodeLogType) - you can supply one or more. @@ -33,7 +50,7 @@ You can control the log level of custom log items being written, by supplying `- ### Log to File -This example will create a Custom logging Type that will take some custom hashtable, transform it into a string, and then pass that to the inbuilt File logging Method: +This example will create a Custom Log Type that will take some custom hashtable, transform it into a string, and then pass that to the inbuilt File Log Method: ```powershell New-PodeLogFileMethod -Name 'Custom' | Add-PodeLogType -Name 'Main' -ScriptBlock { @@ -48,9 +65,51 @@ Write-PodeLog -Name 'Main' -InputObject @{ } ``` +### Log as JSON + +This example will create a Custom Log Type that will take some custom hashtable, select appropriate data, serialise it into JSON, and then pass that to the inbuilt File Log Method. This example also uses `-Version 2` of the supplied parameters. + +```powershell +New-PodeLogFileMethod -Name 'Custom' | Add-PodeLogType -Name 'Main' -Version 2 -SerialiseFormat Json -ScriptBlock { + param($logEvent) + return [ordered]@{ + Level = $logEvent.Level + Key1 = $logEvent.Data.Key1 + MergedKey = "$($logEvent.Data.Key2) & $($logEvent.Data.Key3)" + } +} + +Write-PodeLog -Name 'Main' -InputObject @{ + Key1 = 'Value1' + Key2 = 'Value2' + Key3 = 'Value3' +} +``` + +### Log as Syslog + +This example will create a Custom Log Type that will take some custom hashtable, select appropriate data, convert it to Syslog format, and then pass that to the inbuilt File Log Method. This example also uses `-Version 2` of the supplied parameters. + +```powershell +New-PodeLogFileMethod -Name 'Custom' | Add-PodeLogType -Name 'Main' -Version 2 -LogFormat -ScriptBlock { + param($logEvent) + return [ordered]@{ + Level = $logEvent.Level + Key1 = $logEvent.Data.Key1 + MergedKey = "$($logEvent.Data.Key2) & $($logEvent.Data.Key3)" + } +} + +Write-PodeLog -Name 'Main' -InputObject @{ + Key1 = 'Value1' + Key2 = 'Value2' + Key3 = 'Value3' +} +``` + ### Log to Multiple -The following example will also enable a Custom logging Type, but will output all items to the Terminal and to a File: +The following example will also enable a Custom Log Type, but will output all items to the Terminal and to a File: ```powershell $methods = @( @@ -72,7 +131,7 @@ Write-PodeLog -Name 'Main' -InputObject @{ ### Using Raw Item -The following example uses the Terminal logging Method, and sets a Custom logging Type to return and supply the raw log item to the Terminal Method's scriptblock. The Terminal Method simply outputs the raw item to the CLI. +The following example uses the Terminal Log Method, and sets a Custom Log Type to return and supply the raw log item to the Terminal Method's scriptblock. The Terminal Method simply outputs the raw item to the CLI. ```powershell New-PodeLogTerminalMethod | Add-PodeLogType -Name 'Example' -Raw @@ -81,4 +140,4 @@ New-PodeLogTerminalMethod | Add-PodeLogType -Name 'Example' -Raw Write-PodeLog -Name 'Example' -InputObject 'This message will simply be outputted to CLI' ``` -This is useful when all you're supplying to your Custom log Type is strings or other primitive value types. +This is useful when all you're supplying to your Custom Log Type is strings or other primitive value types. diff --git a/docs/Tutorials/Logging/Types/Errors.md b/docs/Tutorials/Logging/Types/Errors.md index 4c538e8a2..4107b3120 100644 --- a/docs/Tutorials/Logging/Types/Errors.md +++ b/docs/Tutorials/Logging/Types/Errors.md @@ -1,22 +1,46 @@ # Errors -Pode has an inbuilt Error logging Type, that parses and transforms Exceptions/ErrorRecords, and will return a valid log item for whatever logging Method you supply. +Pode has an inbuilt Error Log Type, that parses and transforms Exceptions/ErrorRecords, and will return a valid log item for whatever Log Method you supply. -It also has support for error levels (such as Error, Warning, Verbose), with support for only allowing certain levels to be logged. By default, Error is always logged if no levels are supplied. +It also has support for error levels (such as Error, Warning, Verbose), with support for only allowing certain levels to be logged. By default the following levels are always logged if no levels are supplied: Emergency, Alert, Critical, and Error. ## Enabling -To enable and use the Error logging Type you use [`Enable-PodeErrorLogType`](../../../../Functions/Logging/Enable-PodeErrorLogType), supplying one or more logging Methods - such as the [Terminal](../../Methods/Terminal) Method. +To enable the Error Log Type use [`Enable-PodeLogErrorType`](../../../../Functions/Logging/Enable-PodeLogErrorType), and supply one or more Log Methods - such as the [Terminal](../../Methods/Terminal) Method. !!! important - The `Enable-PodeErrorLogging` function is now deprecated, please use [`Enable-PodeErrorLogType`](../../../../Functions/Logging/Enable-PodeErrorLogType) instead. + The `Enable-PodeErrorLogging` function is now deprecated, please use [`Enable-PodeLogErrorType`](../../../../Functions/Logging/Enable-PodeLogErrorType) instead. The former is aliased to the latter for now. -When Pode logs an error, the information being logged is as follows: +## Custom Logic + +By default, if you supply no `-ScriptBlock`, Pode will use inbuilt data selection logic on error log items. However, if you do supply a custom `-ScriptBlock` then you can select/return your own data. + +This custom scriptblock will be supplied the [Log Event](../../Objects#log-event), and any arguments supplied to `-ArgumentList`, as parameters. + +```powershell +Enable-PodeLogRequestType -ScriptBlock { + param($logEvent) + return @{ + Message = $logEvent.Data.Message + } +} +``` + +!!! note + The `$logEvent.Data` will be the same raw data as found in below in [Raw Request](#raw-request). + +## Formatting + +More information on formatting can be [found here](../../Formatting). + +When Pode logs an error, the information logged is as follows: | Property | Description | | ------------ | ----------------------------------------------------- | | `Date` | The date/time the error occurred | | `Level` | The level of the error, such as Error or verbose | +| `ThreadId` | The thread ID of the current runspace/process | +| `ContextId` | The Pode Context ID of the current request | | `Server` | The name of the machine from where the error occurred | | `Category` | The category/type of error that was thrown | | `Message` | The error message | @@ -24,15 +48,19 @@ When Pode logs an error, the information being logged is as follows: ## Log Levels -The Error logging Type uses the following log levels (Error is the default): +The Error Log Type uses the following log levels: -* `Error` -* `Warning` -* `Informational` -* `Verbose` -* `Debug` +* Emergency (default) +* Alert (default) +* Critical (default) +* Error (default) +* Warning +* Notice +* Informational +* Verbose +* Debug -You can alter the log level by supplying `-Levels` to [`Enable-PodeErrorLogType`](../../../../Functions/Logging/Enable-PodeErrorLogType) - you can supply one or more. +You can alter the log level by supplying `-Levels` to [`Enable-PodeLogErrorType`](../../../../Functions/Logging/Enable-PodeLogErrorType) - you can supply one or more. !!! tip To enable all log levels more easily, simply supply `-Levels '*'` @@ -60,7 +88,7 @@ Or, a raw string message: ## Internal Logging -When error logging is enabled, you'll start to also see internal logging from Pode. Pode at present has internal Error logging, as well as Debug and Verbose logging from its various Adapters. +When error logging is enabled, you'll also see internal logging from Pode. Pode at present has internal Error logging, as well as Debug and Verbose logging from its various Adapters. The internal error logging will show you unhandled exceptions from routes, middleware, etc. @@ -68,15 +96,31 @@ The internal error logging will show you unhandled exceptions from routes, middl ### Log to Terminal -The following example simply enables Error logging, and will output all items to the terminal - by default, only Error level items are logged: +The following example enables the Error Log Type, and will output all items to the terminal - by default, only Emergency, Alert, Critical, and Error level items are logged: + +```powershell +New-PodeLogTerminalMethod | Enable-PodeLogErrorType +``` + +### Log as JSON + +The following example enables the Error Log Type, and will output all items to the terminal as JSON - by default, only Emergency, Alert, Critical, and Error level items are logged: + +```powershell +New-PodeLogTerminalMethod | Enable-PodeLogErrorType -SerialiseFormat Json +``` + +### Log as Syslog + +The following example enables the Error Log Type, and will output all items to the terminal in Syslog format - by default, only Emergency, Alert, Critical, and Error level items are logged: ```powershell -New-PodeLogTerminalMethod | Enable-PodeErrorLogType +New-PodeLogTerminalMethod | Enable-PodeLogErrorType -LogFormat Syslog ``` ### Log to Multiple -The following example will also enable Error logging, but will output all items to the Terminal and to a File: +The following example will also enable the Error Log Type, but will output all items to the Terminal and to a File: ```powershell $methods = @( @@ -84,7 +128,7 @@ $methods = @( New-PodeLogFileMethod -Name 'errors' ) -$methods | Enable-PodeErrorLogType +$methods | Enable-PodeLogErrorType ``` ### Log Verbose @@ -92,12 +136,12 @@ $methods | Enable-PodeErrorLogType The following example will enable Error logging, and it will log all errors levels except Debug: ```powershell -New-PodeLogTerminalMethod | Enable-PodeErrorLogType -Levels Error, Warning, Informational, Verbose +New-PodeLogTerminalMethod | Enable-PodeLogErrorType -Levels Error, Warning, Informational, Verbose ``` ### Using Raw Item -The following example uses a Custom logging Method, and sets Error logging to return and supply the raw log item to the Custom Method's scriptblock. The Custom Method simply logs the Server and Message to the terminal (but could be to something like an S3 bucket): +The following example uses a Custom Log Method, and sets Error logging to supply the raw log item to the Custom Method's scriptblock instead of a transformed one. The Custom Method simply logs the Server and Message to the terminal (but could be to something like an S3 bucket): ```powershell $method = New-PodeLogCustomMethod -ScriptBlock { @@ -105,21 +149,40 @@ $method = New-PodeLogCustomMethod -ScriptBlock { "$($item.Server) - $($item.Message)" | Out-Default } -$method | Enable-PodeErrorLogType -Raw +$method | Enable-PodeLogErrorType -Raw ``` ## Raw Error -The raw log item that the Error log Type will supply to any Custom logging Methods will look as follows: +The raw log item that the Error Log Type will supply to any Custom Log Methods will be the following hashtable - this is also the data that will be supplied to any custom `-ScriptBlock`: ```powershell @{ Date = [datetime]::Now Level = 'Error' - Server = 'ComputerName' + Server = 'APP-VM-1' + ContextId = '6087a032-8e02-43ed-bbd8-e783b9839f3a' + ThreadId = 1 + Category = 'InvalidOperation: (:) [], RuntimeException' + Message = 'You cannot call a method on a null-valued expression.' + StackTrace = 'at , : line 45' +} +``` + +## Serialise Data + +If you supply your own custom `-SerialiseScriptBlock`, the following hashtable will be supplied - unless you also supply your own custom `-ScriptBlock`: + +```powershell +[ordered]@{ + Date = '2026-07-04 16:59:00' + Level = 'Error' + ThreadId = 1 ContextId = '6087a032-8e02-43ed-bbd8-e783b9839f3a' + Server = 'APP-VM-1' Category = 'InvalidOperation: (:) [], RuntimeException' Message = 'You cannot call a method on a null-valued expression.' StackTrace = 'at , : line 45' } ``` + diff --git a/docs/Tutorials/Logging/Types/Requests.md b/docs/Tutorials/Logging/Types/Requests.md index 141c21b90..c95381cad 100644 --- a/docs/Tutorials/Logging/Types/Requests.md +++ b/docs/Tutorials/Logging/Types/Requests.md @@ -1,29 +1,70 @@ # Requests -Pode has an inbuilt Request logging Type, which will parse and transform a valid log item for use with any supplied logging Method. +Pode has an inbuilt Request Log Type, which will parse and transform web requests for use with any supplied Log Method. + +!!! note + This Log Type currently only supports web requests. ## Enabling -To enable and use the Request logging Type you use [`Enable-PodeRequestLogType`](../../../../Functions/Logging/Enable-PodeRequestLogType), supplying one or more logging Methods - such as the [Terminal](../../Methods/Terminal) Method. +To enable the Request Log Type use [`Enable-PodeLogRequestType`](../../../../Functions/Logging/Enable-PodeLogRequestType), and supply one or more Log Methods - such as the [Terminal](../../Methods/Terminal) Method. !!! important - The `Enable-PodeRequestLogging` function is now deprecated, please use [`Enable-PodeRequestLogType`](../../../../Functions/Logging/Enable-PodeRequestLogType) instead. + The `Enable-PodeRequestLogging` function is now deprecated, please use [`Enable-PodeLogRequestType`](../../../../Functions/Logging/Enable-PodeLogRequestType) instead. The former is aliased to the latter for now. + +## Custom Logic + +By default, if you supply no `-ScriptBlock`, Pode will use inbuilt data selection logic on request log items. However, if you do supply a custom `-ScriptBlock` then you can select/return your own data. + +This custom scriptblock will be supplied the [Log Event](../../Objects#log-event), and any arguments supplied to `-ArgumentList`, as parameters. + +```powershell +Enable-PodeLogRequestType -ScriptBlock { + param($logEvent) + return @{ + Method = $logEvent.Data.Request.Method + } +} +``` + +!!! note + The `$logEvent.Data` will be the same raw data as found in below in [Raw Request](#raw-request). + +## Formatting -The Request logging Type will transform a supplied raw log item into a [Combined Log Format](https://httpd.apache.org/docs/1.3/logs.html#combined) string. This string is then supplied to the logging Method's scriptblock. If you're using a Custom logging method and want the raw log item instead, you can supply `-Raw` to [`Enable-PodeRequestLogType`](../../../../Functions/Logging/Enable-PodeRequestLogType). +More information on formatting can be [found here](../../Formatting). + +The Request Log Type will transform a supplied raw web request into a [Combined Log Format](https://httpd.apache.org/docs/1.3/logs.html#combined) string. This string is then supplied to the Log Method's scriptblock. If you're using a Custom Log Method and want the raw log item instead, you can supply `-Raw` to [`Enable-PodeLogRequestType`](../../../../Functions/Logging/Enable-PodeLogRequestType). ## Examples ### Log to Terminal -The following example simply enables Request logging, and will output all items to the Terminal: +The following example enables the Request Log Type, and will output all items to the Terminal: + +```powershell +New-PodeLogTerminalMethod | Enable-PodeLogRequestType +``` + +### Log as JSON + +The following example enables the Request Log Type, and will output all items to the Terminal as JSON: + +```powershell +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -SerialiseFormat Json +``` + +### Log as Syslog + +The following example enables the Request Log Type, and will output all items to the Terminal in Syslog format: ```powershell -New-PodeLogTerminalMethod | Enable-PodeRequestLogType +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -LogFormat Syslog ``` ### Log to Multiple -The following example will also enable Request logging, but will output all items to the Terminal and to a File: +The following example will also enable the Request Log Type, but will output all items to the Terminal and to a File: ```powershell $methods = @( @@ -31,12 +72,12 @@ $methods = @( New-PodeLogFileMethod -Name 'requests' ) -$methods | Enable-PodeRequestLogType +$methods | Enable-PodeLogRequestType ``` ### Using Raw Item -The following example uses a Custom logging Method, and sets Request logging Type to return and supply the raw log item to the Custom method's scriptblock instead of a transformed one. The Custom Method simply logs the Host and StatusCode to the terminal (but could be to something like an S3 bucket): +The following example uses a Custom Log Method, and sets the Request Log Type to supply the raw log item to the Custom method's scriptblock instead of a transformed one. The Custom Method simply logs the Host and StatusCode to the terminal (but could be to something like an S3 bucket): ```powershell $method = New-PodeLogCustomMethod -ScriptBlock { @@ -44,7 +85,7 @@ $method = New-PodeLogCustomMethod -ScriptBlock { "$($item.Host) - $($item.Response.StatusCode)" | Out-Default } -$method | Enable-PodeRequestLogType -Raw +$method | Enable-PodeLogRequestType -Raw ``` ### Username @@ -54,24 +95,24 @@ If you're not using any Authentication then the "user" field in the log will alw For example, if the username was actually user "ID": ```powershell -Enable-PodeRequestLogType -UsernameProperty 'ID' +Enable-PodeLogRequestType -UsernameProperty 'ID' ``` -Or if the username was inside another "Meta" property, and then within a "Username" property inside the Meta object: +Or if the "Username" property is instead a sub-property of another "Meta" property: ```powershell -Enable-PodeRequestLogType -UsernameProperty 'Meta.Username' +Enable-PodeLogRequestType -UsernameProperty 'Meta.Username' ``` ## Raw Request -The raw log item that the Request log Type will supply to any Custom logging Methods will look as follows: +The raw log item that the Request Log Type will supply to any Custom Log Method will be the following hashtable - this is also the data that will be supplied to any custom `-ScriptBlock`: ```powershell @{ Host = '10.10.0.3' - RfcUserIdentity = '-' - User = '-' + RfcUserIdentity = $null + User = $null Date = '14/Jun/2018:20:23:52 +01:00' UtcDate = [datetime] Request = @{ @@ -91,3 +132,23 @@ The raw log item that the Request log Type will supply to any Custom logging Met } } ``` + +## Serialise Data + +If you supply your own custom `-SerialiseScriptBlock`, the following hashtable will be supplied - unless you also supply your own custom `-ScriptBlock`: + +```powershell +[ordered]@{ + Host = "10.10.0.3", + Identifier = $null, + User = $null, + Date = "14/Jun/2018:20:23:52 +01:00", + Method = "GET", + Resource = "/api/users", + Protocol = "HTTP/1.1", + StatusCode = 200, + Size = 9001, + Referrer = "", + UserAgent = "" +} +``` diff --git a/docs/Tutorials/Server Operations/Overview.md b/docs/Tutorials/Server Operations/Overview.md index 6247c5a65..7b8941eca 100644 --- a/docs/Tutorials/Server Operations/Overview.md +++ b/docs/Tutorials/Server Operations/Overview.md @@ -6,7 +6,7 @@ Pode offers a suite of server management operations to provide granular control - [**Suspending**](./Suspending/Overview.md): Temporarily pause server activities and later restore them without a full restart. - [**Restarting**](./Restarting/Overview.md): Multiple ways to restart the server, including file monitoring, scheduled restarts, and manual commands. -- [**Disabling**](./Disabling/Overview.md): Block or allow new incoming requests without affecting the server’s state. +- [**Disabling**](./Disabling/Overview.md): Block or allow new incoming requests without affecting the server's state. --- @@ -50,7 +50,7 @@ Pode introduces the ability to configure and control server behaviors using the ### Benefits of Allowed Actions -- **Customizable Behavior**: Tailor server operations to match your application’s requirements. +- **Customizable Behavior**: Tailor server operations to match your application's requirements. - **Enhanced Control**: Prevent unwanted actions like suspending or restarting during critical operations. - **Predictability**: Enforce consistent timeouts for suspend and resume actions to avoid delays. - **Middleware Control**: Specify a custom middleware scriptblock for handling specific scenarios, such as client retries during downtime. @@ -59,7 +59,7 @@ Pode introduces the ability to configure and control server behaviors using the ## Monitoring the Server State -In addition to managing operations, Pode allows you to monitor the server's state using the `Get-PodeServerState` function. This command evaluates the server’s internal state and returns a status such as: +In addition to managing operations, Pode allows you to monitor the server's state using the `Get-PodeServerState` function. This command evaluates the server's internal state and returns a status such as: - **Starting**: The server is initializing. - **Running**: The server is actively processing requests. diff --git a/docs/Tutorials/Server Operations/Suspending/Overview.md b/docs/Tutorials/Server Operations/Suspending/Overview.md index 3b8777d6b..51ed0cb82 100644 --- a/docs/Tutorials/Server Operations/Suspending/Overview.md +++ b/docs/Tutorials/Server Operations/Suspending/Overview.md @@ -1,10 +1,10 @@ # 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. +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-PodeServer` function. This function will pause all active server runspaces, effectively putting the server into a suspended state. Here’s how to do it: +To suspend a running Pode server, use the `Suspend-PodeServer` 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**: @@ -20,14 +20,14 @@ To suspend a running Pode server, use the `Suspend-PodeServer` function. This fu - When you run `Suspend-PodeServer`, 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. + - 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-PodeServer`. This will restore the Pode server to its normal operational state: +Once you've completed any tasks or troubleshooting, you can resume the server using `Resume-PodeServer`. This will restore the Pode server to its normal operational state: 1. **Run the Resume Command**: - Call `Resume-PodeServer` to bring the server back online. diff --git a/examples/Caching.ps1 b/examples/Caching.ps1 index 6a61b53f1..b30f038a0 100644 --- a/examples/Caching.ps1 +++ b/examples/Caching.ps1 @@ -48,7 +48,7 @@ Start-PodeServer -Threads 3 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http # log errors - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Set-PodeCacheDefaultTtl -Value 60 diff --git a/examples/Create-Routes.ps1 b/examples/Create-Routes.ps1 index 915a65445..3e9f5d0c4 100644 --- a/examples/Create-Routes.ps1 +++ b/examples/Create-Routes.ps1 @@ -35,7 +35,7 @@ catch { throw } Start-PodeServer -Threads 1 -ScriptBlock { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeRoute -PassThru -Method Get -Path '/routeCreateScriptBlock/:id' -ScriptBlock ([ScriptBlock]::Create( (Get-Content -Path "$ScriptPath\scripts\routeScript.ps1" -Raw))) | Set-PodeOARouteInfo -Summary 'Test' -OperationId 'routeCreateScriptBlock' -PassThru | diff --git a/examples/Dot-SourceScript.ps1 b/examples/Dot-SourceScript.ps1 index 2d726983d..27f7d58f8 100644 --- a/examples/Dot-SourceScript.ps1 +++ b/examples/Dot-SourceScript.ps1 @@ -39,7 +39,7 @@ catch { throw } # runs the logic once, then exits Start-PodeServer { - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Use-PodeScript -Path './modules/Script1.ps1' } diff --git a/examples/File-Watchers.ps1 b/examples/File-Watchers.ps1 index 1d862e86d..7b65542c3 100644 --- a/examples/File-Watchers.ps1 +++ b/examples/File-Watchers.ps1 @@ -39,7 +39,7 @@ catch { throw } Start-PodeServer -Verbose { # enable logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeFileWatcher -Path $ScriptPath -Include '*.ps1' -ScriptBlock { "[$($FileEvent.Type)][$($FileEvent.Parameters['project'])]: $($FileEvent.FullPath)" | Out-Default diff --git a/examples/FileBrowser/FileBrowser.ps1 b/examples/FileBrowser/FileBrowser.ps1 index 7cb9fd27c..4be0267d6 100644 --- a/examples/FileBrowser/FileBrowser.ps1 +++ b/examples/FileBrowser/FileBrowser.ps1 @@ -45,8 +45,8 @@ Start-PodeServer -ConfigFile '..\Server.psd1' -ScriptBlock { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http -Default - New-PodeLogTerminalMethod | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup basic auth (base64> username:password in header) New-PodeAuthScheme -Basic -Realm 'Pode Static Page' | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { diff --git a/examples/Logging.ps1 b/examples/Logging.ps1 index cc33793f7..3cf23662f 100644 --- a/examples/Logging.ps1 +++ b/examples/Logging.ps1 @@ -50,11 +50,11 @@ Start-PodeServer { switch ($LOGGING_TYPE.ToLowerInvariant()) { 'terminal' { - New-PodeLogTerminalMethod | Enable-PodeRequestLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType } 'file' { - New-PodeLogFileMethod -Name 'requests' -MaxDays 4 | Enable-PodeRequestLogType + New-PodeLogFileMethod -Name 'requests' -MaxDays 4 | Enable-PodeLogRequestType } 'custom' { @@ -63,7 +63,7 @@ Start-PodeServer { # send request row to S3 } - $type | Enable-PodeRequestLogType + $type | Enable-PodeLogRequestType } } diff --git a/examples/Mail-Server.ps1 b/examples/Mail-Server.ps1 index daea8c965..abf2d0cd0 100644 --- a/examples/Mail-Server.ps1 +++ b/examples/Mail-Server.ps1 @@ -51,7 +51,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Protocol Smtps -SelfSigned -TlsMode Implicit # enable logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType -Levels Error, Debug + New-PodeLogTerminalMethod | Enable-PodeLogErrorType -Levels Error, Debug # allow the local ip #Add-PodeAccessRule -Access Allow -Type IP -Values 127.0.0.1 diff --git a/examples/OneOff-Script.ps1 b/examples/OneOff-Script.ps1 index 8b80b7d7f..d6dc49898 100644 --- a/examples/OneOff-Script.ps1 +++ b/examples/OneOff-Script.ps1 @@ -38,7 +38,7 @@ catch { throw } # runs the logic once, then exits Start-PodeServer { - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Write-PodeHost 'hello, world!' } diff --git a/examples/OpenApi-SimplePotato.ps1 b/examples/OpenApi-SimplePotato.ps1 index 1083da276..d728a776b 100644 --- a/examples/OpenApi-SimplePotato.ps1 +++ b/examples/OpenApi-SimplePotato.ps1 @@ -43,8 +43,8 @@ catch { Start-PodeServer { # Enable terminal logging for requests and errors - New-PodeLogTerminalMethod | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # Define the endpoint for the server Add-PodeEndpoint -Address 127.0.0.1 -Port 8080 -Protocol Http diff --git a/examples/OpenApi-TuttiFrutti.ps1 b/examples/OpenApi-TuttiFrutti.ps1 index 644437ff4..861525474 100644 --- a/examples/OpenApi-TuttiFrutti.ps1 +++ b/examples/OpenApi-TuttiFrutti.ps1 @@ -70,7 +70,7 @@ catch { throw } Start-PodeServer -Threads 1 -Daemon:$Daemon -IgnoreServerConfig:$IgnoreServerConfig -ScriptBlock { Add-PodeEndpoint -Address localhost -Port $PortV3 -Protocol Http -Default -Name 'endpoint_v3' Add-PodeEndpoint -Address localhost -Port $PortV3_1 -Protocol Http -Name 'endpoint_v3.1' - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType $InfoDescription = @' This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about Swagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! diff --git a/examples/PetStore/Petstore-OpenApi.ps1 b/examples/PetStore/Petstore-OpenApi.ps1 index 2834bcf41..44c00fab8 100644 --- a/examples/PetStore/Petstore-OpenApi.ps1 +++ b/examples/PetStore/Petstore-OpenApi.ps1 @@ -115,7 +115,7 @@ Start-PodeServer -Threads 1 -ScriptBlock { Add-PodeFavicon -Default # Enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # Configure CORS Set-PodeSecurityAccessControl -Origin '*' -Duration 7200 -WithOptions -AuthorizationHeader -autoMethods -AutoHeader -Credentials -CrossDomainXhrRequests diff --git a/examples/PetStore/Petstore-OpenApiMultiTag.ps1 b/examples/PetStore/Petstore-OpenApiMultiTag.ps1 index c33ce8150..18b6f9a35 100644 --- a/examples/PetStore/Petstore-OpenApiMultiTag.ps1 +++ b/examples/PetStore/Petstore-OpenApiMultiTag.ps1 @@ -103,7 +103,7 @@ Start-PodeServer -Threads 1 -ScriptBlock { Add-PodeEndpoint -Address (Get-PodeConfig).Address -Port (Get-PodeConfig).RestFulPort -Protocol Http -Default -Name 'endpoint_v3' Add-PodeEndpoint -Address (Get-PodeConfig).Address -Port ((Get-PodeConfig).RestFulPort + 1) -Protocol Http -Name 'endpoint_v3.1' } - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType #Configure CORS Set-PodeSecurityAccessControl -Origin '*' -Duration 7200 -WithOptions -AuthorizationHeader -autoMethods -AutoHeader -Credentials -CrossDomainXhrRequests #-Header 'content-type' # -Header 'Accept','Content-Type' ,'Connection' #-Headers '*' 'x-requested-with' ,'crossdomain'# diff --git a/examples/Shared-State.ps1 b/examples/Shared-State.ps1 index deafbedad..0b592690a 100644 --- a/examples/Shared-State.ps1 +++ b/examples/Shared-State.ps1 @@ -42,8 +42,8 @@ catch { throw } Start-PodeServer { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # re-initialise the state Restore-PodeState -Path './state.json' diff --git a/examples/Suspend-Resume.ps1 b/examples/Suspend-Resume.ps1 index 76218b1d0..50acfd1a9 100644 --- a/examples/Suspend-Resume.ps1 +++ b/examples/Suspend-Resume.ps1 @@ -72,7 +72,7 @@ Start-PodeServer -Threads 4 -EnablePool Tasks -IgnoreServerConfig -ScriptBlock { Set-PodeViewEngine -Type Html # Enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # Enable OpenAPI documentation diff --git a/examples/SwaggerEditor/Swagger-Editor.ps1 b/examples/SwaggerEditor/Swagger-Editor.ps1 index 12affbba5..86df84e9d 100644 --- a/examples/SwaggerEditor/Swagger-Editor.ps1 +++ b/examples/SwaggerEditor/Swagger-Editor.ps1 @@ -46,8 +46,8 @@ Start-PodeServer -ConfigFile '..\Server.psd1' -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port $port -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set view engine to pode renderer Set-PodeViewEngine -Type HTML diff --git a/examples/Tasks.ps1 b/examples/Tasks.ps1 index ac29a7cff..ede77e4f6 100644 --- a/examples/Tasks.ps1 +++ b/examples/Tasks.ps1 @@ -42,7 +42,7 @@ catch { throw } Start-PodeServer { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeTask -Name 'Test1' -ScriptBlock { 'a string' diff --git a/examples/Tcp-Server.ps1 b/examples/Tcp-Server.ps1 index 43af5dfbf..c92deeba8 100644 --- a/examples/Tcp-Server.ps1 +++ b/examples/Tcp-Server.ps1 @@ -42,7 +42,7 @@ Start-PodeServer -Threads 2 { # Add-PodeEndpoint -Address localhost -Port 9000 -Protocol Tcps -SelfSigned -CRLFMessageEnd -TlsMode Implicit -Acknowledge 'Welcome!' # enable logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeVerb -Verb 'HELLO' -ScriptBlock { Write-PodeTcpClient -Message 'HI' diff --git a/examples/Tcp-ServerAuth.ps1 b/examples/Tcp-ServerAuth.ps1 index 0f8a54501..4808e06c2 100644 --- a/examples/Tcp-ServerAuth.ps1 +++ b/examples/Tcp-ServerAuth.ps1 @@ -40,7 +40,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Tcp -CRLFMessageEnd # enable logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # create a role access method get retrieves roles from a database New-PodeAccessScheme -Type Role | Add-PodeAccess -Name 'RoleExample' -ScriptBlock { diff --git a/examples/Tcp-ServerHttp.ps1 b/examples/Tcp-ServerHttp.ps1 index 2ab811225..3ab2d7e07 100644 --- a/examples/Tcp-ServerHttp.ps1 +++ b/examples/Tcp-ServerHttp.ps1 @@ -40,7 +40,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Tcp # enable logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # example limits # Add-PodeLimitAccessRule -Name 'Main' -Action Deny -Component @( diff --git a/examples/Tcp-ServerMultiEndpoint.ps1 b/examples/Tcp-ServerMultiEndpoint.ps1 index 7a2352edd..964d357ea 100644 --- a/examples/Tcp-ServerMultiEndpoint.ps1 +++ b/examples/Tcp-ServerMultiEndpoint.ps1 @@ -41,7 +41,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Hostname 'foo.pode.com' -Port 9000 -Protocol Tcp -Name 'EP2' -Acknowledge 'Hello there!' -CRLFMessageEnd # enable logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # hello verb for endpoint1 Add-PodeVerb -Verb 'HELLO :forename :surname' -EndpointName EP1 -ScriptBlock { diff --git a/examples/Threading.ps1 b/examples/Threading.ps1 index a54a98ef7..7e901337b 100644 --- a/examples/Threading.ps1 +++ b/examples/Threading.ps1 @@ -67,7 +67,7 @@ catch { throw } Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port $Port -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # custom locks New-PodeLockable -Name 'TestLock' diff --git a/examples/Web-AuthApiKey.ps1 b/examples/Web-AuthApiKey.ps1 index ccdd337c4..ad2fd1e70 100644 --- a/examples/Web-AuthApiKey.ps1 +++ b/examples/Web-AuthApiKey.ps1 @@ -56,8 +56,8 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogFileMethod -Name 'requests' | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogFileMethod -Name 'requests' | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup bearer auth New-PodeAuthScheme -ApiKey -Location $Location | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { diff --git a/examples/Web-AuthBasic.ps1 b/examples/Web-AuthBasic.ps1 index f37dd67fe..26f369254 100644 --- a/examples/Web-AuthBasic.ps1 +++ b/examples/Web-AuthBasic.ps1 @@ -55,7 +55,7 @@ Start-PodeServer -Threads 2 { # request logging $batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 - New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeRequestLogType + New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeLogRequestType # setup basic auth (base64> username:password in header) New-PodeAuthScheme -Basic -Realm 'Pode Example Page' | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { diff --git a/examples/Web-AuthBasicBearer.ps1 b/examples/Web-AuthBasicBearer.ps1 index d99bbc2ed..25c95181c 100644 --- a/examples/Web-AuthBasicBearer.ps1 +++ b/examples/Web-AuthBasicBearer.ps1 @@ -42,7 +42,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogFileMethod -Name 'requests' | Enable-PodeRequestLogType + New-PodeLogFileMethod -Name 'requests' | Enable-PodeLogRequestType # setup bearer auth New-PodeAuthScheme -Bearer -Scope write | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { diff --git a/examples/Web-AuthBasicHeader.ps1 b/examples/Web-AuthBasicHeader.ps1 index eb8fef0f5..101208dc4 100644 --- a/examples/Web-AuthBasicHeader.ps1 +++ b/examples/Web-AuthBasicHeader.ps1 @@ -57,7 +57,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http # enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup session details Enable-PodeSessionMiddleware -Duration 120 -Extend -UseHeaders -Strict diff --git a/examples/Web-AuthForm.ps1 b/examples/Web-AuthForm.ps1 index 1a5c624f2..76f77c0b9 100644 --- a/examples/Web-AuthForm.ps1 +++ b/examples/Web-AuthForm.ps1 @@ -57,7 +57,7 @@ Start-PodeServer -Threads 2 { Set-PodeViewEngine -Type Pode # enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup session details Enable-PodeSessionMiddleware -Duration 120 -Extend diff --git a/examples/Web-AuthFormAccess.ps1 b/examples/Web-AuthFormAccess.ps1 index 23053b221..74b9cf746 100644 --- a/examples/Web-AuthFormAccess.ps1 +++ b/examples/Web-AuthFormAccess.ps1 @@ -54,7 +54,7 @@ Start-PodeServer -Threads 2 { Set-PodeViewEngine -Type Pode # enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup session details Enable-PodeSessionMiddleware -Duration 120 -Extend diff --git a/examples/Web-AuthFormAd.ps1 b/examples/Web-AuthFormAd.ps1 index bd2bae2fe..0d709a28b 100644 --- a/examples/Web-AuthFormAd.ps1 +++ b/examples/Web-AuthFormAd.ps1 @@ -48,7 +48,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set the view engine Set-PodeViewEngine -Type Pode diff --git a/examples/Web-AuthFormAnon.ps1 b/examples/Web-AuthFormAnon.ps1 index e18ede6a9..2450cef02 100644 --- a/examples/Web-AuthFormAnon.ps1 +++ b/examples/Web-AuthFormAnon.ps1 @@ -58,7 +58,7 @@ Start-PodeServer -Threads 2 { Set-PodeViewEngine -Type Pode # enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup session details Enable-PodeSessionMiddleware -Duration 120 -Extend diff --git a/examples/Web-AuthFormCreds.ps1 b/examples/Web-AuthFormCreds.ps1 index b3308235d..a67459f87 100644 --- a/examples/Web-AuthFormCreds.ps1 +++ b/examples/Web-AuthFormCreds.ps1 @@ -58,7 +58,7 @@ Start-PodeServer -Threads 2 { Set-PodeViewEngine -Type Pode # enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup session details Enable-PodeSessionMiddleware -Duration 120 -Extend diff --git a/examples/Web-AuthFormFile.ps1 b/examples/Web-AuthFormFile.ps1 index f029548c1..bedd397e7 100644 --- a/examples/Web-AuthFormFile.ps1 +++ b/examples/Web-AuthFormFile.ps1 @@ -51,7 +51,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set the view engine Set-PodeViewEngine -Type Pode diff --git a/examples/Web-AuthFormLocal.ps1 b/examples/Web-AuthFormLocal.ps1 index 76696194f..80f066a9d 100644 --- a/examples/Web-AuthFormLocal.ps1 +++ b/examples/Web-AuthFormLocal.ps1 @@ -46,7 +46,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set the view engine Set-PodeViewEngine -Type Pode diff --git a/examples/Web-AuthFormMerged.ps1 b/examples/Web-AuthFormMerged.ps1 index 1a50293e5..96f2d4e1e 100644 --- a/examples/Web-AuthFormMerged.ps1 +++ b/examples/Web-AuthFormMerged.ps1 @@ -52,7 +52,7 @@ Start-PodeServer -Threads 2 { Set-PodeViewEngine -Type Pode # enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup session details Enable-PodeSessionMiddleware -Duration 120 -Extend diff --git a/examples/Web-AuthFormSessionAuth.ps1 b/examples/Web-AuthFormSessionAuth.ps1 index e3a772052..79780c50d 100644 --- a/examples/Web-AuthFormSessionAuth.ps1 +++ b/examples/Web-AuthFormSessionAuth.ps1 @@ -54,7 +54,7 @@ Start-PodeServer -Threads 2 { Set-PodeViewEngine -Type Pode # enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup session details Enable-PodeSessionMiddleware -Duration 120 -Extend diff --git a/examples/Web-AuthMerged.ps1 b/examples/Web-AuthMerged.ps1 index 0d912756f..5d8b55b60 100644 --- a/examples/Web-AuthMerged.ps1 +++ b/examples/Web-AuthMerged.ps1 @@ -46,11 +46,11 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # request logging $batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 - New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeRequestLogType + New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeLogRequestType # setup access New-PodeAccessScheme -Type Role | Add-PodeAccess -Name 'Rbac' diff --git a/examples/Web-AuthNegotiate.ps1 b/examples/Web-AuthNegotiate.ps1 index 8ad29b56e..e5000ef97 100644 --- a/examples/Web-AuthNegotiate.ps1 +++ b/examples/Web-AuthNegotiate.ps1 @@ -43,7 +43,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8080 -Host 'pode.example.com' -Protocol Http # enable error logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup negotiate auth New-PodeAuthScheme -Negotiate -KeytabPath '.\pode-user.keytab' | Add-PodeAuth -Name 'Login' -Sessionless -ScriptBlock { diff --git a/examples/Web-AuthOauth2.ps1 b/examples/Web-AuthOauth2.ps1 index b0c8f6111..1608edc3c 100644 --- a/examples/Web-AuthOauth2.ps1 +++ b/examples/Web-AuthOauth2.ps1 @@ -44,7 +44,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http -Default - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set the view engine Set-PodeViewEngine -Type Pode diff --git a/examples/Web-AuthOauth2Form.ps1 b/examples/Web-AuthOauth2Form.ps1 index 0fc1a81d3..cc010c62d 100644 --- a/examples/Web-AuthOauth2Form.ps1 +++ b/examples/Web-AuthOauth2Form.ps1 @@ -44,7 +44,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http -Default - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set the view engine Set-PodeViewEngine -Type Pode diff --git a/examples/Web-AuthOauth2Oidc.ps1 b/examples/Web-AuthOauth2Oidc.ps1 index 03a21a655..dac17dd08 100644 --- a/examples/Web-AuthOauth2Oidc.ps1 +++ b/examples/Web-AuthOauth2Oidc.ps1 @@ -44,7 +44,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http -Default - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set the view engine Set-PodeViewEngine -Type Pode diff --git a/examples/Web-FuncsToRoutes.ps1 b/examples/Web-FuncsToRoutes.ps1 index 6b95d66d4..4ce8a7fff 100644 --- a/examples/Web-FuncsToRoutes.ps1 +++ b/examples/Web-FuncsToRoutes.ps1 @@ -58,7 +58,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # make routes for functions - with every route requires authentication ConvertTo-PodeRoute -Commands @('Get-ChildItem', 'Get-Host', 'Invoke-Expression') -Authentication Validate -Verbose diff --git a/examples/Web-GzipRequest.ps1 b/examples/Web-GzipRequest.ps1 index 84bfc1bad..0a1b9a808 100644 --- a/examples/Web-GzipRequest.ps1 +++ b/examples/Web-GzipRequest.ps1 @@ -41,7 +41,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # GET request that receives gzip'd json Add-PodeRoute -Method Post -Path '/users' -ScriptBlock { diff --git a/examples/Web-Hostname.ps1 b/examples/Web-Hostname.ps1 index 35d13c1e7..056ef1bd3 100644 --- a/examples/Web-Hostname.ps1 +++ b/examples/Web-Hostname.ps1 @@ -60,7 +60,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Hostname pode4.foo.com -Port $Port -Protocol Http -LookupHostname # logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set view engine to pode renderer Set-PodeViewEngine -Type Pode diff --git a/examples/Web-Mcp.ps1 b/examples/Web-Mcp.ps1 index 8af986648..39edcf782 100644 --- a/examples/Web-Mcp.ps1 +++ b/examples/Web-Mcp.ps1 @@ -40,8 +40,8 @@ Start-PodeServer -Threads 2 { # request logging $batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 - New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # Create a simple default group for MCP tools Add-PodeMcpGroup -Name 'Default' -Description 'Default group for MCP tools' diff --git a/examples/Web-McpServices.ps1 b/examples/Web-McpServices.ps1 index 71bc1c8cf..6444a6e48 100644 --- a/examples/Web-McpServices.ps1 +++ b/examples/Web-McpServices.ps1 @@ -40,8 +40,8 @@ Start-PodeServer -Threads 2 { # request logging $batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 - New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # Create a simple default group for MCP tools Add-PodeMcpGroup -Name 'Default' -Description 'Default group for MCP tools' diff --git a/examples/Web-Pages.ps1 b/examples/Web-Pages.ps1 index bec97915e..33e414ba2 100644 --- a/examples/Web-Pages.ps1 +++ b/examples/Web-Pages.ps1 @@ -85,15 +85,15 @@ Start-PodeServer -Threads 2 -Verbose { # ) # wire up a custom logger - $customLogMethod = New-PodeLogCustomMethod -ScriptBlock { - param($item, $options, $raw) - $item.HttpMethod | Out-Default + $customLogMethod = New-PodeLogCustomMethod -Version 2 -ScriptBlock { + param($items, $options) + $items[0].Data.HttpMethod | Out-Default } - $customLogMethod | Add-PodeLogType -Name 'custom' -ScriptBlock { - param($item) + $customLogMethod | Add-PodeLogType -Name 'custom' -Version 2 -ScriptBlock { + param($logEvent) return @{ - HttpMethod = $item.HttpMethod + HttpMethod = $logEvent.Data.HttpMethod } } @@ -102,10 +102,10 @@ Start-PodeServer -Threads 2 -Verbose { # log requests to the terminal $batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 - New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeRequestLogType + New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeLogRequestType # log errors to the terminal - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set view engine to pode renderer Set-PodeViewEngine -Type Pode diff --git a/examples/Web-PagesHttps.ps1 b/examples/Web-PagesHttps.ps1 index 6ecb1bcfd..7c3ae3648 100644 --- a/examples/Web-PagesHttps.ps1 +++ b/examples/Web-PagesHttps.ps1 @@ -50,8 +50,8 @@ catch { throw } # create a server, flagged to generate a self-signed cert for dev/testing Start-PodeServer { - New-PodeLogTerminalMethod | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # bind to ip/port and set as https with self-signed cert switch ($CertType) { diff --git a/examples/Web-PagesSimple.ps1 b/examples/Web-PagesSimple.ps1 index cf96f48f8..7d81406a3 100644 --- a/examples/Web-PagesSimple.ps1 +++ b/examples/Web-PagesSimple.ps1 @@ -53,7 +53,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port $Port -Protocol Http -Name '8081Address' -RedirectTo '8090Address' # log errors to the terminal - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set view engine to pode renderer Set-PodeViewEngine -Type Pode diff --git a/examples/Web-PagesUsing.ps1 b/examples/Web-PagesUsing.ps1 index ae163dd4d..0fca87972 100644 --- a/examples/Web-PagesUsing.ps1 +++ b/examples/Web-PagesUsing.ps1 @@ -70,8 +70,8 @@ Start-PodeServer -Threads 2 { # log requests to the terminal $batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 - New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set view engine to pode renderer Set-PodeViewEngine -Type Pode diff --git a/examples/Web-RestApi.ps1 b/examples/Web-RestApi.ps1 index df8e4ca59..934e666e6 100644 --- a/examples/Web-RestApi.ps1 +++ b/examples/Web-RestApi.ps1 @@ -46,7 +46,7 @@ Start-PodeServer { # request logging $batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 - New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeRequestLogType + New-PodeLogTerminalMethod -Batch $batchInfo | Enable-PodeLogRequestType # can be hit by sending a GET request to "localhost:8086/api/test" Add-PodeRoute -Method Get -Path '/api/test' -ScriptBlock { diff --git a/examples/Web-RestOpenApi.ps1 b/examples/Web-RestOpenApi.ps1 index 0f66886a2..397fc690f 100644 --- a/examples/Web-RestOpenApi.ps1 +++ b/examples/Web-RestOpenApi.ps1 @@ -44,7 +44,7 @@ Start-PodeServer { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http -Name 'user' Add-PodeEndpoint -Address localhost -Port 8082 -Protocol Http -Name 'admin' - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Enable-PodeOpenApi -DisableMinimalDefinitions Add-PodeOAInfo -Title 'OpenAPI Example' diff --git a/examples/Web-RestOpenApiShared.ps1 b/examples/Web-RestOpenApiShared.ps1 index 9d9a025cb..d144f8f0f 100644 --- a/examples/Web-RestOpenApiShared.ps1 +++ b/examples/Web-RestOpenApiShared.ps1 @@ -44,7 +44,7 @@ Start-PodeServer { Add-PodeEndpoint -Address localhost -Port 8080 -Protocol Http -Name 'user' Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http -Name 'admin' - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Enable-PodeOpenApi -DisableMinimalDefinitions diff --git a/examples/Web-RouteGroup.ps1 b/examples/Web-RouteGroup.ps1 index 9cad49a68..6bd9cf256 100644 --- a/examples/Web-RouteGroup.ps1 +++ b/examples/Web-RouteGroup.ps1 @@ -47,7 +47,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType $mid1 = New-PodeMiddleware -ScriptBlock { 'here1' | Out-Default diff --git a/examples/Web-Secrets.ps1 b/examples/Web-Secrets.ps1 index 710f490dc..18a68ad57 100644 --- a/examples/Web-Secrets.ps1 +++ b/examples/Web-Secrets.ps1 @@ -62,7 +62,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http # logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # secret manage azure keyvault - need to run "Connect-AzAccount" first! diff --git a/examples/Web-SecretsLocal.ps1 b/examples/Web-SecretsLocal.ps1 index 4b4186293..b46a5a361 100644 --- a/examples/Web-SecretsLocal.ps1 +++ b/examples/Web-SecretsLocal.ps1 @@ -47,7 +47,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http # logging - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # secret manage local vault diff --git a/examples/Web-SelfSigned.ps1 b/examples/Web-SelfSigned.ps1 index 095a49801..8ff80641f 100644 --- a/examples/Web-SelfSigned.ps1 +++ b/examples/Web-SelfSigned.ps1 @@ -30,8 +30,8 @@ catch { throw } Start-PodeServer -Threads 6 { Add-PodeEndpoint -Address localhost -Port '8081' -Protocol 'Https' -SelfSigned - New-PodeLogFileMethod -Name 'requests' | Enable-PodeRequestLogType - New-PodeLogFileMethod -Name 'errors' | Enable-PodeErrorLogType + New-PodeLogFileMethod -Name 'requests' | Enable-PodeLogRequestType + New-PodeLogFileMethod -Name 'errors' | Enable-PodeLogErrorType Add-PodeRoute -Method Get -Path / -ScriptBlock { Write-PodeTextResponse -Value 'Test' diff --git a/examples/Web-Signal.ps1 b/examples/Web-Signal.ps1 index ad4a743b6..568937aff 100644 --- a/examples/Web-Signal.ps1 +++ b/examples/Web-Signal.ps1 @@ -51,7 +51,7 @@ Start-PodeServer -Threads 3 { # Add-PodeEndpoint -Address localhost -Port 8091 -Certificate './certs/pode-cert.pfx' -CertificatePassword '1234' -Protocol Wss # log errors to the terminal - New-PodeLogTerminalMethod | Enable-PodeErrorLogType -Levels Error + New-PodeLogTerminalMethod | Enable-PodeLogErrorType -Levels Error # register a connect event Register-PodeSignalEvent -Name '/msg' -Type Connect -EventName 'SignalConnected' -ScriptBlock { diff --git a/examples/Web-SignalAuthForm.ps1 b/examples/Web-SignalAuthForm.ps1 index 898f2c37b..d1bd71908 100644 --- a/examples/Web-SignalAuthForm.ps1 +++ b/examples/Web-SignalAuthForm.ps1 @@ -52,7 +52,7 @@ Start-PodeServer -Threads 3 { Add-PodeEndpoint -Address localhost -Port 8091 -Protocol Ws -NoAutoUpgradeWebSockets # log errors to the terminal - New-PodeLogTerminalMethod | Enable-PodeErrorLogType -Levels Error + New-PodeLogTerminalMethod | Enable-PodeLogErrorType -Levels Error # register a connect event Register-PodeSignalEvent -Name 'Msg', 'Local' -Type Connect -EventName 'SignalConnected' -ScriptBlock { diff --git a/examples/Web-SignalConnection.ps1 b/examples/Web-SignalConnection.ps1 index f00f60099..d49759b16 100644 --- a/examples/Web-SignalConnection.ps1 +++ b/examples/Web-SignalConnection.ps1 @@ -54,7 +54,7 @@ Start-PodeServer -EnablePool WebSockets { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http # log requests to the terminal - New-PodeLogTerminalMethod | Enable-PodeErrorLogType -Levels Error, Debug, Verbose + New-PodeLogTerminalMethod | Enable-PodeLogErrorType -Levels Error, Debug, Verbose # connect to web socket from web-signal.ps1 Connect-PodeWebSocket -Name 'Example' -Url 'ws://localhost:8091' -ScriptBlock { diff --git a/examples/Web-SignalManual.ps1 b/examples/Web-SignalManual.ps1 index ff906deea..5b7f19520 100644 --- a/examples/Web-SignalManual.ps1 +++ b/examples/Web-SignalManual.ps1 @@ -49,7 +49,7 @@ Start-PodeServer -Threads 3 { Add-PodeEndpoint -Address localhost -Port 8091 -Protocol Ws -NoAutoUpgradeWebSockets # log errors to the terminal - New-PodeLogTerminalMethod | Enable-PodeErrorLogType -Levels Error + New-PodeLogTerminalMethod | Enable-PodeLogErrorType -Levels Error # register a connect event Register-PodeSignalEvent -Name 'Msg' -Type Connect -EventName 'SignalConnected' -ScriptBlock { diff --git a/examples/Web-SimplePages.ps1 b/examples/Web-SimplePages.ps1 index 7b57cfc6e..b0ba79ae3 100644 --- a/examples/Web-SimplePages.ps1 +++ b/examples/Web-SimplePages.ps1 @@ -44,7 +44,7 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodePage -Name Processes -ScriptBlock { Get-Process } Add-PodePage -Name Services -ScriptBlock { Get-Service } diff --git a/examples/Web-Sockets.ps1 b/examples/Web-Sockets.ps1 index 2d031d0f9..77bbd0028 100644 --- a/examples/Web-Sockets.ps1 +++ b/examples/Web-Sockets.ps1 @@ -47,7 +47,7 @@ Start-PodeServer -Threads 5 { # Add-PodeEndpoint -Address localhost -Port 8081 -SelfSigned -Protocol Https # log requests to the terminal - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # GET request for web page on "localhost:8085/" Add-PodeRoute -Method Get -Path '/' -ScriptBlock { diff --git a/examples/Web-Sse.ps1 b/examples/Web-Sse.ps1 index 54aaf8281..53f399a09 100644 --- a/examples/Web-Sse.ps1 +++ b/examples/Web-Sse.ps1 @@ -44,7 +44,7 @@ Start-PodeServer -Threads 3 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http # log errors - New-PodeLogTerminalMethod | Enable-PodeErrorLogType -Levels Error + New-PodeLogTerminalMethod | Enable-PodeLogErrorType -Levels Error # register a connect event Register-PodeSseEvent -Name 'Test', 'Data' -Type Connect -EventName 'Connected' -ScriptBlock { diff --git a/examples/Web-Static.ps1 b/examples/Web-Static.ps1 index 7eaa91d6a..cdcbc4cf5 100644 --- a/examples/Web-Static.ps1 +++ b/examples/Web-Static.ps1 @@ -51,8 +51,8 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port $port -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # set view engine to pode renderer Set-PodeViewEngine -Type Pode diff --git a/examples/Web-StaticAuth.ps1 b/examples/Web-StaticAuth.ps1 index 81d253706..2024708ad 100644 --- a/examples/Web-StaticAuth.ps1 +++ b/examples/Web-StaticAuth.ps1 @@ -45,8 +45,8 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup basic auth (base64> username:password in header) New-PodeAuthScheme -Basic -Realm 'Pode Static Page' | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { diff --git a/examples/Web-TpEPS.ps1 b/examples/Web-TpEPS.ps1 index 948c49ac2..2816ab874 100644 --- a/examples/Web-TpEPS.ps1 +++ b/examples/Web-TpEPS.ps1 @@ -40,7 +40,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http # log requests to the terminal - New-PodeLogTerminalMethod | Enable-PodeRequestLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType # set view engine to EPS renderer Set-PodeViewEngine -Type EPS -ScriptBlock { diff --git a/examples/Web-TpPSHTML.ps1 b/examples/Web-TpPSHTML.ps1 index a6eea3935..ac66c24e2 100644 --- a/examples/Web-TpPSHTML.ps1 +++ b/examples/Web-TpPSHTML.ps1 @@ -40,7 +40,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http # log requests to the terminal - New-PodeLogTerminalMethod | Enable-PodeRequestLogType + New-PodeLogTerminalMethod | Enable-PodeLogRequestType # set view engine to PSHTML renderer Set-PodeViewEngine -Type PSHTML -Extension PS1 -ScriptBlock { diff --git a/examples/Web-Upload.ps1 b/examples/Web-Upload.ps1 index 422668db2..25431d564 100644 --- a/examples/Web-Upload.ps1 +++ b/examples/Web-Upload.ps1 @@ -55,7 +55,7 @@ Start-PodeServer -Threads 2 { Add-PodeEndpoint -Address localhost -Port $port -Protocol Http Set-PodeViewEngine -Type HTML - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # GET request for web page on "localhost:8081/" Add-PodeRoute -Method Get -Path '/' -ScriptBlock { diff --git a/examples/Web-UsePodeAuth.ps1 b/examples/Web-UsePodeAuth.ps1 index 8b0e07948..d7b9e6157 100644 --- a/examples/Web-UsePodeAuth.ps1 +++ b/examples/Web-UsePodeAuth.ps1 @@ -67,8 +67,8 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogFileMethod -Name 'requests' | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogFileMethod -Name 'requests' | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Use-PodeAuth diff --git a/examples/WebAuth-ApikeyJWT.ps1 b/examples/WebAuth-ApikeyJWT.ps1 index ece8e555e..7215ede81 100644 --- a/examples/WebAuth-ApikeyJWT.ps1 +++ b/examples/WebAuth-ApikeyJWT.ps1 @@ -66,8 +66,8 @@ Start-PodeServer -Threads 2 { # listen on localhost:8081 Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogFileMethod -Name 'requests' | Enable-PodeRequestLogType - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogFileMethod -Name 'requests' | Enable-PodeLogRequestType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # setup bearer auth New-PodeAuthScheme -ApiKey -Location $Location -AsJWT | Add-PodeAuth -Name 'Validate' -Sessionless -ScriptBlock { diff --git a/examples/WebHook.ps1 b/examples/WebHook.ps1 index 541508fde..2398eab60 100644 --- a/examples/WebHook.ps1 +++ b/examples/WebHook.ps1 @@ -61,7 +61,7 @@ Start-PodeServer { Set-PodeState -Name 'subscriptions' -Value @{} | Out-Null # Enable terminal logging for errors - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType # Enable OpenAPI documentation Enable-PodeOpenApi -Path '/docs/openapi/v3.1' -OpenApiVersion '3.1.0' -DisableMinimalDefinitions -NoDefaultResponses -EnableSchemaValidation diff --git a/examples/scripts/server.ps1 b/examples/scripts/server.ps1 index fdffa7eb8..8240a287e 100644 --- a/examples/scripts/server.ps1 +++ b/examples/scripts/server.ps1 @@ -1,6 +1,6 @@ { Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Set-PodeViewEngine -Type Pode Add-PodeTimer -Name 'Hi' -Interval 4 -ScriptBlock { diff --git a/packers/choco/pode_template.nuspec b/packers/choco/pode_template.nuspec index 2a9093c65..fbf76d724 100644 --- a/packers/choco/pode_template.nuspec +++ b/packers/choco/pode_template.nuspec @@ -1,61 +1,61 @@ - - - - - pode - Pode - $version$ - Badgerati - Badgerati - Pode is a Cross-Platform PowerShell framework for creating web servers to host REST APIs and Websites. - -Pode is a Cross-Platform framework for creating web servers to host REST APIs and Websites. Pode also has support for being used in Azure Functions and AWS Lambda. - -### Features - -* Cross-platform using PowerShell Core (with support for PS5) -* Docker support, including images for ARM/Raspberry Pi -* Azure Functions, AWS Lambda, and IIS support -* OpenAPI, Swagger, and ReDoc support -* Listen on a single or multiple IP address/hostnames -* Cross-platform support for HTTP, HTTPS, TCP and SMTP -* Cross-platform support for WebSockets, including secure WebSockets -* Host REST APIs, Web Pages, and Static Content (with caching) -* Support for custom error pages -* Request and Response compression using GZip/Deflate -* Multi-thread support for incoming requests -* Inbuilt template engine, with support for third-parties -* Async timers for short-running repeatable processes -* Async scheduled tasks using cron expressions for short/long-running processes -* Supports logging to CLI, Files, and custom logic for other services like LogStash -* Cross-state variable access across multiple runspaces -* Restart the server via file monitoring, or defined periods/times -* Ability to allow/deny requests from certain IP addresses and subnets -* Basic rate limiting for IP addresses and subnets -* Middleware and Sessions on web servers, with Flash message and CSRF support -* Authentication on requests, such as Basic, Windows and Azure AD -* Support for dynamically building Routes from Functions and Modules -* Generate/bind self-signed certificates -* Secret management support to load secrets from vaults -* Support for File Watchers -* (Windows) Open the hosted server as a desktop application - - - https://github.com/Badgerati/Pode - https://github.com/Badgerati/Pode/tree/master/packers - https://badgerati.github.io/Pode - https://github.com/Badgerati/Pode/issues - powershell web server rest api smtp unix cross-platform file-monitoring multithreaded schedule middleware authentication aws azure websockets openapi - Copyright 2017-2023 - https://github.com/Badgerati/Pode/blob/master/LICENSE.txt - false - https://raw.githubusercontent.com/Badgerati/Pode/master/images/icon.png - https://github.com/Badgerati/Pode/releases - - - - - - - + + + + + pode + Pode + $version$ + Badgerati + Badgerati + Pode is a Cross-Platform PowerShell framework for creating web servers to host REST APIs and Websites. + +Pode is a Cross-Platform framework for creating web servers to host REST APIs and Websites. Pode also has support for being used in Azure Functions and AWS Lambda. + +### Features + +* Cross-platform using PowerShell Core (with support for PS5) +* Docker support, including images for ARM/Raspberry Pi +* Azure Functions, AWS Lambda, and IIS support +* OpenAPI, Swagger, and ReDoc support +* Listen on a single or multiple IP address/hostnames +* Cross-platform support for HTTP, HTTPS, TCP and SMTP +* Cross-platform support for WebSockets, including secure WebSockets +* Host REST APIs, Web Pages, and Static Content (with caching) +* Support for custom error pages +* Request and Response compression using GZip/Deflate +* Multi-thread support for incoming requests +* Inbuilt template engine, with support for third-parties +* Async timers for short-running repeatable processes +* Async scheduled tasks using cron expressions for short/long-running processes +* Supports logging to CLI, Files, and custom logic for other services like LogStash +* Cross-state variable access across multiple runspaces +* Restart the server via file monitoring, or defined periods/times +* Ability to allow/deny requests from certain IP addresses and subnets +* Basic rate limiting for IP addresses and subnets +* Middleware and Sessions on web servers, with Flash message and CSRF support +* Authentication on requests, such as Basic, Windows and Azure AD +* Support for dynamically building Routes from Functions and Modules +* Generate/bind self-signed certificates +* Secret management support to load secrets from vaults +* Support for File Watchers +* (Windows) Open the hosted server as a desktop application + + + https://github.com/Badgerati/Pode + https://github.com/Badgerati/Pode/tree/master/packers + https://badgerati.github.io/Pode + https://github.com/Badgerati/Pode/issues + powershell web server rest api smtp unix cross-platform file-monitoring multithreaded schedule middleware authentication aws azure websockets openapi + Copyright 2017-2023 + https://github.com/Badgerati/Pode/blob/master/LICENSE.txt + false + https://raw.githubusercontent.com/Badgerati/Pode/master/images/icon.png + https://github.com/Badgerati/Pode/releases + + + + + + + \ No newline at end of file diff --git a/src/Listener/Transport/Clients/IPodeClient.cs b/src/Listener/Transport/Clients/IPodeClient.cs new file mode 100644 index 000000000..98d004994 --- /dev/null +++ b/src/Listener/Transport/Clients/IPodeClient.cs @@ -0,0 +1,15 @@ +using System; + +namespace Pode.Transport.Clients +{ + public interface IPodeClient : IDisposable + { + string Server { get; } + int Port { get; } + bool IsConnected { get; } + + void Connect(); + void Disconnect(); + void Send(byte[] data); + } +} \ No newline at end of file diff --git a/src/Listener/Transport/Clients/PodeClientFactory.cs b/src/Listener/Transport/Clients/PodeClientFactory.cs new file mode 100644 index 000000000..089de6b1a --- /dev/null +++ b/src/Listener/Transport/Clients/PodeClientFactory.cs @@ -0,0 +1,27 @@ +namespace Pode.Transport.Clients +{ + public static class PodeClientFactory + { + public static IPodeClient Create(PodeClientType type, string server, int port, bool skipCertificateValidation = false) + { + IPodeClient client = default; + + switch (type) + { + case PodeClientType.Udp: + client = new PodeUdpClient(server, port); + break; + + case PodeClientType.Tcp: + client = new PodeTcpClient(server, port); + break; + + case PodeClientType.Tls: + client = new PodeTlsClient(server, port, skipCertificateValidation); + break; + } + + return client; + } + } +} \ No newline at end of file diff --git a/src/Listener/Transport/Clients/PodeClientType.cs b/src/Listener/Transport/Clients/PodeClientType.cs new file mode 100644 index 000000000..b8653e99d --- /dev/null +++ b/src/Listener/Transport/Clients/PodeClientType.cs @@ -0,0 +1,9 @@ +namespace Pode.Transport.Clients +{ + public enum PodeClientType + { + Udp, + Tcp, + Tls + } +} \ No newline at end of file diff --git a/src/Listener/Transport/Clients/PodeTcpClient.cs b/src/Listener/Transport/Clients/PodeTcpClient.cs new file mode 100644 index 000000000..0a017eb06 --- /dev/null +++ b/src/Listener/Transport/Clients/PodeTcpClient.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Net.Sockets; + +namespace Pode.Transport.Clients +{ + public class PodeTcpClient : IPodeClient + { + public string Server { get; private set; } + public int Port { get; private set; } + public bool IsConnected { get; private set; } = false; + + private TcpClient Client; + protected Stream Stream; + + public PodeTcpClient(string server, int port) + { + Server = server; + Port = port; + Client = new TcpClient(); + } + + public void Connect() + { + if (IsConnected) + { + return; + } + + Client.Connect(Server, Port); + SetStream(); + IsConnected = true; + } + + protected virtual void SetStream() + { + Stream = Client.GetStream(); + } + + public void Disconnect() + { + IsConnected = false; + Client?.Close(); + Stream?.Close(); + Stream?.Dispose(); + Stream = null; + } + + public void Send(byte[] data) + { + Stream?.Write(data, 0, data.Length); + Stream?.Flush(); + } + + public void Dispose() + { + Disconnect(); + Client?.Dispose(); + Client = null; + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Listener/Transport/Clients/PodeTlsClient.cs b/src/Listener/Transport/Clients/PodeTlsClient.cs new file mode 100644 index 000000000..8eb6628a3 --- /dev/null +++ b/src/Listener/Transport/Clients/PodeTlsClient.cs @@ -0,0 +1,31 @@ +using System.Net.Security; +using System.Security.Authentication; + +namespace Pode.Transport.Clients +{ + public class PodeTlsClient : PodeTcpClient + { + private readonly bool SkipCertificateValidation; + + public PodeTlsClient(string server, int port, bool skipCertificateValidation = false) + : base(server, port) + { + SkipCertificateValidation = skipCertificateValidation; + } + + protected override void SetStream() + { + base.SetStream(); + + Stream = SkipCertificateValidation + ? new SslStream(Stream, false, (sender, certificate, chain, sslPolicyErrors) => true) + : new SslStream(Stream, false); + +#if NETCOREAPP2_1_OR_GREATER + ((SslStream)Stream).AuthenticateAsClient(Server, null, SslProtocols.Tls12 | SslProtocols.Tls13, false); +#else + ((SslStream)Stream).AuthenticateAsClient(Server, null, SslProtocols.Tls12, false); +#endif + } + } +} \ No newline at end of file diff --git a/src/Listener/Transport/Clients/PodeUdpClient.cs b/src/Listener/Transport/Clients/PodeUdpClient.cs new file mode 100644 index 000000000..983a91371 --- /dev/null +++ b/src/Listener/Transport/Clients/PodeUdpClient.cs @@ -0,0 +1,51 @@ +using System; +using System.Net.Sockets; + +namespace Pode.Transport.Clients +{ + public class PodeUdpClient : IPodeClient + { + public string Server { get; private set; } + public int Port { get; private set; } + public bool IsConnected { get; private set; } = false; + + private UdpClient Client; + + public PodeUdpClient(string server, int port) + { + Server = server; + Port = port; + Client = new UdpClient(); + } + + public void Connect() + { + if (IsConnected) + { + return; + } + + Client.Connect(Server, Port); + IsConnected = true; + } + + public void Disconnect() + { + IsConnected = false; + Client?.Close(); + } + + public void Send(byte[] data) + { + Client?.Send(data, data.Length); + } + + public void Dispose() + { + Disconnect(); + Client?.Dispose(); + Client = null; + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/IPodeLogEvent.cs b/src/Listener/Utilities/Logging/IPodeLogEvent.cs index 8cab722df..23c9370fe 100644 --- a/src/Listener/Utilities/Logging/IPodeLogEvent.cs +++ b/src/Listener/Utilities/Logging/IPodeLogEvent.cs @@ -1,9 +1,14 @@ +using System; +using System.Collections; + namespace Pode.Utilities.Logging { public interface IPodeLogEvent { string Name { get; } PodeLogLevel Level { get; } - object Item { get; } + DateTime Timestamp { get; } + Hashtable Metadata { get; } + object Data { get; } } } \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/IPodeLogItem.cs b/src/Listener/Utilities/Logging/IPodeLogItem.cs index ca9fde8db..a0d2e9774 100644 --- a/src/Listener/Utilities/Logging/IPodeLogItem.cs +++ b/src/Listener/Utilities/Logging/IPodeLogItem.cs @@ -2,7 +2,9 @@ namespace Pode.Utilities.Logging { public interface IPodeLogItem { - object Items { get; set; } - object RawItems { get; set; } + object Data { get; } + IPodeLogEvent Event { get; } + + string ToString(); } } \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/IPodeLogItemCollection.cs b/src/Listener/Utilities/Logging/IPodeLogItemCollection.cs new file mode 100644 index 000000000..3dbfbb56f --- /dev/null +++ b/src/Listener/Utilities/Logging/IPodeLogItemCollection.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace Pode.Utilities.Logging +{ + public interface IPodeLogItemCollection : IDisposable + { + IList Items { get; } + DateTime Timestamp { get; } + int Count { get; } + int MaxCount { get; } + int Timeout { get; } + bool HasTimedOut { get; } + bool IsFull { get; } + + void Add(IPodeLogItem item); + string ToString(); + } +} \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/IPodeLogger.cs b/src/Listener/Utilities/Logging/IPodeLogger.cs index d5e0d1019..622e801cf 100644 --- a/src/Listener/Utilities/Logging/IPodeLogger.cs +++ b/src/Listener/Utilities/Logging/IPodeLogger.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Threading; namespace Pode.Utilities.Logging @@ -14,11 +15,11 @@ public interface IPodeLogger : IDisposable void RegisterType(IPodeLogType logType); void UnregisterType(string name); - void Add(string name, PodeLogLevel level, object item); + void Add(IPodeLogEvent logEvent); - void AddException(Exception exception, string contextId, PodeLogLevel level, int threadId = 0); - void AddException(string message, string contextId, PodeLogLevel level, int threadId = 0); - void AddException(string category, string message, string stackTrace, string contextId, PodeLogLevel level, int threadId = 0); + void AddException(Exception exception, string contextId, PodeLogLevel level, Hashtable metadata = null, int threadId = 0); + void AddException(string message, string contextId, PodeLogLevel level, Hashtable metadata = null, int threadId = 0); + void AddException(string category, string message, string stackTrace, string contextId, PodeLogLevel level, Hashtable metadata = null, int threadId = 0); bool TryTake(out IPodeLogEvent logEvent, CancellationToken cancellationToken); void Reset(); } diff --git a/src/Listener/Utilities/Logging/PodeLogEvent.cs b/src/Listener/Utilities/Logging/PodeLogEvent.cs index 9519459bc..9ef1ee2c3 100644 --- a/src/Listener/Utilities/Logging/PodeLogEvent.cs +++ b/src/Listener/Utilities/Logging/PodeLogEvent.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; namespace Pode.Utilities.Logging { @@ -6,9 +7,11 @@ public class PodeLogEvent : IPodeLogEvent { public string Name { get; private set; } public PodeLogLevel Level { get; private set; } - public object Item { get; private set; } + public DateTime Timestamp { get; private set; } = DateTime.Now; + public Hashtable Metadata { get; private set; } + public object Data { get; private set; } - public PodeLogEvent(string name, PodeLogLevel level, object item) + public PodeLogEvent(string name, PodeLogLevel level, object data, Hashtable metadata = null) { if (string.IsNullOrEmpty(name)) { @@ -17,7 +20,8 @@ public PodeLogEvent(string name, PodeLogLevel level, object item) Name = name; Level = level; - Item = item; + Data = data; + Metadata = metadata ?? new Hashtable(StringComparer.InvariantCultureIgnoreCase); } } } \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/PodeLogFormat.cs b/src/Listener/Utilities/Logging/PodeLogFormat.cs new file mode 100644 index 000000000..7ba76f5e4 --- /dev/null +++ b/src/Listener/Utilities/Logging/PodeLogFormat.cs @@ -0,0 +1,9 @@ +namespace Pode.Utilities.Logging +{ + public enum PodeLogFormat + { + None, + Custom, + Syslog + } +} \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/PodeLogItem.cs b/src/Listener/Utilities/Logging/PodeLogItem.cs index c78274eb7..86a9e202e 100644 --- a/src/Listener/Utilities/Logging/PodeLogItem.cs +++ b/src/Listener/Utilities/Logging/PodeLogItem.cs @@ -2,13 +2,18 @@ namespace Pode.Utilities.Logging { public class PodeLogItem : IPodeLogItem { - public object Items { get; set; } - public object RawItems { get; set; } + public object Data { get; private set; } + public IPodeLogEvent Event { get; private set; } - public PodeLogItem(object items, object rawItems) + public PodeLogItem(object data, IPodeLogEvent logEvent) { - Items = items; - RawItems = rawItems; + Data = data; + Event = logEvent; + } + + public override string ToString() + { + return Data?.ToString() ?? string.Empty; } } } \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/PodeLogItemCollection.cs b/src/Listener/Utilities/Logging/PodeLogItemCollection.cs new file mode 100644 index 000000000..87a2e1885 --- /dev/null +++ b/src/Listener/Utilities/Logging/PodeLogItemCollection.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +namespace Pode.Utilities.Logging +{ + public class PodeLogItemCollection : IPodeLogItemCollection + { + public IList Items { get; private set; } + public DateTime Timestamp { get; private set; } + public int Count => Items.Count; + public int MaxCount { get; private set; } = 1; + public int Timeout { get; private set; } = 0; + + public bool HasTimedOut + { + get + { + if (Count <= 0 || Timeout <= 0) + { + return false; + } + + return Timestamp.AddSeconds(Timeout) <= DateTime.UtcNow; + } + } + + public bool IsFull + { + get + { + if (Count <= 0 || MaxCount <= 0) + { + return false; + } + + return Count >= MaxCount; + } + } + + public PodeLogItemCollection() : this(1, 0) { } + + public PodeLogItemCollection(int maxCount, int timeout) + { + Items = new List(); + MaxCount = maxCount; + Timeout = timeout; + Timestamp = DateTime.UtcNow; + } + + public void Add(IPodeLogItem item) + { + Items.Add(item); + Timestamp = DateTime.UtcNow; + } + + public override string ToString() + { + return string.Join(Environment.NewLine, Items); + } + + public void Dispose() + { + Items.Clear(); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/PodeLogLevel.cs b/src/Listener/Utilities/Logging/PodeLogLevel.cs index ca319c10a..15067bafb 100644 --- a/src/Listener/Utilities/Logging/PodeLogLevel.cs +++ b/src/Listener/Utilities/Logging/PodeLogLevel.cs @@ -2,8 +2,12 @@ namespace Pode.Utilities.Logging { public enum PodeLogLevel { + Emergency, + Alert, + Critical, Error, Warning, + Notice, Informational, Debug, Verbose diff --git a/src/Listener/Utilities/Logging/PodeLogSerialiseFormat.cs b/src/Listener/Utilities/Logging/PodeLogSerialiseFormat.cs new file mode 100644 index 000000000..72c022c30 --- /dev/null +++ b/src/Listener/Utilities/Logging/PodeLogSerialiseFormat.cs @@ -0,0 +1,11 @@ +namespace Pode.Utilities.Logging +{ + public enum PodeSerialiseFormat + { + None, + Custom, + Json, + Xml, + Yaml + } +} \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/PodeLogSyslogFormat.cs b/src/Listener/Utilities/Logging/PodeLogSyslogFormat.cs new file mode 100644 index 000000000..a0f8b8c8b --- /dev/null +++ b/src/Listener/Utilities/Logging/PodeLogSyslogFormat.cs @@ -0,0 +1,8 @@ +namespace Pode.Utilities.Logging +{ + public enum PodeSyslogFormat + { + RFC3164, + RFC5424 + } +} \ No newline at end of file diff --git a/src/Listener/Utilities/Logging/PodeLogType.cs b/src/Listener/Utilities/Logging/PodeLogType.cs index c0dc72b96..a393260f1 100644 --- a/src/Listener/Utilities/Logging/PodeLogType.cs +++ b/src/Listener/Utilities/Logging/PodeLogType.cs @@ -12,7 +12,7 @@ public PodeLogType(string name, HashSet levels) { if (string.IsNullOrEmpty(name)) { - throw new ArgumentException("Log type name cannot be null or empty.", nameof(name)); + throw new ArgumentException("Log Type name cannot be null or empty.", nameof(name)); } Name = name; diff --git a/src/Listener/Utilities/Logging/PodeLogger.cs b/src/Listener/Utilities/Logging/PodeLogger.cs index 6511eeff3..49ad2589a 100644 --- a/src/Listener/Utilities/Logging/PodeLogger.cs +++ b/src/Listener/Utilities/Logging/PodeLogger.cs @@ -53,16 +53,6 @@ public void UnregisterType(string name) LogTypes.TryRemove(name, out _); } - public void Add(string name, PodeLogLevel level, object item) - { - if (IsDisposed || !IsEnabled) - { - return; - } - - Add(new PodeLogEvent(name, level, item)); - } - public void Add(IPodeLogEvent logEvent) { if (IsDisposed || !IsEnabled) @@ -70,13 +60,13 @@ public void Add(IPodeLogEvent logEvent) return; } - // does the log type exist? + // does the Log Type exist? if (!LogTypes.TryGetValue(logEvent.Name, out var logType)) { return; } - // is the log level enabled for the log type? + // is the log level enabled for the Log Type? if (!logType.IsLevelEnabled(logEvent.Level)) { return; @@ -86,35 +76,35 @@ public void Add(IPodeLogEvent logEvent) Queue.Add(logEvent); } - public void AddException(Exception exception, string contextId, PodeLogLevel level, int threadId = 0) + public void AddException(Exception exception, string contextId, PodeLogLevel level, Hashtable metadata = null, int threadId = 0) { if (exception == null) { return; } - AddException(exception.Source, exception.Message, exception.StackTrace, contextId, level, threadId); + AddException(exception.Source, exception.Message, exception.StackTrace, contextId, level, metadata, threadId); } - public void AddException(string message, string contextId, PodeLogLevel level, int threadId = 0) + public void AddException(string message, string contextId, PodeLogLevel level, Hashtable metadata = null, int threadId = 0) { - AddException(string.Empty, message, string.Empty, contextId, level, threadId); + AddException(string.Empty, message, string.Empty, contextId, level, metadata, threadId); } - public void AddException(string category, string message, string stackTrace, string contextId, PodeLogLevel level, int threadId = 0) + public void AddException(string category, string message, string stackTrace, string contextId, PodeLogLevel level, Hashtable metadata = null, int threadId = 0) { if (IsDisposed || !IsEnabled) { return; } - // does the log type exist? + // does the Log Type exist? if (!LogTypes.TryGetValue(ERROR_LOG_TYPE_NAME, out var logType)) { return; } - // is the log level enabled for the log type? + // is the log level enabled for the Log Type? if (!logType.IsLevelEnabled(level)) { return; @@ -134,6 +124,11 @@ public void AddException(string category, string message, string stackTrace, str } } + // default "" values where not set + stackTrace = string.IsNullOrWhiteSpace(stackTrace) ? "" : stackTrace; + message = string.IsNullOrWhiteSpace(message) ? "" : message; + contextId = string.IsNullOrWhiteSpace(contextId) ? "" : contextId; + // convert the exception to a log item var item = new Hashtable(StringComparer.InvariantCultureIgnoreCase) { @@ -148,7 +143,7 @@ public void AddException(string category, string message, string stackTrace, str }; // add the log event to the queue - Queue.Add(new PodeLogEvent(ERROR_LOG_TYPE_NAME, level, item)); + Queue.Add(new PodeLogEvent(ERROR_LOG_TYPE_NAME, level, item, metadata)); } public bool TryTake(out IPodeLogEvent logEvent, CancellationToken cancellationToken) @@ -178,7 +173,7 @@ public void Reset() return; } - // clear log types + // clear Log Types LogTypes.Clear(); } @@ -194,7 +189,7 @@ public void Dispose() // dispose the queue Queue.Dispose(); - // clear the log types + // clear the Log Types LogTypes.Clear(); // suppress finalization diff --git a/src/Listener/Utilities/PodeHelpers.cs b/src/Listener/Utilities/PodeHelpers.cs index 7e0634dd0..fd43cf311 100644 --- a/src/Listener/Utilities/PodeHelpers.cs +++ b/src/Listener/Utilities/PodeHelpers.cs @@ -282,22 +282,43 @@ public static string ReadStreamToEnd(Stream stream, Encoding encoding = default) // decompress bytes into either a gzip or deflate stream, and return the string public static string DecompressBytes(byte[] bytes, PodeCompressionType type, Encoding encoding = default) { - var stream = CompressStream(new MemoryStream(bytes), type, CompressionMode.Decompress); - return ReadStreamToEnd(stream, encoding); + using (var stream = CompressStream(new MemoryStream(bytes), type, CompressionMode.Decompress)) + { + return ReadStreamToEnd(stream, encoding); + } } // compress bytes into either a gzip or deflate stream, and return the bytes - public static byte[] CompressBytes(byte[] bytes, PodeCompressionType type) + public static byte[] CompressString(string value, PodeCompressionType type, Encoding encoding = default) { - var ms = new MemoryStream(); + // return empty bytes if no value + if (string.IsNullOrEmpty(value)) + { + return Array.Empty(); + } - using (var stream = CompressStream(ms, type, CompressionMode.Compress)) + // set the encoding if not provided + if (encoding == default) { - stream.Write(bytes, 0, bytes.Length); + encoding = Encoding; } - ms.Position = 0; - return ms.ToArray(); + var bytes = encoding.GetBytes(value); + return CompressBytes(bytes, type); + } + + public static byte[] CompressBytes(byte[] bytes, PodeCompressionType type) + { + using (var ms = new MemoryStream()) + { + using (var stream = CompressStream(ms, type, CompressionMode.Compress)) + { + stream.Write(bytes, 0, bytes.Length); + } + + ms.Position = 0; + return ms.ToArray(); + } } // compress stream into either a gzip or deflate stream diff --git a/src/Locales/ar/Pode.psd1 b/src/Locales/ar/Pode.psd1 index f69c8225f..266b5b24c 100644 --- a/src/Locales/ar/Pode.psd1 +++ b/src/Locales/ar/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "لا يمكن أن تكون القيم الافتراضية للمسار الثابت مسارات جذرية. القيمة الافتراضية غير صالحة: '{0}'" deprecatedFunctionWarningMessage = "تحذير: الدالة '{0}' لم تعد مدعومة وسيتم إزالتها في الإصدارات المستقبلية. يرجى استخدام الدالة '{1}' بدلاً منها." loggingMethodDoesNotExistExceptionMessage = "طريقة التسجيل '{0}' غير موجودة." -} + loggingApiMethodBodyNotStringExceptionMessage = 'النص الذي تم إرجاعه من كتلة نص API للتسجيل ليس سلسلة.' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'الرؤوس التي تم إرجاعها من كتلة رؤوس API للتسجيل ليست جدول تجزئة.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'مطلوب ScriptBlock غير فارغ لتنسيق التسلسل المخصص للتسجيل.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'مطلوب ScriptBlock غير فارغ لتنسيق السجل المخصص.' +} \ No newline at end of file diff --git a/src/Locales/de/Pode.psd1 b/src/Locales/de/Pode.psd1 index dc293f1a1..c17cbb481 100644 --- a/src/Locales/de/Pode.psd1 +++ b/src/Locales/de/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "Die Standardwerte für statische Routen können keine gerooteten Pfade sein. Ungültiger Standardwert: '{0}'" deprecatedFunctionWarningMessage = "WARNUNG: Die Funktion '{0}' ist veraltet und wird in zukünftigen Versionen entfernt. Bitte verwenden Sie stattdessen die Funktion '{1}'." loggingMethodDoesNotExistExceptionMessage = "Die Protokollierungsmethode '{0}' existiert nicht." + loggingApiMethodBodyNotStringExceptionMessage = 'Der von dem Logging-API-Body-Scriptblock zurückgegebene Body ist kein String.' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'Die von dem Logging-API-Headers-Scriptblock zurückgegebenen Header sind keine Hashtable.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'Ein nicht-leerer ScriptBlock ist für das benutzerdefinierte Logging-Serialisierungsformat erforderlich.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'Ein nicht-leerer ScriptBlock ist für das benutzerdefinierte Logging-Format erforderlich.' } \ No newline at end of file diff --git a/src/Locales/en-us/Pode.psd1 b/src/Locales/en-us/Pode.psd1 index 559e75b64..7ff16ca4b 100644 --- a/src/Locales/en-us/Pode.psd1 +++ b/src/Locales/en-us/Pode.psd1 @@ -37,7 +37,7 @@ cronExpressionInvalidExceptionMessage = 'Cron expression should only consist of 5 parts: {0}' noSessionToSetOnResponseExceptionMessage = 'There is no session available to set on the response.' valueOutOfRangeExceptionMessage = "Value '{0}' for {1} is invalid, should be between {2} and {3}" - loggingMethodAlreadyDefinedExceptionMessage = 'Logging method already defined: {0}' + loggingMethodAlreadyDefinedExceptionMessage = 'Log Method already defined: {0}' noSecretForHmac256ExceptionMessage = 'No secret supplied for HMAC256 hash.' eolPowerShellWarningMessage = '[WARNING] Pode {0} has not been tested on PowerShell {1}, as it is EOL.' runspacePoolFailedToLoadExceptionMessage = '{0} RunspacePool failed to load.' @@ -48,7 +48,7 @@ taskDoesNotExistExceptionMessage = "Task '{0}' does not exist." scopedVariableNotFoundExceptionMessage = 'Scoped Variable not found: {0}' sessionsRequiredForCsrfExceptionMessage = 'Sessions are required to use CSRF unless you want to use cookies.' - nonEmptyScriptBlockRequiredForLoggingMethodExceptionMessage = 'A non-empty ScriptBlock is required for the logging method.' + nonEmptyScriptBlockRequiredForLoggingMethodExceptionMessage = 'A non-empty ScriptBlock is required for the Log Method.' credentialsPassedWildcardForHeadersLiteralExceptionMessage = 'When Credentials is passed, The * wildcard for Headers will be taken as a literal string and not a wildcard.' podeNotInitializedExceptionMessage = 'Pode has not been initialized.' multipleEndpointsForGuiMessage = 'Multiple endpoints defined, only the first will be used for the GUI.' @@ -225,7 +225,7 @@ validationOfOneOfSchemaNotSupportedExceptionMessage = "Validation of a schema that includes 'oneof' is not supported." routeParameterCannotBeNullExceptionMessage = "The parameter 'Route' cannot be null." cacheStorageAlreadyExistsExceptionMessage = "Cache Storage with name '{0}' already exists." - loggingMethodRequiresValidScriptBlockExceptionMessage = "The supplied output Method for the '{0}' Logging method requires a valid ScriptBlock." + loggingMethodRequiresValidScriptBlockExceptionMessage = "The supplied output Method for the '{0}' Log Method requires a valid ScriptBlock." scopedVariableAlreadyDefinedExceptionMessage = 'Scoped Variable already defined: {0}' oauth2RequiresAuthorizeUrlExceptionMessage = "OAuth2 requires an 'AuthoriseUrl' property to be supplied." pathNotExistExceptionMessage = 'Path does not exist: {0}' @@ -348,5 +348,9 @@ mcpToolGroupAlreadyExistsExceptionMessage = "The MCP tool group '{0}' already exists." staticRouteDefaultCannotBeRootedExceptionMessage = "Static route defaults cannot be rooted paths. Invalid default: '{0}'" deprecatedFunctionWarningMessage = "WARNING: The function '{0}' is deprecated and will be removed in future releases. Please use the '{1}' function instead." - loggingMethodDoesNotExistExceptionMessage = "The logging method '{0}' does not exist." + loggingMethodDoesNotExistExceptionMessage = "The Log Method '{0}' does not exist." + loggingApiMethodBodyNotStringExceptionMessage = 'The body returned from the logging API body scriptblock is not a string.' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'The headers returned from the logging API headers scriptblock is not a hashtable.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'A non-empty ScriptBlock is required for the Custom logging serialization format.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'A non-empty ScriptBlock is required for the Custom log format.' } \ No newline at end of file diff --git a/src/Locales/en/Pode.psd1 b/src/Locales/en/Pode.psd1 index 0c9543524..9a86099a1 100644 --- a/src/Locales/en/Pode.psd1 +++ b/src/Locales/en/Pode.psd1 @@ -38,7 +38,7 @@ cronExpressionInvalidExceptionMessage = 'Cron expression should only consist of 5 parts: {0}' noSessionToSetOnResponseExceptionMessage = 'There is no session available to set on the response.' valueOutOfRangeExceptionMessage = "Value '{0}' for {1} is invalid, should be between {2} and {3}" - loggingMethodAlreadyDefinedExceptionMessage = 'Logging method already defined: {0}' + loggingMethodAlreadyDefinedExceptionMessage = 'Log Method already defined: {0}' noSecretForHmac256ExceptionMessage = 'No secret supplied for HMAC256 hash.' eolPowerShellWarningMessage = '[WARNING] Pode {0} has not been tested on PowerShell {1}, as it is EOL.' runspacePoolFailedToLoadExceptionMessage = '{0} RunspacePool failed to load.' @@ -49,7 +49,7 @@ taskDoesNotExistExceptionMessage = "Task '{0}' does not exist." scopedVariableNotFoundExceptionMessage = 'Scoped Variable not found: {0}' sessionsRequiredForCsrfExceptionMessage = 'Sessions are required to use CSRF unless you want to use cookies.' - nonEmptyScriptBlockRequiredForLoggingMethodExceptionMessage = 'A non-empty ScriptBlock is required for the logging method.' + nonEmptyScriptBlockRequiredForLoggingMethodExceptionMessage = 'A non-empty ScriptBlock is required for the Log Method.' credentialsPassedWildcardForHeadersLiteralExceptionMessage = 'When Credentials is passed, The * wildcard for Headers will be taken as a literal string and not a wildcard.' podeNotInitializedExceptionMessage = 'Pode has not been initialised.' multipleEndpointsForGuiMessage = 'Multiple endpoints defined, only the first will be used for the GUI.' @@ -225,7 +225,7 @@ validationOfOneOfSchemaNotSupportedExceptionMessage = "Validation of a schema that includes 'oneof' is not supported." routeParameterCannotBeNullExceptionMessage = "The parameter 'Route' cannot be null." cacheStorageAlreadyExistsExceptionMessage = "Cache Storage with name '{0}' already exists." - loggingMethodRequiresValidScriptBlockExceptionMessage = "The supplied output Method for the '{0}' Logging method requires a valid ScriptBlock." + loggingMethodRequiresValidScriptBlockExceptionMessage = "The supplied output Method for the '{0}' Log Method requires a valid ScriptBlock." scopedVariableAlreadyDefinedExceptionMessage = 'Scoped Variable already defined: {0}' oauth2RequiresAuthorizeUrlExceptionMessage = "OAuth2 requires an 'AuthoriseUrl' property to be supplied." pathNotExistExceptionMessage = 'Path does not exist: {0}' @@ -348,5 +348,9 @@ mcpToolGroupAlreadyExistsExceptionMessage = "The MCP tool group '{0}' already exists." staticRouteDefaultCannotBeRootedExceptionMessage = "Static route defaults cannot be rooted paths. Invalid default: '{0}'" deprecatedFunctionWarningMessage = "WARNING: The function '{0}' is deprecated and will be removed in future releases. Please use the '{1}' function instead." - loggingMethodDoesNotExistExceptionMessage = "The logging method '{0}' does not exist." + loggingMethodDoesNotExistExceptionMessage = "The Log Method '{0}' does not exist." + loggingApiMethodBodyNotStringExceptionMessage = 'The body returned from the logging API body scriptblock is not a string.' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'The headers returned from the logging API headers scriptblock is not a hashtable.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'A non-empty ScriptBlock is required for the Custom logging serialisation format.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'A non-empty ScriptBlock is required for the Custom log format.' } \ No newline at end of file diff --git a/src/Locales/es/Pode.psd1 b/src/Locales/es/Pode.psd1 index 8cb377869..edce84031 100644 --- a/src/Locales/es/Pode.psd1 +++ b/src/Locales/es/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "Los valores predeterminados de las rutas estáticas no pueden ser rutas absolutas. Valor predeterminado no válido: '{0}'" deprecatedFunctionWarningMessage = "ADVERTENCIA: La función '{0}' está obsoleta y se eliminará en futuras versiones. Por favor, use la función '{1}' en su lugar." loggingMethodDoesNotExistExceptionMessage = "El método de registro '{0}' no existe." + loggingApiMethodBodyNotStringExceptionMessage = 'El cuerpo devuelto por el scriptblock del cuerpo de la API de registro no es una cadena.' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'Los encabezados devueltos por el scriptblock de encabezados de la API de registro no son una tabla hash.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'Se requiere un ScriptBlock no vacío para el formato de serialización de registro personalizado.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'Se requiere un ScriptBlock no vacío para el formato de registro personalizado.' } \ No newline at end of file diff --git a/src/Locales/fr/Pode.psd1 b/src/Locales/fr/Pode.psd1 index 4da917daa..078c729bf 100644 --- a/src/Locales/fr/Pode.psd1 +++ b/src/Locales/fr/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "Les valeurs par défaut des routes statiques ne peuvent pas être des chemins absolus. Valeur par défaut non valide : '{0}'" deprecatedFunctionWarningMessage = "AVERTISSEMENT : La fonction '{0}' est obsolète et sera supprimée dans les futures versions. Veuillez utiliser la fonction '{1}' à la place." loggingMethodDoesNotExistExceptionMessage = "La méthode de journalisation '{0}' n'existe pas." + loggingApiMethodBodyNotStringExceptionMessage = "Le corps renvoyé par le scriptblock du corps de l'API de journalisation n'est pas une chaîne." + loggingApiMethodHeadersNotHashtableExceptionMessage = "Les en-têtes renvoyés par le scriptblock des en-têtes de l'API de journalisation ne sont pas une table de hachage." + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'Un ScriptBlock non vide est requis pour le format de sérialisation de journalisation personnalisé.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'Un ScriptBlock non vide est requis pour le format de journalisation personnalisé.' } \ No newline at end of file diff --git a/src/Locales/it/Pode.psd1 b/src/Locales/it/Pode.psd1 index cd026c2fc..98a22b6c9 100644 --- a/src/Locales/it/Pode.psd1 +++ b/src/Locales/it/Pode.psd1 @@ -330,12 +330,12 @@ signalConnectionNameNotFoundExceptionMessage = "Nessuna connessione Signal trovata con il nome '{0}'." signalFailedToBroadcastExceptionMessage = 'Signal fallito durante la trasmissione a causa del livello di trasmissione Signal definito per {0}: {1}.' authMethodNotFoundExceptionMessage = "Nessuna metodo di autenticazione trovata con il nome '{0}'." - authMethodEventAlreadyRegisteredExceptionMessage = "L'événement '{0}' est déjà enregistré pour le méthode d'authentification '{1}'." - authMethodEventNotRegisteredExceptionMessage = "L'événement '{0}' n'est pas enregistré pour le méthode d'authentification '{1}'." - signalEventAlreadyRegisteredExceptionMessage = "L'événement '{0}' est déjà enregistré pour la connexion Signal '{1}'." - signalEventNotRegisteredExceptionMessage = "L'événement '{0}' n'est pas enregistré pour la connexion Signal '{1}'." - sseEventAlreadyRegisteredExceptionMessage = "L'événement '{0}' est déjà enregistré pour la connexion SSE '{1}'." - sseEventNotRegisteredExceptionMessage = "L'événement '{0}' n'est pas enregistré pour la connexion SSE '{1}'." + authMethodEventAlreadyRegisteredExceptionMessage = "L'evento '{0}' è già registrato per il metodo di autenticazione '{1}'." + authMethodEventNotRegisteredExceptionMessage = "L'evento '{0}' non è registrato per il metodo di autenticazione '{1}'." + signalEventAlreadyRegisteredExceptionMessage = "L'evento '{0}' è già registrato per la connessione Signal '{1}'." + signalEventNotRegisteredExceptionMessage = "L'evento '{0}' non è registrato per la connessione Signal '{1}'." + sseEventAlreadyRegisteredExceptionMessage = "L'evento '{0}' è già registrato per la connessione SSE '{1}'." + sseEventNotRegisteredExceptionMessage = "L'evento '{0}' non è registrato per la connessione SSE '{1}'." jsonSchemaObjectPropertyMissingNameExceptionMessage = "La proprietà di uno schema di oggetto JSON non ha un nome. Assegna un nome a questa proprietà usando il parametro 'Name'." jsonSchemaObjectMaxPropsLessThanMinPropsExceptionMessage = 'MaxProperties non può essere inferiore a MinProperties per la proprietà dello schema JSON di tipo oggetto.' jsonSchemaArrayMaxItemsLessThanMinItemsExceptionMessage = 'MaxItems non può essere inferiore a MinItems per la proprietà dello schema JSON di tipo array.' @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "I valori predefiniti delle route statiche non possono essere percorsi assoluti. Valore predefinito non valido: '{0}'" deprecatedFunctionWarningMessage = "AVVERTENZA: La funzione '{0}' è obsoleta e sarà rimossa nelle versioni future. Si prega di utilizzare la funzione '{1}' invece." loggingMethodDoesNotExistExceptionMessage = "Il metodo di registrazione '{0}' non esiste." + loggingApiMethodBodyNotStringExceptionMessage = "Il corpo restituito dallo scriptblock del corpo dell'API di registrazione non è una stringa." + loggingApiMethodHeadersNotHashtableExceptionMessage = "Gli header restituiti dallo scriptblock degli header dell'API di registrazione non sono una tabella hash." + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'È richiesto un ScriptBlock non vuoto per il formato di serializzazione della registrazione personalizzata.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'È richiesto un ScriptBlock non vuoto per il formato di registro personalizzato.' } \ No newline at end of file diff --git a/src/Locales/ja/Pode.psd1 b/src/Locales/ja/Pode.psd1 index 1b134bf54..cdfcc2dc8 100644 --- a/src/Locales/ja/Pode.psd1 +++ b/src/Locales/ja/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "静的ルートのデフォルトはルートパスにできません。無効なデフォルト: '{0}'" deprecatedFunctionWarningMessage = "警告: 関数 '{0}' は非推奨であり、将来のリリースで削除されます。代わりに '{1}' 関数を使用してください。" loggingMethodDoesNotExistExceptionMessage = "ログ方法 '{0}' は存在しません。" + loggingApiMethodBodyNotStringExceptionMessage = 'ログAPIのボディスクリプトブロックから返されたボディは文字列ではありません。' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'ログAPIのヘッダースクリプトブロックから返されたヘッダーはハッシュテーブルではありません。' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'カスタムログシリアル化形式には、空でないScriptBlockが必要です。' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'カスタムログ形式には、空でないScriptBlockが必要です。' } \ No newline at end of file diff --git a/src/Locales/ko/Pode.psd1 b/src/Locales/ko/Pode.psd1 index 1c550a76e..42e60143d 100644 --- a/src/Locales/ko/Pode.psd1 +++ b/src/Locales/ko/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "정적 경로의 기본값은 루트 경로일 수 없습니다. 잘못된 기본값: '{0}'" deprecatedFunctionWarningMessage = "경고: 함수 '{0}'는 더 이상 사용되지 않으며 향후 릴리스에서 제거될 예정입니다. 대신 '{1}' 함수를 사용하십시오." loggingMethodDoesNotExistExceptionMessage = "로그 방법 '{0}'은 존재하지 않습니다." + loggingApiMethodBodyNotStringExceptionMessage = '로그 API 본문 스크립트블록에서 반환된 본문이 문자열이 아닙니다.' + loggingApiMethodHeadersNotHashtableExceptionMessage = '로그 API 헤더 스크립트블록에서 반환된 헤더가 해시테이블이 아닙니다.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = '사용자 정의 로그 직렬화 형식에는 비어 있지 않은 ScriptBlock이 필요합니다.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = '사용자 정의 로그 형식에는 비어 있지 않은 ScriptBlock이 필요합니다.' } \ No newline at end of file diff --git a/src/Locales/nl/Pode.psd1 b/src/Locales/nl/Pode.psd1 index f6f764328..ff9ec7fb3 100644 --- a/src/Locales/nl/Pode.psd1 +++ b/src/Locales/nl/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "Statische route-standaarden kunnen geen geroot pad zijn. Ongeldige standaard: '{0}'" deprecatedFunctionWarningMessage = "WAARSCHUWING: De functie '{0}' is verouderd en zal in toekomstige versies worden verwijderd. Gebruik in plaats daarvan de functie '{1}'." loggingMethodDoesNotExistExceptionMessage = "De logmethode '{0}' bestaat niet." + loggingApiMethodBodyNotStringExceptionMessage = 'Het lichaam dat wordt geretourneerd door het logging API body scriptblock is geen string.' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'De headers die worden geretourneerd door het logging API headers scriptblock zijn geen hashtable.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'Een niet-leeg ScriptBlock is vereist voor het aangepaste logging serialisatieformaat.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'Een niet-leeg ScriptBlock is vereist voor het aangepaste logformaat.' } \ No newline at end of file diff --git a/src/Locales/pl/Pode.psd1 b/src/Locales/pl/Pode.psd1 index fe5d7fc8d..b79ddff80 100644 --- a/src/Locales/pl/Pode.psd1 +++ b/src/Locales/pl/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "Domyślne wartości statycznej trasy nie mogą być ścieżkami zrootowanymi. Nieprawidłowa wartość domyślna: '{0}'" deprecatedFunctionWarningMessage = "UWAGA: Funkcja '{0}' jest przestarzała i zostanie usunięta w przyszłych wersjach. Proszę użyć funkcji '{1}' zamiast niej." loggingMethodDoesNotExistExceptionMessage = "Metoda logowania '{0}' nie istnieje." + loggingApiMethodBodyNotStringExceptionMessage = 'Ciało zwrócone przez blok skryptu ciała API logowania nie jest ciągiem znaków.' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'Nagłówki zwrócone przez blok skryptu nagłówków API logowania nie są hashtable.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'Niepusty ScriptBlock jest wymagany dla niestandardowego formatu serializacji logowania.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'Niepusty ScriptBlock jest wymagany dla niestandardowego formatu logowania.' } \ No newline at end of file diff --git a/src/Locales/pt/Pode.psd1 b/src/Locales/pt/Pode.psd1 index 6a71e67d0..fb578bc6f 100644 --- a/src/Locales/pt/Pode.psd1 +++ b/src/Locales/pt/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "Os valores padrão da rota estática não podem ser caminhos raiz. Valor padrão inválido: '{0}'" deprecatedFunctionWarningMessage = "AVISO: A função '{0}' está obsoleta e será removida em versões futuras. Por favor, use a função '{1}' em vez disso." loggingMethodDoesNotExistExceptionMessage = "O método de registro '{0}' não existe." + loggingApiMethodBodyNotStringExceptionMessage = 'O corpo retornado pelo bloco de script do corpo da API de registro não é uma string.' + loggingApiMethodHeadersNotHashtableExceptionMessage = 'Os cabeçalhos retornados pelo bloco de script dos cabeçalhos da API de registro não são uma hashtable.' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = 'Um ScriptBlock não vazio é necessário para o formato de serialização de registro personalizado.' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = 'Um ScriptBlock não vazio é necessário para o formato de log personalizado.' } \ No newline at end of file diff --git a/src/Locales/zh/Pode.psd1 b/src/Locales/zh/Pode.psd1 index 344098cef..41a9c9091 100644 --- a/src/Locales/zh/Pode.psd1 +++ b/src/Locales/zh/Pode.psd1 @@ -349,4 +349,8 @@ staticRouteDefaultCannotBeRootedExceptionMessage = "静态路由的默认值不能是根路径。无效的默认值: '{0}'" deprecatedFunctionWarningMessage = "警告: 函数 '{0}' 已被弃用,并将在未来版本中移除。请改用 '{1}' 函数。" loggingMethodDoesNotExistExceptionMessage = "日志方法 '{0}' 不存在。" + loggingApiMethodBodyNotStringExceptionMessage = '日志 API 本体脚本块返回的本体不是字符串。' + loggingApiMethodHeadersNotHashtableExceptionMessage = '日志 API 头脚本块返回的头不是哈希表。' + nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage = '自定义日志序列化格式需要一个非空的 ScriptBlock。' + nonEmptyScriptBlockRequiredForCustomLogExceptionMessage = '自定义日志格式需要一个非空的 ScriptBlock。' } \ No newline at end of file diff --git a/src/Pode.psd1 b/src/Pode.psd1 index cf9fea692..1e093bcdb 100644 --- a/src/Pode.psd1 +++ b/src/Pode.psd1 @@ -177,6 +177,9 @@ 'Set-PodeCurrentRunspaceName', 'Invoke-PodeGC', 'Start-PodeSleep', + 'ConvertTo-PodeString', + 'Get-PodeServerName', + 'Get-PodeAppName', # routes 'Add-PodeRoute', @@ -322,10 +325,10 @@ # logging 'New-PodeLoggingMethod', - 'Enable-PodeRequestLogType', - 'Enable-PodeErrorLogType', - 'Disable-PodeRequestLogType', - 'Disable-PodeErrorLogType', + 'Enable-PodeLogRequestType', + 'Enable-PodeLogErrorType', + 'Disable-PodeLogRequestType', + 'Disable-PodeLogErrorType', 'Add-PodeLogType', 'Remove-PodeLogType', 'Clear-PodeLogTypes', @@ -340,6 +343,21 @@ 'New-PodeLogCustomMethod', 'Clear-PodeLogMethods', 'Remove-PodeLogMethod', + 'Convert-PodeLogItemToString', + 'New-PodeLogSyslogInfo', + 'ConvertTo-PodeSyslog', + 'Get-PodeLogDefaultFormat', + 'Get-PodeLogDefaultSerialiseFormat', + 'Get-PodeLogDefaultSyslogFormat', + 'New-PodeLogApiMethod', + 'New-PodeLogAwsMethod', + 'New-PodeLogAzureMethod', + 'New-PodeLogDatadogMethod', + 'New-PodeLogNetworkMethod', + 'New-PodeLogSplunkMethod', + 'Set-PodeLogDefaultFormat', + 'Set-PodeLogDefaultSerialiseFormat', + 'Set-PodeLogDefaultSyslogFormat', # core 'Start-PodeServer', diff --git a/src/Pode.psm1 b/src/Pode.psm1 index 74a18a753..b28435bf4 100644 --- a/src/Pode.psm1 +++ b/src/Pode.psm1 @@ -29,7 +29,7 @@ throw } The import statement is within a try/catch block. - This way, if the module fails to load, your script won’t proceed, preventing possible errors or unexpected behaviour. + This way, if the module fails to load, your script won't proceed, preventing possible errors or unexpected behaviour. .NOTES This is the entry point for the Pode module. diff --git a/src/Private/Console.ps1 b/src/Private/Console.ps1 index b172a3e92..d4d05b588 100644 --- a/src/Private/Console.ps1 +++ b/src/Private/Console.ps1 @@ -1106,15 +1106,15 @@ function Get-PodeDefaultConsole { $KeyBindings = @{ # Define custom key bindings for controls. Browser = [System.ConsoleKey]::B # Open the default browser. Help = [System.ConsoleKey]::F2 # Show/hide help instructions. - OpenAPI = [System.ConsoleKey]::F3 # Show/hide OpenAPI information. - Endpoints = [System.ConsoleKey]::F4 # Show/hide endpoints. + OpenAPI = [System.ConsoleKey]::F3 # Show/hide OpenAPI information. + Endpoints = [System.ConsoleKey]::F4 # Show/hide endpoints. Clear = [System.ConsoleKey]::L # Clear the console output. - Quiet = [System.ConsoleKey]::F12 # Toggle quiet mode. + Quiet = [System.ConsoleKey]::F12 # Toggle quiet mode. Terminate = [System.ConsoleKey]::C # Terminate the server. - Restart = [System.ConsoleKey]::F6 # Restart the server. - Disable = [System.ConsoleKey]::F7 # Disable the server. - Suspend = [System.ConsoleKey]::F9 # Suspend the server. - Metrics = [System.ConsoleKey]::F10 # Show Metrics. + Restart = [System.ConsoleKey]::F6 # Restart the server. + Disable = [System.ConsoleKey]::F7 # Disable the server. + Suspend = [System.ConsoleKey]::F9 # Suspend the server. + Metrics = [System.ConsoleKey]::F10 # Show Metrics. } } else { @@ -1148,9 +1148,9 @@ function Get-PodeDefaultConsole { Header = [System.ConsoleColor]::White # The server's header section, including the Pode version and timestamp. EndpointsHeader = [System.ConsoleColor]::Yellow # The header for the endpoints list. Endpoints = [System.ConsoleColor]::Cyan # The endpoints URLs. - EndpointsProtocol = [System.ConsoleColor]::White # The endpoints protocol. - EndpointsFlag = [System.ConsoleColor]::Gray # The endpoints flags. - EndpointsName = [System.ConsoleColor]::Magenta # The endpoints Name. + EndpointsProtocol = [System.ConsoleColor]::White # The endpoints protocol. + EndpointsFlag = [System.ConsoleColor]::Gray # The endpoints flags. + EndpointsName = [System.ConsoleColor]::Magenta # The endpoints Name. OpenApiUrls = [System.ConsoleColor]::Cyan # URLs listed under the OpenAPI information section. OpenApiHeaders = [System.ConsoleColor]::Yellow # Section headers for OpenAPI information. OpenApiTitles = [System.ConsoleColor]::White # The OpenAPI "default" title. diff --git a/src/Private/Context.ps1 b/src/Private/Context.ps1 index 3d8159678..2d011638d 100644 --- a/src/Private/Context.ps1 +++ b/src/Private/Context.ps1 @@ -28,6 +28,10 @@ function New-PodeContext { [string] $Name = $null, + [Parameter()] + [string] + $AppName = $null, + [Parameter()] [string] $ServerlessType, @@ -61,6 +65,11 @@ function New-PodeContext { $Name = Get-PodeRandomName } + # set default app name if one not supplied + if (Test-PodeIsEmpty $AppName) { + $AppName = 'Pode' + } + # are we running in a serverless context $isServerless = ![string]::IsNullOrWhiteSpace($ServerlessType) @@ -90,6 +99,7 @@ function New-PodeContext { # set the server name, logic and root, and other basic properties $ctx.Server.Name = $Name + $ctx.Server.AppName = $AppName $ctx.Server.Logic = $ScriptBlock $ctx.Server.LogicPath = $FilePath $ctx.Server.Interval = $Interval @@ -134,11 +144,16 @@ function New-PodeContext { # basic logging setup $ctx.Server.Logging = @{ - Logger = [PodeLogger]::new() - Running = $false - Masking = @{} - Methods = [hashtable]::Synchronized(@{}) - Types = [hashtable]::Synchronized(@{}) + Logger = [PodeLogger]::new() + Running = $false + Masking = @{} + Formatting = @{ + Log = $null + Syslog = $null + Serialise = $null + } + Methods = [hashtable]::Synchronized(@{}) + Types = [hashtable]::Synchronized(@{}) } # set thread counts @@ -983,6 +998,11 @@ function Set-PodeServerConfiguration { Patterns = Remove-PodeEmptyItemsFromArray -Array @($Configuration.Logging.Masking.Patterns) Mask = Protect-PodeValue -Value $Configuration.Logging.Masking.Mask -Default '********' } + $Context.Server.Logging.Formatting = @{ + Log = Protect-PodeValue -Value $Configuration.Logging.Formatting.Log -Default $Context.Server.Logging.Formatting.Log -EnumType ([PodeLogFormat]) -AllowNullEnum + Syslog = Protect-PodeValue -Value $Configuration.Logging.Formatting.Syslog -Default $Context.Server.Logging.Formatting.Syslog -EnumType ([PodeSyslogFormat]) -AllowNullEnum + Serialise = Protect-PodeValue -Value $Configuration.Logging.Formatting.Serialise -Default $Context.Server.Logging.Formatting.Serialise -EnumType ([PodeSerialiseFormat]) -AllowNullEnum + } # sockets if (!(Test-PodeIsEmpty $Configuration.Ssl.Protocols)) { @@ -1018,70 +1038,76 @@ function Set-PodeServerConfiguration { } } + # server and app names + $Context.Server.Name = Protect-PodeValue -Value $Configuration.Name -Default $Context.Server.Name + $Context.Server.AppName = Protect-PodeValue -Value $Configuration.AppName -Default $Context.Server.AppName + # debug $Context.Server.Debug = @{ Breakpoints = @{ - Enabled = [bool](Protect-PodeValue -Value $Configuration.Debug.Breakpoints.Enable -Default $Context.Server.Debug.Breakpoints.Enable) + Enabled = [bool](Protect-PodeValue -Value $Configuration.Debug.Breakpoints.Enable -Default $Context.Server.Debug.Breakpoints.Enable) } } + # allowed actions $Context.Server.AllowedActions = @{ - Suspend = [bool](Protect-PodeValue -Value $Configuration.AllowedActions.Suspend -Default $Context.Server.AllowedActions.Suspend) - 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) + Suspend = [bool](Protect-PodeValue -Value $Configuration.AllowedActions.Suspend -Default $Context.Server.AllowedActions.Suspend) + 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) - LimitRuleName = (Protect-PodeValue -Value $Configuration.AllowedActions.DisableSettings.LimitRuleName -Default $Context.Server.AllowedActions.DisableSettings.LimitRuleName) + RetryAfter = [int](Protect-PodeValue -Value $Configuration.AllowedActions.DisableSettings.RetryAfter -Default $Context.Server.AllowedActions.DisableSettings.RetryAfter) + LimitRuleName = (Protect-PodeValue -Value $Configuration.AllowedActions.DisableSettings.LimitRuleName -Default $Context.Server.AllowedActions.DisableSettings.LimitRuleName) } Timeout = @{ - Suspend = [int](Protect-PodeValue -Value $Configuration.AllowedActions.Timeout.Suspend -Default $Context.Server.AllowedActions.Timeout.Suspend) - Resume = [int](Protect-PodeValue -Value $Configuration.AllowedActions.Timeout.Resume -Default $Context.Server.AllowedActions.Timeout.Resume) + Suspend = [int](Protect-PodeValue -Value $Configuration.AllowedActions.Timeout.Suspend -Default $Context.Server.AllowedActions.Timeout.Suspend) + Resume = [int](Protect-PodeValue -Value $Configuration.AllowedActions.Timeout.Resume -Default $Context.Server.AllowedActions.Timeout.Resume) } } + # key bindings and console settings $Context.Server.Console = @{ - DisableTermination = [bool](Protect-PodeValue -Value $Configuration.Console.DisableTermination -Default $Context.Server.Console.DisableTermination) - DisableConsoleInput = [bool](Protect-PodeValue -Value $Configuration.Console.DisableConsoleInput -Default $Context.Server.Console.DisableConsoleInput) - Quiet = [bool](Protect-PodeValue -Value $Configuration.Console.Quiet -Default $Context.Server.Console.Quiet) - ClearHost = [bool](Protect-PodeValue -Value $Configuration.Console.ClearHost -Default $Context.Server.Console.ClearHost) - ShowOpenAPI = [bool](Protect-PodeValue -Value $Configuration.Console.ShowOpenAPI -Default $Context.Server.Console.ShowOpenAPI) - ShowEndpoints = [bool](Protect-PodeValue -Value $Configuration.Console.ShowEndpoints -Default $Context.Server.Console.ShowEndpoints) - ShowHelp = [bool](Protect-PodeValue -Value $Configuration.Console.ShowHelp -Default $Context.Server.Console.ShowHelp) - ShowDivider = [bool](Protect-PodeValue -Value $Configuration.Console.ShowDivider -Default $Context.Server.Console.ShowDivider) - ShowTimeStamp = [bool](Protect-PodeValue -Value $Configuration.Console.ShowTimeStamp -Default $Context.Server.Console.ShowTimeStamp) - DividerLength = [int](Protect-PodeValue -Value $Configuration.Console.DividerLength -Default $Context.Server.Console.DividerLength) + DisableTermination = [bool](Protect-PodeValue -Value $Configuration.Console.DisableTermination -Default $Context.Server.Console.DisableTermination) + DisableConsoleInput = [bool](Protect-PodeValue -Value $Configuration.Console.DisableConsoleInput -Default $Context.Server.Console.DisableConsoleInput) + Quiet = [bool](Protect-PodeValue -Value $Configuration.Console.Quiet -Default $Context.Server.Console.Quiet) + ClearHost = [bool](Protect-PodeValue -Value $Configuration.Console.ClearHost -Default $Context.Server.Console.ClearHost) + ShowOpenAPI = [bool](Protect-PodeValue -Value $Configuration.Console.ShowOpenAPI -Default $Context.Server.Console.ShowOpenAPI) + ShowEndpoints = [bool](Protect-PodeValue -Value $Configuration.Console.ShowEndpoints -Default $Context.Server.Console.ShowEndpoints) + ShowHelp = [bool](Protect-PodeValue -Value $Configuration.Console.ShowHelp -Default $Context.Server.Console.ShowHelp) + ShowDivider = [bool](Protect-PodeValue -Value $Configuration.Console.ShowDivider -Default $Context.Server.Console.ShowDivider) + ShowTimeStamp = [bool](Protect-PodeValue -Value $Configuration.Console.ShowTimeStamp -Default $Context.Server.Console.ShowTimeStamp) + DividerLength = [int](Protect-PodeValue -Value $Configuration.Console.DividerLength -Default $Context.Server.Console.DividerLength) Colors = @{ - Header = Protect-PodeValue $Configuration.Console.Colors.Header -Default $Context.Server.Console.Colors.Header -EnumType ([type][System.ConsoleColor]) - EndpointsHeader = Protect-PodeValue -Value $Configuration.Console.Colors.EndpointsHeader -Default $Context.Server.Console.Colors.EndpointsHeader -EnumType ([type][System.ConsoleColor]) - Endpoints = Protect-PodeValue -Value $Configuration.Console.Colors.Endpoints -Default $Context.Server.Console.Colors.Endpoints -EnumType ([type][System.ConsoleColor]) - EndpointsProtocol = Protect-PodeValue -Value $Configuration.Console.Colors.EndpointsProtocol -Default $Context.Server.Console.Colors.EndpointsProtocol -EnumType ([type][System.ConsoleColor]) - EndpointsFlag = Protect-PodeValue -Value $Configuration.Console.Colors.EndpointsFlag -Default $Context.Server.Console.Colors.EndpointsFlag -EnumType ([type][System.ConsoleColor]) - EndpointsName = Protect-PodeValue -Value $Configuration.Console.Colors.EndpointsName -Default $Context.Server.Console.Colors.EndpointsName -EnumType ([type][System.ConsoleColor]) - OpenApiUrls = Protect-PodeValue -Value $Configuration.Console.Colors.OpenApiUrls -Default $Context.Server.Console.Colors.OpenApiUrls -EnumType ([type][System.ConsoleColor]) - OpenApiHeaders = Protect-PodeValue -Value $Configuration.Console.Colors.OpenApiHeaders -Default $Context.Server.Console.Colors.OpenApiHeaders -EnumType ([type][System.ConsoleColor]) - OpenApiTitles = Protect-PodeValue -Value $Configuration.Console.Colors.OpenApiTitles -Default $Context.Server.Console.Colors.OpenApiTitles -EnumType ([type][System.ConsoleColor]) - OpenApiSubtitles = Protect-PodeValue -Value $Configuration.Console.Colors.OpenApiSubtitles -Default $Context.Server.Console.Colors.OpenApiSubtitles -EnumType ([type][System.ConsoleColor]) - HelpHeader = Protect-PodeValue -Value $Configuration.Console.Colors.HelpHeader -Default $Context.Server.Console.Colors.HelpHeader -EnumType ([type][System.ConsoleColor]) - HelpKey = Protect-PodeValue -Value $Configuration.Console.Colors.HelpKey -Default $Context.Server.Console.Colors.HelpKey -EnumType ([type][System.ConsoleColor]) - HelpDescription = Protect-PodeValue -Value $Configuration.Console.Colors.HelpDescription -Default $Context.Server.Console.Colors.HelpDescription -EnumType ([type][System.ConsoleColor]) - HelpDivider = Protect-PodeValue -Value $Configuration.Console.Colors.HelpDivider -Default $Context.Server.Console.Colors.HelpDivider -EnumType ([type][System.ConsoleColor]) - Divider = Protect-PodeValue -Value $Configuration.Console.Colors.Divider -Default $Context.Server.Console.Colors.Divider -EnumType ([type][System.ConsoleColor]) - MetricsHeader = Protect-PodeValue -Value $Configuration.Console.Colors.MetricsHeader -Default $Context.Server.Console.Colors.MetricsHeader -EnumType ([type][System.ConsoleColor]) - MetricsLabel = Protect-PodeValue -Value $Configuration.Console.Colors.MetricsLabel -Default $Context.Server.Console.Colors.MetricsLabel -EnumType ([type][System.ConsoleColor]) - MetricsValue = Protect-PodeValue -Value $Configuration.Console.Colors.MetricsValue -Default $Context.Server.Console.Colors.MetricsValue -EnumType ([type][System.ConsoleColor]) + Header = Protect-PodeValue -Value $Configuration.Console.Colors.Header -Default $Context.Server.Console.Colors.Header -EnumType ([System.ConsoleColor]) + EndpointsHeader = Protect-PodeValue -Value $Configuration.Console.Colors.EndpointsHeader -Default $Context.Server.Console.Colors.EndpointsHeader -EnumType ([System.ConsoleColor]) + Endpoints = Protect-PodeValue -Value $Configuration.Console.Colors.Endpoints -Default $Context.Server.Console.Colors.Endpoints -EnumType ([System.ConsoleColor]) + EndpointsProtocol = Protect-PodeValue -Value $Configuration.Console.Colors.EndpointsProtocol -Default $Context.Server.Console.Colors.EndpointsProtocol -EnumType ([System.ConsoleColor]) + EndpointsFlag = Protect-PodeValue -Value $Configuration.Console.Colors.EndpointsFlag -Default $Context.Server.Console.Colors.EndpointsFlag -EnumType ([System.ConsoleColor]) + EndpointsName = Protect-PodeValue -Value $Configuration.Console.Colors.EndpointsName -Default $Context.Server.Console.Colors.EndpointsName -EnumType ([System.ConsoleColor]) + OpenApiUrls = Protect-PodeValue -Value $Configuration.Console.Colors.OpenApiUrls -Default $Context.Server.Console.Colors.OpenApiUrls -EnumType ([System.ConsoleColor]) + OpenApiHeaders = Protect-PodeValue -Value $Configuration.Console.Colors.OpenApiHeaders -Default $Context.Server.Console.Colors.OpenApiHeaders -EnumType ([System.ConsoleColor]) + OpenApiTitles = Protect-PodeValue -Value $Configuration.Console.Colors.OpenApiTitles -Default $Context.Server.Console.Colors.OpenApiTitles -EnumType ([System.ConsoleColor]) + OpenApiSubtitles = Protect-PodeValue -Value $Configuration.Console.Colors.OpenApiSubtitles -Default $Context.Server.Console.Colors.OpenApiSubtitles -EnumType ([System.ConsoleColor]) + HelpHeader = Protect-PodeValue -Value $Configuration.Console.Colors.HelpHeader -Default $Context.Server.Console.Colors.HelpHeader -EnumType ([System.ConsoleColor]) + HelpKey = Protect-PodeValue -Value $Configuration.Console.Colors.HelpKey -Default $Context.Server.Console.Colors.HelpKey -EnumType ([System.ConsoleColor]) + HelpDescription = Protect-PodeValue -Value $Configuration.Console.Colors.HelpDescription -Default $Context.Server.Console.Colors.HelpDescription -EnumType ([System.ConsoleColor]) + HelpDivider = Protect-PodeValue -Value $Configuration.Console.Colors.HelpDivider -Default $Context.Server.Console.Colors.HelpDivider -EnumType ([System.ConsoleColor]) + Divider = Protect-PodeValue -Value $Configuration.Console.Colors.Divider -Default $Context.Server.Console.Colors.Divider -EnumType ([System.ConsoleColor]) + MetricsHeader = Protect-PodeValue -Value $Configuration.Console.Colors.MetricsHeader -Default $Context.Server.Console.Colors.MetricsHeader -EnumType ([System.ConsoleColor]) + MetricsLabel = Protect-PodeValue -Value $Configuration.Console.Colors.MetricsLabel -Default $Context.Server.Console.Colors.MetricsLabel -EnumType ([System.ConsoleColor]) + MetricsValue = Protect-PodeValue -Value $Configuration.Console.Colors.MetricsValue -Default $Context.Server.Console.Colors.MetricsValue -EnumType ([System.ConsoleColor]) } KeyBindings = @{ - Browser = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Browser -Default $Context.Server.Console.KeyBindings.Browser -EnumType ([type][System.ConsoleKey]) - Help = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Help -Default $Context.Server.Console.KeyBindings.Help -EnumType ([type][System.ConsoleKey]) - OpenAPI = Protect-PodeValue -Value $Configuration.Console.KeyBindings.OpenAPI -Default $Context.Server.Console.KeyBindings.OpenAPI -EnumType ([type][System.ConsoleKey]) - Endpoints = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Endpoints -Default $Context.Server.Console.KeyBindings.Endpoints -EnumType ([type][System.ConsoleKey]) - Clear = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Clear -Default $Context.Server.Console.KeyBindings.Clear -EnumType ([type][System.ConsoleKey]) - Quiet = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Quiet -Default $Context.Server.Console.KeyBindings.Quiet -EnumType ([type][System.ConsoleKey]) - Terminate = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Terminate -Default $Context.Server.Console.KeyBindings.Terminate -EnumType ([type][System.ConsoleKey]) - Restart = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Restart -Default $Context.Server.Console.KeyBindings.Restart -EnumType ([type][System.ConsoleKey]) - Disable = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Disable -Default $Context.Server.Console.KeyBindings.Disable -EnumType ([type][System.ConsoleKey]) - Suspend = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Suspend -Default $Context.Server.Console.KeyBindings.Suspend -EnumType ([type][System.ConsoleKey]) - Metrics = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Metrics -Default $Context.Server.Console.KeyBindings.Metrics -EnumType ([type][System.ConsoleKey]) + Browser = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Browser -Default $Context.Server.Console.KeyBindings.Browser -EnumType ([System.ConsoleKey]) + Help = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Help -Default $Context.Server.Console.KeyBindings.Help -EnumType ([System.ConsoleKey]) + OpenAPI = Protect-PodeValue -Value $Configuration.Console.KeyBindings.OpenAPI -Default $Context.Server.Console.KeyBindings.OpenAPI -EnumType ([System.ConsoleKey]) + Endpoints = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Endpoints -Default $Context.Server.Console.KeyBindings.Endpoints -EnumType ([System.ConsoleKey]) + Clear = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Clear -Default $Context.Server.Console.KeyBindings.Clear -EnumType ([System.ConsoleKey]) + Quiet = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Quiet -Default $Context.Server.Console.KeyBindings.Quiet -EnumType ([System.ConsoleKey]) + Terminate = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Terminate -Default $Context.Server.Console.KeyBindings.Terminate -EnumType ([System.ConsoleKey]) + Restart = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Restart -Default $Context.Server.Console.KeyBindings.Restart -EnumType ([System.ConsoleKey]) + Disable = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Disable -Default $Context.Server.Console.KeyBindings.Disable -EnumType ([System.ConsoleKey]) + Suspend = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Suspend -Default $Context.Server.Console.KeyBindings.Suspend -EnumType ([System.ConsoleKey]) + Metrics = Protect-PodeValue -Value $Configuration.Console.KeyBindings.Metrics -Default $Context.Server.Console.KeyBindings.Metrics -EnumType ([System.ConsoleKey]) } } } diff --git a/src/Private/Events.ps1 b/src/Private/Events.ps1 index 7716fea42..abbf1841d 100644 --- a/src/Private/Events.ps1 +++ b/src/Private/Events.ps1 @@ -3,7 +3,7 @@ using namespace Pode.Utilities function Invoke-PodeEvent { param( [Parameter(Mandatory = $true)] - [PodeServerEventType] + [Pode.Utilities.PodeServerEventType] $Type, [Parameter()] diff --git a/src/Private/Helpers.ps1 b/src/Private/Helpers.ps1 index 913499b34..112b0cadb 100644 --- a/src/Private/Helpers.ps1 +++ b/src/Private/Helpers.ps1 @@ -1390,8 +1390,8 @@ function New-PodeRequestException { $Message, [Parameter()] - [PodeProtocolType] - $Type = [PodeProtocolType]::Http + [Pode.Utilities.PodeProtocolType] + $Type = [Pode.Utilities.PodeProtocolType]::Http ) return [PodeRequestExceptionFactory]::Create($Type, $Message, $StatusCode) @@ -3437,13 +3437,17 @@ function ConvertTo-PodeYaml { [CmdletBinding()] [OutputType([string])] param ( - [parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] + [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [AllowNull()] $InputObject, - [parameter()] + [Parameter()] + [int] + $Depth = 16, + + [Parameter()] [int] - $Depth = 16 + $StringWrapLength = 80 ) begin { @@ -3460,7 +3464,7 @@ function ConvertTo-PodeYaml { } if ($PodeContext.Server.Web.OpenApi.UsePodeYamlInternal) { - return ConvertTo-PodeYamlInternal -InputObject $InputObject -Depth $Depth -NoNewLine + return ConvertTo-PodeYamlInternal -InputObject $InputObject -Depth $Depth -StringWrapLength $StringWrapLength -NoNewLine } if ($null -eq $PodeContext.Server.InternalCache.YamlModuleImported) { @@ -3471,7 +3475,7 @@ function ConvertTo-PodeYaml { return ($InputObject | ConvertTo-Yaml) } else { - return ConvertTo-PodeYamlInternal -InputObject $InputObject -Depth $Depth -NoNewLine + return ConvertTo-PodeYamlInternal -InputObject $InputObject -Depth $Depth -StringWrapLength $StringWrapLength -NoNewLine } } } @@ -3508,72 +3512,76 @@ function ConvertTo-PodeYaml { .NOTES This is an internal function and may change in future releases of Pode. It converts only basic PowerShell types, such as strings, integers, booleans, arrays, hashtables, and ordered dictionaries into a YAML format. - #> function ConvertTo-PodeYamlInternal { [CmdletBinding()] [OutputType([string])] - param ( - [parameter(Mandatory = $true)] + param( + [Parameter(Mandatory = $true)] [AllowNull()] $InputObject, - [parameter()] + [Parameter()] [int] $Depth = 10, - [parameter()] + [Parameter()] [int] $NestingLevel = 0, - [parameter()] + [Parameter()] + [int] + $StringWrapLength = 80, + + [Parameter()] [switch] $NoNewLine ) #report the leaves in terms of object type if ($Depth -ilt $NestingLevel) { - return '' + return [string]::Empty } + # if it is null return null - If ( !($InputObject) ) { - if ($InputObject -is [Object[]]) { + If (!$InputObject) { + if ($InputObject -is [object[]]) { return '[]' } - else { - return '' - } + + return [string]::Empty } - $padding = [string]::new(' ', $NestingLevel * 2) # lets just create our left-padding for the block + # the padding for the current level of nesting + $padding = [string]::new(' ', $NestingLevel * 2) + try { - $Type = $InputObject.GetType().Name # we start by getting the object's type + # we start by getting the object's type + $Type = $InputObject.GetType().Name if ($InputObject -is [object[]]) { - #what it really is $Type = "$($InputObject.GetType().BaseType.Name)" } - # Check for specific value types string + # override the type if it's an ordered dictionary or list if ($Type -ne 'String') { - # prevent these values being identified as an object if ($InputObject -is [System.Collections.Specialized.OrderedDictionary]) { - $Type = 'hashTable' + $Type = 'hashtable' } elseif ($Type -ieq 'List`1') { $Type = 'array' } elseif ($InputObject -is [array]) { $Type = 'array' - } # whatever it thinks it is called + } elseif ($InputObject -is [hashtable] ) { - $Type = 'hashTable' - } # for our purposes it is a hashtable + $Type = 'hashtable' + } } $output += switch ($Type.ToLower()) { 'string' { $String = "$InputObject" - if (($string -match '[\r\n]' -or $string.Length -gt 80) -and ($string -notlike 'http*')) { + if (($string -match '[\r\n]' -or ($StringWrapLength -gt 0 -and $string.Length -gt $StringWrapLength)) -and ($string -notlike 'http*')) { $multiline = [System.Text.StringBuilder]::new("|`n") $items = $string.Split("`n") @@ -3581,14 +3589,13 @@ function ConvertTo-PodeYamlInternal { $workingString = $items[$i] -replace '\r$' $length = $workingString.Length $index = 0 - $wrap = 80 while ($index -lt $length) { - $breakpoint = $wrap + $breakpoint = $StringWrapLength $linebreak = $false - if (($length - $index) -gt $wrap) { - $lastSpaceIndex = $workingString.LastIndexOf(' ', $index + $wrap, $wrap) + if (($StringWrapLength -gt 0) -and (($length - $index) -gt $StringWrapLength)) { + $lastSpaceIndex = $workingString.LastIndexOf(' ', $index + $StringWrapLength, $StringWrapLength) if ($lastSpaceIndex -ne -1) { $breakpoint = $lastSpaceIndex - $index } @@ -3622,15 +3629,17 @@ function ConvertTo-PodeYamlInternal { } else { # decide if this needs quoting - $needsQuote = ($string -match '^[\-?:,\[\]{}#&*!|>''"%@`]') -or - $string.StartsWith(' ') -or # leading space - $string.EndsWith(' ') -or # trailing space - ($string -match ':\s') -or # contains ": " - ($string -match '^(?:~|null|true|false)$') -or # bare null/boolean - ($string -match '^-?\d+(\.\d+)?$') # integer or float - + $needsQuote = ( + ($string -match '^[\-?:,\[\]{}#&*!|>''"%@`]') -or + $string.StartsWith(' ') -or # leading space + $string.EndsWith(' ') -or # trailing space + ($string -match ':\s') -or # contains ": " + ($string -match '^(?:~|null|true|false)$') -or # bare null/boolean + ($string -match '^-?\d+(\.\d+)?$') # integer or float + ) + + # single-quote style: double any internal ' to '' if ($needsQuote) { - # single-quote style: double any internal ' to '' $s = $string -replace '''', '''''' "'$s'" } @@ -3660,7 +3669,7 @@ function ConvertTo-PodeYamlInternal { } else { if ($InputObject[$item] -is [string]) { $increment = 2 } else { $increment = 1 } - $null = $string.Append((ConvertTo-PodeYamlInternal -InputObject $InputObject[$item] -Depth $Depth -NestingLevel ($NestingLevel + $increment))) + $null = $string.Append((ConvertTo-PodeYamlInternal -InputObject $InputObject[$item] -Depth $Depth -StringWrapLength $StringWrapLength -NestingLevel ($NestingLevel + $increment))) } } $string.ToString() @@ -3686,7 +3695,7 @@ function ConvertTo-PodeYamlInternal { } else { if ($InputObject.$item -is [string]) { $increment = 2 } else { $increment = 1 } - $null = $string.Append((ConvertTo-PodeYamlInternal -InputObject $InputObject.$item -Depth $Depth -NestingLevel ($NestingLevel + $increment))) + $null = $string.Append((ConvertTo-PodeYamlInternal -InputObject $InputObject.$item -Depth $Depth -StringWrapLength $StringWrapLength -NestingLevel ($NestingLevel + $increment))) } } $string.ToString() @@ -3700,25 +3709,268 @@ function ConvertTo-PodeYamlInternal { $index = 0 foreach ($item in $InputObject ) { if ($NoNewLine -and $index++ -eq 0) { $NewPadding = '' } else { $NewPadding = "`n$padding" } - $null = $string.Append($NewPadding).Append('- ').Append((ConvertTo-PodeYamlInternal -InputObject $item -depth $Depth -NestingLevel ($NestingLevel + 1) -NoNewLine)) + $null = $string.Append($NewPadding).Append('- ').Append((ConvertTo-PodeYamlInternal -InputObject $item -Depth $Depth -StringWrapLength $StringWrapLength -NestingLevel ($NestingLevel + 1) -NoNewLine)) } $string.ToString() break } default { - "'$InputObject'" + "'$($InputObject)'" } } + return $Output } catch { $_ | Write-PodeErrorLog - $_.Exception | Write-PodeErrorLog -CheckInnerException - throw ($PodeLocale.scriptErrorExceptionMessage -f $_, $_.InvocationInfo.ScriptName, $_.InvocationInfo.Line.Trim(), $_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.OffsetInLine, $_.InvocationInfo.MyCommand, $type, $InputObject, $InputObject.GetType().Name, $InputObject.GetType().BaseType.Name) + throw } } +function ConvertTo-PodeXml { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [AllowNull()] + [object] + $InputObject, + + [Parameter()] + [int] + $Depth = 10, + + [Parameter()] + [string] + $RootName = 'root', + + [Parameter()] + [ValidateSet('Parent', 'Item', 'Parent-Hide')] + $ArrayItemName = 'Item', + + [switch] + $EncodeNewlines, + + [switch] + $Compress, + + [switch] + $IncludeDeclaration + ) + + begin { + $pipelineObject = @() + } + + process { + $pipelineObject += $_ + } + + + end { + if ($pipelineObject.Count -gt 1) { + $InputObject = $pipelineObject + } + + ConvertTo-PodeXmlInternal ` + -InputObject $InputObject ` + -Depth $Depth ` + -ElementName $RootName ` + -IsRoot ` + -ArrayItemName $ArrayItemName ` + -EncodeNewlines:$EncodeNewlines ` + -Compress:$Compress ` + -IncludeDeclaration:$IncludeDeclaration + } +} + +# based on https://blog.wannemacher.us/posts/p430/, by Eric Wannemacher +function ConvertTo-PodeXmlInternal { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Mandatory = $true)] + [AllowNull()] + [object] + $InputObject, + + [Parameter()] + [int] + $Depth = 10, + + [Parameter()] + [string] + $ElementName = 'root', + + [Parameter()] + [int] + $NestingLevel = 0, + + [Parameter()] + [System.Text.StringBuilder] + $StringBuilder, + + [Parameter()] + [ValidateSet('Parent', 'Item', 'Parent-Hide')] + $ArrayItemName = 'Item', + + [switch] + $IsRoot, + + [switch] + $EncodeNewlines, + + [switch] + $Compress, + + [switch] + $IncludeDeclaration + ) + + # do we need a string builder? + if ($null -eq $StringBuilder) { + $StringBuilder = [System.Text.StringBuilder]::new() + } + + # if this is the root, add the xml declaration + if ($IsRoot -and $IncludeDeclaration) { + $null = $StringBuilder.Append('') + if (!$Compress) { + $null = $StringBuilder.AppendLine() + } + } + + # the padding for this level of nesting + $padding = [string]::Empty + if (!$Compress) { + $padding = [string]::new(' ', $NestingLevel * 2) + } + + # are we hiding the array parent elements? + $hideArrayParent = ($ArrayItemName -eq 'Parent-Hide') -and ($InputObject -is [array]) + + # indent the element and add the opening tag + if (!$IsRoot -and !$Compress -and !$hideArrayParent) { + $null = $StringBuilder.AppendLine() + } + if (!$hideArrayParent) { + $null = $StringBuilder.Append($padding).Append("<$([System.Web.HttpUtility]::HtmlEncode($ElementName))>") + } + + # if we're at the max depth, just show the object type + if ($Depth -lt $NestingLevel) { + $null = $StringBuilder.Append($InputObject.GetType().FullName) + if (!$hideArrayParent) { + $null = $StringBuilder.Append("") + } + return + } + + # append new line at end? + $newLine = $true + + # get the objects type, and overrides + $type = [string]::Empty + if (($InputObject -is [hashtable]) -or ($InputObject -is [System.Collections.Specialized.OrderedDictionary])) { + $type = 'hashtable' + } + elseif (($InputObject -is [array]) -or ($InputObject -is [System.Collections.IList])) { + $type = 'array' + } + else { + $type = $InputObject.GetType().Name + } + + # render based on object type + switch ($type.ToLower()) { + 'pscustomobject' { + foreach ($prop in (Get-Member -InputObject $InputObject -MemberType Properties).Name) { + ConvertTo-PodeXmlInternal ` + -InputObject ($InputObject.$prop) ` + -NestingLevel ($NestingLevel + 1) ` + -Depth $Depth ` + -ElementName $prop ` + -StringBuilder $StringBuilder ` + -ArrayItemName $ArrayItemName ` + -EncodeNewlines:$EncodeNewlines ` + -Compress:$Compress + } + } + + 'hashtable' { + foreach ($key in $InputObject.Keys) { + ConvertTo-PodeXmlInternal ` + -InputObject ($InputObject[$key]) ` + -NestingLevel ($NestingLevel + 1) ` + -Depth $Depth ` + -ElementName $key ` + -StringBuilder $StringBuilder ` + -ArrayItemName $ArrayItemName ` + -EncodeNewlines:$EncodeNewlines ` + -Compress:$Compress + } + } + + + 'array' { + # are we using "item" or the parent name for the array items? + $elName = 'item' + if ($ArrayItemName -iin 'parent', 'parent-hide') { + $elName = $ElementName + } + + # increment the nesting level, unless we're hiding the array parent elements + $newNestingLevel = $NestingLevel + if (!$hideArrayParent) { + $newNestingLevel++ + } + + foreach ($element in $InputObject) { + ConvertTo-PodeXmlInternal ` + -InputObject $element ` + -NestingLevel $newNestingLevel ` + -Depth $Depth ` + -ElementName $elName ` + -StringBuilder $StringBuilder ` + -ArrayItemName $ArrayItemName ` + -EncodeNewlines:$EncodeNewlines ` + -Compress:$Compress + } + } + + default { + if ($null -eq $InputObject) { + $InputObject = [string]::Empty + } + + # encode new line chars + $value = [System.Web.HttpUtility]::HtmlEncode($InputObject.ToString()) + if ($EncodeNewlines -or $Compress) { + $value = $value.Replace("`n", ' ').Replace("`r", ' ') + } + + $null = $StringBuilder.Append($value) + $padding = [string]::Empty + $newLine = $false + } + } + + # add new line before closing tag, unless we're compressing or hiding the array parent elements + if ($newLine -and !$Compress -and !$hideArrayParent) { + $null = $StringBuilder.AppendLine() + } + + # close the element, unless we're hiding the array parent elements + if (!$hideArrayParent) { + $null = $StringBuilder.Append($padding).Append("") + } + + # at the end, if this is the root, return the final string + if ($IsRoot) { + return $StringBuilder.ToString() + } +} <# .SYNOPSIS @@ -4115,4 +4367,35 @@ function Get-PodeImageContentType { # If none of the above formats match, return a generic binary type return 'application/octet-stream' +} + +function Get-PodeUnixEpoch { + return [datetime]::new(1970, 1, 1, 0, 0, 0, [datetimekind]::Utc) +} + +function ConvertTo-PodeUnixEpoch { + param( + [Parameter(Mandatory = $true)] + [datetime] + $DateTime, + + [Parameter()] + [ValidateSet('Seconds', 'Milliseconds')] + [string] + $Format = 'Seconds' + ) + + $unixEpoch = Get-PodeUnixEpoch + $diff = $DateTime.ToUniversalTime() - $unixEpoch + + switch ($Format.ToLowerInvariant()) { + 'seconds' { + $amount = $diff.TotalSeconds + } + 'milliseconds' { + $amount = $diff.TotalMilliseconds + } + } + + return [math]::Round($amount) } \ No newline at end of file diff --git a/src/Private/Logging.ps1 b/src/Private/Logging.ps1 index e96217499..51287308c 100644 --- a/src/Private/Logging.ps1 +++ b/src/Private/Logging.ps1 @@ -1,3 +1,4 @@ +using namespace Pode.Utilities using namespace Pode.Utilities.Logging function Get-PodeLoggingTerminalMethod { @@ -23,22 +24,15 @@ function Get-PodeLoggingTerminalMethod { Test-PodeSuspensionToken try { - # try and get a log item - $log = $null - $found = $method.Queue.TryTake([ref]$log, $PodeContext.Tokens.Cancellation.Token) + # try and get a log item collection + $logCol = $null + $found = $method.Queue.TryTake([ref]$logCol, $PodeContext.Tokens.Cancellation.Token) - if (!$found -or ($null -eq $log)) { + if (!$found -or ($null -eq $logCol)) { continue } - # check if it's an array from batching - if ($log.Items -is [array]) { - $log.Items = $log.Items -join [System.Environment]::NewLine - } - - # protect then write - $log.Items = ($log.Items | Protect-PodeLogItem) - $log.Items.ToString() | Out-PodeHost + $logCol | Convert-PodeLogItemToString | Protect-PodeLogItem | Out-PodeHost } catch [System.OperationCanceledException] { $_ | Write-PodeErrorLog -Level Debug @@ -46,6 +40,11 @@ function Get-PodeLoggingTerminalMethod { catch { $_ | Out-Default } + finally { + if ($null -ne $logCol) { + $logCol.Dispose() + } + } } } catch [System.OperationCanceledException] { @@ -78,21 +77,13 @@ function Get-PodeLoggingFileMethod { try { # try and get a log item - $log = $null - $found = $method.Queue.TryTake([ref]$log, $PodeContext.Tokens.Cancellation.Token) + $logCol = $null + $found = $method.Queue.TryTake([ref]$logCol, $PodeContext.Tokens.Cancellation.Token) - if (!$found -or ($null -eq $log)) { + if (!$found -or ($null -eq $logCol)) { continue } - # check if it's an array from batching - if ($log.Items -is [array]) { - $log.Items = $log.Items -join [System.Environment]::NewLine - } - - # mask values - $log.Items = $log.Items | Protect-PodeLogItem - # current date $date = [DateTime]::Now.ToString('yyyy-MM-dd') @@ -124,7 +115,7 @@ function Get-PodeLoggingFileMethod { $path = [System.IO.Path]::Combine($method.Arguments.Path, "$($method.Arguments.Name)_$($date)_$($id).log") # write the item to the file - $log.Items.ToString() | Out-File -FilePath $path -Encoding utf8 -Append -Force + $logCol | Convert-PodeLogItemToString | Protect-PodeLogItem | Out-File -FilePath $path -Encoding utf8 -Append -Force # if set, remove log files beyond days set (ensure this is only run once a day) if (($method.Arguments.MaxDays -gt 0) -and ($method.Arguments.NextClearDown -le [DateTime]::Now.Date)) { @@ -143,6 +134,11 @@ function Get-PodeLoggingFileMethod { catch { $_ | Out-Default } + finally { + if ($null -ne $logCol) { + $logCol.Dispose() + } + } } } catch [System.OperationCanceledException] { @@ -174,25 +170,16 @@ function Get-PodeLoggingEventViewerMethod { try { # try and get a log item - $log = $null - $found = $method.Queue.TryTake([ref]$log, $PodeContext.Tokens.Cancellation.Token) + $logCol = $null + $found = $method.Queue.TryTake([ref]$logCol, $PodeContext.Tokens.Cancellation.Token) - if (!$found -or ($null -eq $log)) { + if (!$found -or ($null -eq $logCol)) { continue } - # check if it's an array from batching - if ($log.Items -isnot [array]) { - $log.Items = @($log.Items) - } - - if ($log.RawItems -isnot [array]) { - $log.RawItems = @($log.RawItems) - } - - for ($i = 0; $i -lt $log.Items.Length; $i++) { + foreach ($item in $logCol.Items) { # convert log level - info if no level present - $entryType = ConvertTo-PodeEventViewerLevel -Level $log.RawItems[$i].Level + $entryType = ConvertTo-PodeEventViewerLevel -Level $item.Event.Level # create log instance $entryInstance = [System.Diagnostics.EventInstance]::new($method.Arguments.ID, 0, $entryType) @@ -203,7 +190,7 @@ function Get-PodeLoggingEventViewerMethod { $entryLog.Source = $method.Arguments.Source try { - $message = ($log.Items[$i] | Protect-PodeLogItem) + $message = $item | Convert-PodeLogItemToString | Protect-PodeLogItem $entryLog.WriteEvent($entryInstance, $message) } catch { @@ -217,6 +204,11 @@ function Get-PodeLoggingEventViewerMethod { catch { $_ | Out-Default } + finally { + if ($null -ne $logCol) { + $logCol.Dispose() + } + } } } catch [System.OperationCanceledException] { @@ -249,15 +241,24 @@ function Get-PodeLoggingCustomMethod { try { # try and get a log item - $log = $null - $found = $method.Queue.TryTake([ref]$log, $PodeContext.Tokens.Cancellation.Token) + $logCol = $null + $found = $method.Queue.TryTake([ref]$logCol, $PodeContext.Tokens.Cancellation.Token) - if (!$found -or ($null -eq $log)) { + if (!$found -or ($null -eq $logCol)) { continue } - # invoke the custom scriptblock - $_args = @(, $log.Items) + @($method.Arguments) + @(, $log.RawItems) + # build scriptblock arguments based on the Log Method version + switch ($method.Version) { + 1 { + $_args = @(, ($logCol.Items | Select-Object -ExpandProperty 'Data')) + @($method.Arguments) + @(, ($logCol.Items.Event | Select-Object -ExpandProperty 'Data')) + } + + 2 { + $_args = @(, $logCol.Items) + @($method.Arguments) + } + } + $null = Invoke-PodeScriptBlock ` -ScriptBlock $method.Custom.ScriptBlock ` -Arguments $_args ` @@ -270,6 +271,11 @@ function Get-PodeLoggingCustomMethod { catch { $_ | Out-Default } + finally { + if ($null -ne $logCol) { + $logCol.Dispose() + } + } } } catch [System.OperationCanceledException] { @@ -282,6 +288,442 @@ function Get-PodeLoggingCustomMethod { } } +function Get-PodeLoggingApiMethod { + return { + param( + [Parameter(Mandatory = $true)] + [string] + $MethodId + ) + + # wait for the server to fully start + Wait-PodeCancellationTokenRequest -Type Start + + try { + $method = $PodeContext.Server.Logging.Methods[$MethodId] + + while (!(Test-PodeCancellationTokenRequest -Type Terminate)) { + # check for suspension + Test-PodeSuspensionToken + + try { + # try and get a log item + $logCol = $null + $found = $method.Queue.TryTake([ref]$logCol, $PodeContext.Tokens.Cancellation.Token) + + if (!$found -or ($null -eq $logCol)) { + continue + } + + # get body + $body = Invoke-PodeScriptBlock ` + -ScriptBlock $method.Arguments.Body.ScriptBlock ` + -Arguments @((, $logCol.Items), $method.Arguments.Body.Arguments) ` + -UsingVariables $method.Arguments.Body.UsingVariables ` + -Splat -Return + + if ([string]::IsNullOrWhiteSpace($body)) { + continue + } + + if ($body -isnot [string]) { + # The body returned from the logging API body scriptblock is not a string + throw ($PodeLocale.loggingApiMethodBodyNotStringExceptionMessage) + } + + $body = $body | Protect-PodeLogItem + + # do we need to compress the body? + if ($method.Arguments.Compress) { + $body = [Pode.Utilities.PodeHelpers]::CompressString($body, [Pode.Utilities.PodeCompressionType]::Gzip) + } + + # get headers + $headers = $method.Arguments.Headers.Value + + if ($null -ne $method.Arguments.Headers.ScriptBlock) { + $addHeaders = Invoke-PodeScriptBlock ` + -ScriptBlock $method.Arguments.Headers.ScriptBlock ` + -Arguments @($body, $method.Arguments.Headers.Arguments) ` + -UsingVariables $method.Arguments.Headers.UsingVariables ` + -Splat -Return + + if ($null -ne $addHeaders) { + if ($addHeaders -isnot [hashtable]) { + # The headers returned from the logging API headers scriptblock is not a hashtable + throw ($PodeLocale.loggingApiMethodHeadersNotHashtableExceptionMessage) + } + + foreach ($key in $addHeaders.Keys) { + $headers[$key] = $addHeaders[$key] + } + } + } + + # invoke the API + Invoke-RestMethod ` + -Uri $method.Arguments.Url ` + -Method $method.Arguments.Method ` + -Headers $headers ` + -Body $body ` + -ContentType $method.Arguments.ContentType ` + -SkipCertificateCheck:($method.Arguments.SkipCertificateCheck) + } + catch [System.OperationCanceledException] { + $_ | Write-PodeErrorLog -Level Debug + } + catch { + $_ | Out-Default + } + finally { + if ($null -ne $logCol) { + $logCol.Dispose() + } + } + } + } + catch [System.OperationCanceledException] { + $_ | Write-PodeErrorLog -Level Debug + } + catch { + $_ | Out-Default + throw + } + } +} + +function Get-PodeLoggingNetworkMethod { + return { + param( + [Parameter(Mandatory = $true)] + [string] + $MethodId + ) + + # wait for the server to fully start + Wait-PodeCancellationTokenRequest -Type Start + + try { + $method = $PodeContext.Server.Logging.Methods[$MethodId] + + $client = [Pode.Transport.Clients.PodeClientFactory]::Create( + $method.Arguments.Transport, + $method.Arguments.Server, + $method.Arguments.Port, + $method.Arguments.SkipCertificateCheck + ) + $client.Connect() + + while (!(Test-PodeCancellationTokenRequest -Type Terminate)) { + # check for suspension + Test-PodeSuspensionToken + + try { + # try and get a log item + $logCol = $null + $found = $method.Queue.TryTake([ref]$logCol, $PodeContext.Tokens.Cancellation.Token) + + if (!$found -or ($null -eq $logCol)) { + continue + } + + # send each log item over the network + foreach ($item in $logCol.Items) { + $message = $item | Convert-PodeLogItemToString | Protect-PodeLogItem + $bytes = [System.Text.Encoding]::UTF8.GetBytes($message) + $client.Send($bytes) + } + } + catch [System.OperationCanceledException] { + $_ | Write-PodeErrorLog -Level Debug + } + catch { + $_ | Out-Default + } + finally { + if ($null -ne $logCol) { + $logCol.Dispose() + } + } + } + } + catch [System.OperationCanceledException] { + $_ | Write-PodeErrorLog -Level Debug + } + catch { + $_ | Out-Default + throw + } + finally { + if ($null -ne $client) { + $client.Dispose() + } + } + } +} + +function New-PodeLogAzureWorkspaceMethod { + [OutputType([string])] + param( + [Parameter()] + [string] + $Id, + + [Parameter(Mandatory = $true)] + [string] + $WorkspaceId, + + [Parameter(Mandatory = $true)] + [string] + $SharedKey, + + [Parameter(Mandatory = $true)] + [string] + $LogType, + + [Parameter()] + [string] + $Source, + + [Parameter()] + [hashtable] + $BatchInfo = $null, + + [switch] + $SkipCertificateCheck + ) + + # build body scriptblock + $bodyScriptBlock = { + param($logItems, $options) + + # build array of events + $events = @(foreach ($item in $logItems) { + # build base event object + $evt = @{ + Data = $item.Data + Level = $item.Event.Level + Timestamp = $item.Event.Timestamp.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } + + # add source + $source = Protect-PodeValue -Value $item.Event.Metadata['Source'] -Default $options.Source + if (![string]::IsNullOrEmpty($source)) { + $evt.Source = $source + } + + $evt + }) + + # convert to json and return + return $events | ConvertTo-Json -Compress -Depth 10 + } + + # build headers scriptblock + $headersScriptBlock = { + param($body, $options) + + # the x-ms-date header + $date = [datetime]::Now.ToString('R') + + # build signature string + $contentLength = $body.Length + $method = 'POST' + $contentType = 'application/json' + $dateHeader = "x-ms-date:$($date)" + $urlPath = '/api/logs' + $stringToSign = "$($method)`n$($contentLength)`n$($contentType)`n$($dateHeader)`n$($urlPath)" + + # build authorization header + $bytesToSign = [System.Text.Encoding]::UTF8.GetBytes($stringToSign) + $keyBytes = [System.Convert]::FromBase64String($options.SharedKey) + $hmacsha256 = [System.Security.Cryptography.HMACSHA256]::new() + $hmacsha256.Key = $keyBytes + $signatureBytes = $hmacsha256.ComputeHash($bytesToSign) + $signature = [System.Convert]::ToBase64String($signatureBytes) + $authorizationHeader = "SharedKey $($workspaceId):$($signature)" + + return @{ + 'Authorization' = $authorizationHeader + 'x-ms-date' = $date + } + } + + # default headers + $headers = @{ + 'Log-Type' = $LogType + 'time-generated-field' = 'Timestamp' + } + + # add method to server + $bodyArgs = @{ + Source = $Source + } + + $headerArgs = @{ + SharedKey = $SharedKey + } + + return New-PodeLogApiMethod ` + -Id $Id ` + -Type 'Azure' ` + -BatchInfo $BatchInfo ` + -Url "https://$($WorkspaceId).ods.opinsights.azure.com/api/logs?api-version=2016-04-01" ` + -Headers $headers ` + -HeadersScriptBlock $headersScriptBlock ` + -HeadersArguments $headerArgs ` + -BodyScriptBlock $bodyScriptBlock ` + -BodyArguments $bodyArgs ` + -SkipCertificateCheck:$SkipCertificateCheck.IsPresent ` + -Compress +} + +function New-PodeLogAzureDataCollectionMethod { + [OutputType([string])] + param( + [Parameter()] + [string] + $Id, + + [Parameter(Mandatory = $true)] + [string] + $Endpoint, + + [Parameter(Mandatory = $true)] + [string] + $ImmutableId, + + [Parameter(Mandatory = $true)] + [string] + $StreamName, + + [Parameter(Mandatory = $true)] + [string] + $ClientId, + + [Parameter(Mandatory = $true)] + [string] + $ClientSecret, + + [Parameter(Mandatory = $true)] + [string] + $TenantId, + + [Parameter()] + [string] + $Source, + + [Parameter()] + [hashtable] + $BatchInfo = $null, + + [switch] + $SkipCertificateCheck + ) + + # build body scriptblock + $bodyScriptBlock = { + param($logItems, $options) + + # build array of events + $events = @(foreach ($item in $logItems) { + # build base event object + $evt = @{ + Data = $item.Data + Level = $item.Event.Level + TimeGenerated = $item.Event.Timestamp.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } + + # add source + $source = Protect-PodeValue -Value $item.Event.Metadata['Source'] -Default $options.Source + if (![string]::IsNullOrEmpty($source)) { + $evt.Source = $source + } + + $evt + }) + + # convert to json and return + return $events | ConvertTo-Json -Compress -Depth 10 + } + + # build headers scriptblock + $headersScriptBlock = { + param($body, $options) + + # has the token expired? if so, generate a new one + if ($options.Auth.ExpiryDate -lt [datetime]::Now.AddMinutes(-1)) { + # build payload + $payload = "client_id=$($options.Client.Id)" + $payload += "&client_secret=$([System.Web.HttpUtility]::UrlEncode($options.Client.Secret))" + $payload += "&scope=$([System.Web.HttpUtility]::UrlEncode('https://monitor.azure.com//.default'))" + $payload += '&grant_type=client_credentials' + + # request token + $uri = "https://login.microsoftonline.com/$($options.Client.TenantId)/oauth2/v2.0/token" + $result = Invoke-RestMethod -Method Post -Uri $uri -Body $payload -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop + + # set token and new expiry + $options.Auth.Token = $result.access_token + $options.Auth.ExpiryDate = [datetime]::Now.AddSeconds($result.expires_in) + } + + # return auth header + return @{ + Authorization = "Bearer $($options.Auth.Token)" + } + } + + # add method to server + $bodyArgs = @{ + Source = $Source + } + + $headerArgs = @{ + Client = @{ + Id = $ClientId + Secret = $ClientSecret + TenantId = $TenantId + } + Auth = @{ + Token = $null + ExpiryDate = [datetime]::MinValue + } + } + + return New-PodeLogApiMethod ` + -Id $Id ` + -Type 'Azure' ` + -BatchInfo $BatchInfo ` + -Url "$($Endpoint)/dataCollectionRules/$($ImmutableId)/streams/$($StreamName)?api-version=2023-01-01" ` + -HeadersScriptBlock $headersScriptBlock ` + -HeadersArguments $headerArgs ` + -BodyScriptBlock $bodyScriptBlock ` + -BodyArguments $bodyArgs ` + -SkipCertificateCheck:$SkipCertificateCheck.IsPresent ` + -Compress +} + +function ConvertTo-PodeSyslogLevel { + param( + [Parameter()] + [string] + $Level + ) + + switch ($Level.ToLowerInvariant()) { + 'emergency' { return 0 } + 'alert' { return 1 } + 'critical' { return 2 } + 'error' { return 3 } + 'warning' { return 4 } + 'notice' { return 5 } + 'informational' { return 6 } + 'debug' { return 7 } + default { return 6 } # default to informational + } +} + function ConvertTo-PodeEventViewerLevel { param( [Parameter()] @@ -293,17 +735,77 @@ function ConvertTo-PodeEventViewerLevel { return [System.Diagnostics.EventLogEntryType]::Information } - if ($Level -ieq 'error') { + if ($Level -iin @('emergency', 'alert', 'critical', 'error')) { return [System.Diagnostics.EventLogEntryType]::Error } - if ($Level -ieq 'warning') { + if ($Level -iin @('warning', 'notice')) { return [System.Diagnostics.EventLogEntryType]::Warning } return [System.Diagnostics.EventLogEntryType]::Information } +function ConvertTo-PodeSplunkLevel { + param( + [Parameter()] + [string] + $Level + ) + + if ([string]::IsNullOrWhiteSpace($Level)) { + return 'info' + } + + if ($Level -iin @('emergency', 'alert', 'critical')) { + return 'critical' + } + + if ($Level -ieq 'error') { + return 'error' + } + + if ($Level -iin @('warning', 'notice')) { + return 'warning' + } + + if ($Level -ieq 'debug') { + return 'debug' + } + + return 'info' +} + +function ConvertTo-PodeDatadogLevel { + param( + [Parameter()] + [string] + $Level + ) + + if ([string]::IsNullOrWhiteSpace($Level)) { + return 'INFO' + } + + if ($Level -iin @('emergency', 'alert', 'critical', 'error')) { + return 'ERROR' + } + + if ($Level -iin @('warning', 'notice')) { + return 'WARN' + } + + if ($Level -ieq 'debug') { + return 'DEBUG' + } + + if ($Level -ieq 'verbose') { + return 'TRACE' + } + + return 'INFO' +} + function Get-PodeLoggingInbuiltType { param( [Parameter(Mandatory = $true)] @@ -315,43 +817,44 @@ function Get-PodeLoggingInbuiltType { switch ($Type.ToLowerInvariant()) { 'requests' { $script = { - param($item, $options) - - # safeguard for null or whitespace values - function sg($value) { - if ([string]::IsNullOrWhiteSpace($value)) { - return '-' - } + param( + [Pode.Utilities.Logging.IPodeLogEvent] + $logEvent + ) - return $value + return [ordered]@{ + Host = Protect-PodeLogRequestItem -Value $logEvent.Data.Host + Identifier = Protect-PodeLogRequestItem -Value $logEvent.Data.RfcUserIdentity + User = Protect-PodeLogRequestItem -Value $logEvent.Data.User + Date = Protect-PodeLogRequestItem -Value $logEvent.Data.Date + Method = Protect-PodeLogRequestItem -Value $logEvent.Data.Request.Method + Resource = Protect-PodeLogRequestItem -Value $logEvent.Data.Request.Resource + Protocol = Protect-PodeLogRequestItem -Value $logEvent.Data.Request.Protocol + StatusCode = Protect-PodeLogRequestItem -Value $logEvent.Data.Response.StatusCode + Size = Protect-PodeLogRequestItem -Value $logEvent.Data.Response.Size + Referrer = Protect-PodeLogRequestItem -Value $logEvent.Data.Request.Referrer + UserAgent = Protect-PodeLogRequestItem -Value $logEvent.Data.Request.Agent } - - # build the url with http method - $url = "$(sg $item.Request.Method) $(sg $item.Request.Resource) $(sg $item.Request.Protocol)" - - # build and return the request row - return "$(sg $item.Host) $(sg $item.RfcUserIdentity) $(sg $item.User) [$(sg $item.Date)] `"$($url)`" $(sg $item.Response.StatusCode) $(sg $item.Response.Size) `"$(sg $item.Request.Referrer)`" `"$(sg $item.Request.Agent)`"" } } 'errors' { $script = { - param($item, $options) - - # build the exception details - $row = @( - "Date: $($item.Date.ToString('yyyy-MM-dd HH:mm:ss'))", - "Level: $($item.Level)", - "ThreadId: $($item.ThreadId)", - "ContextId: $($item.ContextId)", - "Server: $($item.Server)", - "Category: $($item.Category)", - "Message: $($item.Message)", - "StackTrace: $($item.StackTrace)" + param( + [Pode.Utilities.Logging.IPodeLogEvent] + $logEvent ) - # join the details and return - return "$($row -join "`n")`n" + return [ordered]@{ + Date = $logEvent.Data.Date.ToString('yyyy-MM-dd HH:mm:ss') + Level = $logEvent.Data.Level + ThreadId = $logEvent.Data.ThreadId + ContextId = $logEvent.Data.ContextId + Server = $logEvent.Data.Server + Category = $logEvent.Data.Category + Message = $logEvent.Data.Message + StackTrace = $logEvent.Data.StackTrace + } } } } @@ -359,6 +862,20 @@ function Get-PodeLoggingInbuiltType { return $script } +function Protect-PodeLogRequestItem { + param( + [Parameter()] + [object] + $Value + ) + + if ([string]::IsNullOrWhiteSpace($Value)) { + return '-' + } + + return $Value +} + function Get-PodeLogType { [OutputType([hashtable])] param( @@ -413,8 +930,8 @@ function Write-PodeRequestLog { # build a request object $item = @{ Host = $Request.Handler.RemoteEndPoint.Address.IPAddressToString - RfcUserIdentity = '-' - User = '-' + RfcUserIdentity = $null + User = $null Date = [DateTime]::Now.ToString('dd/MMM/yyyy:HH:mm:ss zzz') UtcDate = [DateTime]::UtcNow Request = @{ @@ -422,7 +939,7 @@ function Write-PodeRequestLog { Hostname = $Request.Host.ToLowerInvariant() Scheme = $Request.Handler.Scheme.ToLowerInvariant() Resource = $Path - Query = (Protect-PodeValue -Value $Request.Url.Query -Default '-').TrimStart('?') + Query = (Protect-PodeValue -Value $Request.Url.Query -Default ([string]::Empty)).TrimStart('?') Protocol = "HTTP/$($Request.ProtocolVersion)" Referrer = $Request.UrlReferrer Agent = $Request.UserAgent @@ -430,7 +947,7 @@ function Write-PodeRequestLog { Response = @{ StatusCode = $Response.StatusCode StatusDescription = $Response.StatusDescription - Size = '-' + Size = $null } } @@ -441,7 +958,7 @@ function Write-PodeRequestLog { # set username - dot spaces if (Test-PodeAuthUser -IgnoreSession) { - $userProps = (Get-PodeLogType -Name ([PodeLogger]::REQUEST_LOG_TYPE_NAME)).Properties.Username.Split('.') + $userProps = (Get-PodeLogType -Name ([PodeLogger]::REQUEST_LOG_TYPE_NAME)).Metadata.Username.Split('.') $user = $WebEvent.Auth.User foreach ($atom in $userProps) { @@ -454,7 +971,8 @@ function Write-PodeRequestLog { } # add the item to be processed - $PodeContext.Server.Logging.Logger.Add([PodeLogger]::REQUEST_LOG_TYPE_NAME, [PodeLogLevel]::Informational, $item) + $logEvent = [PodeLogEvent]::new([PodeLogger]::REQUEST_LOG_TYPE_NAME, [PodeLogLevel]::Informational, $item) + $PodeContext.Server.Logging.Logger.Add($logEvent) } function Add-PodeRequestLogEndware { @@ -486,7 +1004,7 @@ function Test-PodeLogTypesExist { } function Start-PodeLoggingRunspace { - # skip if there are no log types configured, or logging is disabled + # skip if there are no Log Types configured, or logging is disabled if (!(Test-PodeLogTypesExist)) { return } @@ -502,58 +1020,99 @@ function Start-PodeLoggingRunspace { try { # try and remove an event from the queue, if none check batches then continue - $log = $null - $found = $PodeContext.Server.Logging.Logger.TryTake([ref]$log, $PodeContext.Tokens.Cancellation.Token) + $logEvent = $null + $found = $PodeContext.Server.Logging.Logger.TryTake([ref]$logEvent, $PodeContext.Tokens.Cancellation.Token) - if (!$found -or ($null -eq $log)) { + if (!$found -or ($null -eq $logEvent)) { Test-PodeLogTypeBatchTimeout continue } # run the log item through the appropriate method - $logType = Get-PodeLogType -Name $log.Name + $logType = Get-PodeLogType -Name $logEvent.Name if ($null -eq $logType) { continue } - $now = [datetime]::Now - - # transform the log item into a writeable format + # transform the log item into a message item, unless the user wants the raw data to be passed to the Log Method if ($logType.Raw) { - $result = $log.Item + $result = $logEvent.Data } else { - $_args = @($log.Item) + @($logType.Arguments) - $result = @(Invoke-PodeScriptBlock -ScriptBlock $logType.ScriptBlock -Arguments $_args -UsingVariables $logType.UsingVariables -Return -Splat) + # what version of the Log Type are we using? (v1 = data only, v2 = full log event) + switch ($logType.Version) { + 1 { + $_args = @($logEvent.Data) + @($logType.Arguments) + } + + 2 { + $_args = @($logEvent) + @($logType.Arguments) + } + } + + # transform the log event + $result = Invoke-PodeScriptBlock -ScriptBlock $logType.ScriptBlock -Arguments $_args -UsingVariables $logType.UsingVariables -Return -Splat } + # little sleep if no transform result, to help lower cpu if ($null -eq $result) { Start-Sleep -Milliseconds 100 continue } - # loop through each log method available to the log type + # transform the result into a specified serialisation format (None just leaves "result" as it - no serialising) + switch ($logType.Options.Formatting.Serialise.Type) { + 'Json' { + $result = $result | ConvertTo-Json -Depth 10 -Compress + } + + 'Xml' { + $result = $result | ConvertTo-PodeXml -Depth 10 -RootName $logType.Options.Formatting.Serialise.XmlRootName -Compress + } + + 'Yaml' { + $result = $result | ConvertTo-PodeYaml -Depth 10 -StringWrapLength 0 + } + + 'Custom' { + $_args = @($result) + @($logEvent) + @($logType.Arguments) + $result = Invoke-PodeScriptBlock -ScriptBlock $logType.Options.Formatting.Serialise.ScriptBlock -Arguments $_args -UsingVariables $logType.Options.Formatting.Serialise.UsingVariables -Return -Splat + } + } + + # transform the result to syslog or other log formats (None just leaves "result" as it - no formatting + switch ($logType.Options.Formatting.Log.Format) { + 'Syslog' { + $params = @{ + Message = $result + Level = $logEvent.Level + Timestamp = $logEvent.Timestamp + AppName = $logType.Options.Formatting.SyslogInfo.AppName + Tags = $logType.Options.Formatting.SyslogInfo.Tags + Format = $logType.Options.Formatting.SyslogInfo.Format + Facility = $logType.Options.Formatting.SyslogInfo.Facility + } + $result = ConvertTo-PodeSyslog @params + } + + 'Custom' { + $_args = @($result) + @($logEvent) + @($logType.Arguments) + $result = Invoke-PodeScriptBlock -ScriptBlock $logType.Options.Formatting.Log.ScriptBlock -Arguments $_args -UsingVariables $logType.Options.Formatting.Log.UsingVariables -Return -Splat + } + } + + # loop through each Log Method available to the Log Type foreach ($logMethodId in $logType.Method) { $logMethod = Get-PodeLogMethod -Id $logMethodId $batch = $logMethod.Batch - if ($batch.Size -gt 1) { - # add current item to batch - $batch.Items += $result - $batch.RawItems += $log.Item - $batch.LastUpdate = $now - - # if the current amount of items matches the batch, send to log method and reset batch - if ($batch.Items.Length -ge $batch.Size) { - $logMethod.Queue.Add([Pode.Utilities.Logging.PodeLogItem]::new($batch.Items, $batch.RawItems)) - $batch.Items = @() - $batch.RawItems = @() - } - } + # add current item to batch + $batch.Items.Add([Pode.Utilities.Logging.PodeLogItem]::new($result, $logEvent)) - # send log message to log method - else { - $logMethod.Queue.Add([Pode.Utilities.Logging.PodeLogItem]::new($result, $log.Item)) + # if the batch is full, send to Log Method and reset + if ($batch.Items.IsFull) { + $logMethod.Queue.Add($batch.Items) + $batch.Items = [Pode.Utilities.Logging.PodeLogItemCollection]::new($batch.Items.MaxCount, $batch.Items.Timeout) } } @@ -565,7 +1124,6 @@ function Start-PodeLoggingRunspace { } catch { $_ | Out-Default - $_ | Write-PodeErrorLog } } } @@ -573,13 +1131,13 @@ function Start-PodeLoggingRunspace { $_ | Write-PodeErrorLog -Level Debug } catch { - $_ | Write-PodeErrorLog + $_ | Out-Default throw } } - # create and start log method runspaces - Write-Verbose 'Starting log method runspaces...' + # create and start Log Method runspaces + Write-Verbose 'Starting Log Method runspaces...' $null = $PodeContext.RunspacePools.Logs.Pool.SetMaxRunspaces($PodeContext.Server.Logging.Methods.Count + 1) foreach ($methodId in $PodeContext.Server.Logging.Methods.Keys) { @@ -615,18 +1173,18 @@ function Add-PodeLogMethod { # check if method already exists if (Test-PodeLogMethod -Id $Id) { - # A logging method with the same ID already exists + # A Log Method with the same ID already exists throw ($PodeLocale.loggingMethodAlreadyDefinedExceptionMessage -f $Id) } - # empty list of log types for later associations + # empty list of Log Types for later associations $Metadata.Types = @() # add batching info to metadata $Metadata.Batch = $BatchInfo | New-PodeLogBatchConfig # create queue for the method's log items - $Metadata.Queue = [Pode.Utilities.Logging.PodeLogQueue[Pode.Utilities.Logging.IPodeLogItem]]::new() + $Metadata.Queue = [Pode.Utilities.Logging.PodeLogQueue[Pode.Utilities.Logging.IPodeLogItemCollection]]::new() # add method to server $PodeContext.Server.Logging.Methods[$Id] = $Metadata @@ -690,29 +1248,22 @@ function Unregister-PodeLogMethodFromType { } function Test-PodeLogTypeBatchTimeout { - $now = [datetime]::Now - - # check each log Type, and see if its batch needs to be outputted due to timeout + # check each Log Type, and see if its batch needs to be outputted due to timeout foreach ($logType in $PodeContext.Server.Logging.Types.Values) { foreach ($logMethodId in $logType.Method) { $logMethod = Get-PodeLogMethod -Id $logMethodId $batch = $logMethod.Batch - # do nothing if not batching, or no items - if (($batch.Size -le 1) -or ($batch.Timeout -le 0) -or ($batch.Items.Length -eq 0)) { - continue - } - # do nothing if the batch timeout hasn't been reached - if (($null -eq $batch.LastUpdate) -or ($batch.LastUpdate.AddSeconds($batch.Timeout) -gt $now)) { + if (!$batch.Items.HasTimedOut) { continue } - # send batch to log method and reset batch - $logMethod.Queue.Add([Pode.Utilities.Logging.PodeLogItem]::new($batch.Items, $batch.RawItems)) + # send batch to Log Method and reset batch + $logMethod.Queue.Add($batch.Items) - $batch.Items = @() - $batch.RawItems = @() + # reset the batch + $batch.Items = [Pode.Utilities.Logging.PodeLogItemCollection]::new($batch.Items.MaxCount, $batch.Items.Timeout) } } } @@ -729,11 +1280,7 @@ function New-PodeLogBatchConfig { } return @{ - Size = $BatchInfo.Size - Timeout = $BatchInfo.Timeout - LastUpdate = $null - Items = @() - RawItems = @() + Items = [PodeLogItemCollection]::new($BatchInfo.Size, $BatchInfo.Timeout) } } @@ -744,7 +1291,7 @@ function Close-PodeLogging { $PodeContext.Server.Logging.Logger = $null } - # Dispose log method queues + # Dispose Log Method queues foreach ($method in $PodeContext.Server.Logging.Methods.Values) { if ($null -ne $method.Queue) { $method.Queue.Dispose() diff --git a/src/Private/SSE.ps1 b/src/Private/SSE.ps1 index b0176372f..932f7b20a 100644 --- a/src/Private/SSE.ps1 +++ b/src/Private/SSE.ps1 @@ -7,7 +7,7 @@ function Invoke-PodeSseEvent { $Name, # Name of the SSE connection [Parameter(Mandatory = $true)] - [PodeClientConnectionEventType] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType] $Type, [Parameter(Mandatory = $true)] diff --git a/src/Private/Signals.ps1 b/src/Private/Signals.ps1 index c529097c9..5aebbf97b 100644 --- a/src/Private/Signals.ps1 +++ b/src/Private/Signals.ps1 @@ -7,7 +7,7 @@ function Invoke-PodeSignalEvent { $Name, # Name of the Signal connection [Parameter(Mandatory = $true)] - [PodeClientConnectionEventType] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType] $Type, [Parameter(Mandatory = $true)] diff --git a/src/Public/Core.ps1 b/src/Public/Core.ps1 index 219e18852..2d8d10c94 100644 --- a/src/Public/Core.ps1 +++ b/src/Public/Core.ps1 @@ -23,6 +23,9 @@ using namespace Pode.Utilities .PARAMETER Name An optional name for the server, useful for identification in logs and future extensions. +.PARAMETER AppName + An optional application name for the server, which can be used in logging and other contexts. + .PARAMETER Threads The number of threads to allocate for Web, SMTP, and TCP servers. Defaults to 1. @@ -126,6 +129,10 @@ function Start-PodeServer { [string] $Name, + [Parameter()] + [string] + $AppName, + [Parameter()] [int] $Threads = 1, @@ -265,6 +272,8 @@ function Start-PodeServer { IgnoreServerConfig = $IgnoreServerConfig ConfigFile = $ConfigFile Daemon = $Daemon + Name = $Name + AppName = $AppName } # Create main context object @@ -1114,7 +1123,7 @@ function Get-PodeServerState { function Test-PodeServerState { param( [Parameter(Mandatory = $true)] - [PodeServerState] + [Pode.Utilities.PodeServerState] $State ) diff --git a/src/Public/Events.ps1 b/src/Public/Events.ps1 index f83ce18cb..9b4352ca5 100644 --- a/src/Public/Events.ps1 +++ b/src/Public/Events.ps1 @@ -26,7 +26,7 @@ function Register-PodeEvent { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - [PodeServerEventType] + [Pode.Utilities.PodeServerEventType] $Type, [Parameter(Mandatory = $true)] @@ -81,7 +81,7 @@ function Unregister-PodeEvent { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - [PodeServerEventType] + [Pode.Utilities.PodeServerEventType] $Type, [Parameter(Mandatory = $true)] @@ -216,7 +216,7 @@ function Clear-PodeEvent { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - [PodeServerEventType] + [Pode.Utilities.PodeServerEventType] $Type ) diff --git a/src/Public/Logging.ps1 b/src/Public/Logging.ps1 index 2aff71fb5..114361f1e 100644 --- a/src/Public/Logging.ps1 +++ b/src/Public/Logging.ps1 @@ -5,15 +5,15 @@ using namespace Pode.Utilities.Logging Create a new method of outputting logs. .DESCRIPTION -This function has been deprecated and will be removed in future versions. It creates various logging methods for outputting logs. -Please use the appropriate new functions for each logging method: +This function has been deprecated and will be removed in future versions. It creates various Log Methods for outputting logs. +Please use the appropriate new functions for each Log Method: - New-PodeLogTerminalMethod - New-PodeLogFileMethod - New-PodeLogEventViewerMethod - New-PodeLogCustomMethod .PARAMETER Id -An optional ID to assign to the logging method. If not supplied, a random ID will be generated. +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. .PARAMETER Terminal If supplied, will use the inbuilt Terminal logging output method. @@ -151,7 +151,7 @@ function New-PodeLoggingMethod { # batch details $batchInfo = New-PodeLogBatchInfo -Size $Batch -Timeout $BatchTimeout - # return info on appropriate logging type + # return info on appropriate Log Type switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) { 'terminal' { # WARNING: Function `New-PodeLoggingMethod` is deprecated. @@ -191,19 +191,61 @@ Enables Request Logging using a supplied output method. Enables Request Logging using a supplied output method. .PARAMETER Method -The logging Method ID to use for output the log entry. +One or more Log Method IDs to use for outputting the log entry. .PARAMETER UsernameProperty An optional property path within the $WebEvent.Auth.User object for the user's Username. (Default: Username). +.PARAMETER LogFormat +The format to use for the log output. (Default: Server default, or 'None' if no default set). + +.PARAMETER LogScriptBlock +A ScriptBlock to use for custom log formatting of the log entry. + +.PARAMETER SerialiseFormat +Specifies the format to use for serialising the log entry. (Default: Server default, or 'Custom' if no default set, or 'None' if Raw is supplied). + +.PARAMETER SerialiseScriptBlock +A ScriptBlock to use for custom serialisation of the log entry. + +.PARAMETER ScriptBlock +A ScriptBlock to use for custom data selection and transforming of the log entry. (Default: inbuilt logic). + +.PARAMETER ArgumentList +An array of arguments to supply to the Custom Log Type's ScriptBlock and SerialiseScriptBlock. + +.PARAMETER SyslogInfo +A hashtable containing Syslog configuration information, built using New-PodeSyslogInfo. + .PARAMETER Raw If supplied, the log item returned will be the raw Request item as a hashtable and not a string (for Custom methods). .EXAMPLE -New-PodeLogTerminalMethod | Enable-PodeRequestLogType +New-PodeLogTerminalMethod | Enable-PodeLogRequestType + +.EXAMPLE +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -SerialiseFormat 'Json' + +.EXAMPLE +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -SerialiseFormat 'Custom' -SerialiseScriptBlock { + param($data) + return "$($data.Host) $($data.Identifier) $($data.User) [$($data.Date)] `"$($data.RequestLine)`" $($data.StatusCode) $($data.Size) `"$($data.Referrer)`" `"$($data.UserAgent)`"" +} + +.EXAMPLE +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -ScriptBlock { + param($logEvent) + return @{ + HttpMethod = $logEvent.Data.HttpMethod + } +} + +.EXAMPLE +$syslogInfo = New-PodeLogSyslogInfo -Format RFC3164 +New-PodeLogTerminalMethod | Enable-PodeLogRequestType -LogFormat Syslog -SyslogInfo $syslogInfo #> -function Enable-PodeRequestLogType { - [CmdletBinding()] +function Enable-PodeLogRequestType { + [CmdletBinding(DefaultParameterSetName = 'ScriptBlock')] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string[]] @@ -213,6 +255,35 @@ function Enable-PodeRequestLogType { [string] $UsernameProperty, + [Parameter()] + [Pode.Utilities.Logging.PodeLogFormat] + $LogFormat = 'None', + + [Parameter()] + [scriptblock] + $LogScriptBlock, + + [Parameter()] + [Pode.Utilities.Logging.PodeSerialiseFormat] + $SerialiseFormat = 'Custom', + + [Parameter()] + [scriptblock] + $SerialiseScriptBlock, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [scriptblock] + $ScriptBlock, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [object[]] + $ArgumentList, + + [Parameter()] + [hashtable] + $SyslogInfo, + + [Parameter(ParameterSetName = 'Raw')] [switch] $Raw ) @@ -220,23 +291,12 @@ function Enable-PodeRequestLogType { begin { # error if it's already enabled $name = [PodeLogger]::REQUEST_LOG_TYPE_NAME - if ($PodeContext.Server.Logging.Types.Contains($name)) { - # Request Logging has already been enabled - throw ($PodeLocale.requestLoggingAlreadyEnabledExceptionMessage) - } - # setup array for log methods being piped in + # setup array for Log Methods being piped in $pipelineMethods = @() } process { - # ensure the Method exists - if (!(Test-PodeLogMethod -Id $_)) { - # The supplied logging Method for Request Logging doesn't exist - throw ($PodeLocale.loggingMethodDoesNotExistExceptionMessage -f $_) - } - - # add to pipeline methods array $pipelineMethods += $_ } @@ -246,45 +306,89 @@ function Enable-PodeRequestLogType { $Method = $pipelineMethods } + # base params for adding the Request Log Type + $params = @{ + Name = $name + Method = $Method + Levels = @('Informational') + LogScriptBlock = $LogScriptBlock + SyslogInfo = $SyslogInfo + XmlRootName = 'Request' + } + # username property if ([string]::IsNullOrWhiteSpace($UsernameProperty)) { $UsernameProperty = 'Username' } + $params['Metadata'] = @{ + Username = $UsernameProperty + } + + # add LogFormat if supplied + if ($PSBoundParameters.ContainsKey('LogFormat')) { + $params['LogFormat'] = $LogFormat + } + + # add SerialiseFormat if supplied, otherwise use local default + if ($PSBoundParameters.ContainsKey('SerialiseFormat')) { + $params['SerialiseFormat'] = $SerialiseFormat + } + else { + $defSerialiseFormat = Get-PodeLogDefaultSerialiseFormat + if ($null -eq $defSerialiseFormat) { + if ($Raw) { + $params['SerialiseFormat'] = 'None' + } + else { + $params['SerialiseFormat'] = $SerialiseFormat + } + } + } - # add the request log type, associated with the supplied log method(s) - $PodeContext.Server.Logging.Types[$name] = @{ - Method = $Method - ScriptBlock = Get-PodeLoggingInbuiltType -Type Requests - Raw = $Raw.IsPresent - Properties = @{ - Username = $UsernameProperty + # add SerialiseScriptBlock if supplied, otherwise use local default + if ($null -eq $SerialiseScriptBlock) { + $SerialiseScriptBlock = { + param($data) + $reqLine = "$($data.Method) $($data.Resource) $($data.Protocol)" + return "$($data.Host) $($data.Identifier) $($data.User) [$($data.Date)] `"$($reqLine)`" $($data.StatusCode) $($data.Size) `"$($data.Referrer)`" `"$($data.UserAgent)`"" } - Arguments = @{} } - $PodeContext.Server.Logging.Logger.RegisterType([PodeLogType]::new($name, @([PodeLogLevel]::Informational))) + $params['SerialiseScriptBlock'] = $SerialiseScriptBlock - # then associate the supplied log method(s) with the request log type - foreach ($methodId in $Method) { - Register-PodeLogTypeToMethod -TypeName $name -MethodId $methodId + # do we only want the raw data? + if ($Raw) { + $params['Raw'] = $true + } + + # if not, add the scriptblock for transforming request data + else { + if ($null -eq $ScriptBlock) { + $ScriptBlock = Get-PodeLoggingInbuiltType -Type Requests + } + $params['ScriptBlock'] = $ScriptBlock + $params['ArgumentList'] = $ArgumentList + $params['Version'] = 2 } + + Add-PodeLogType @params } } if (!(Test-Path Alias:Enable-PodeRequestLogging)) { - New-Alias Enable-PodeRequestLogging -Value Enable-PodeRequestLogType + New-Alias Enable-PodeRequestLogging -Value Enable-PodeLogRequestType } <# .SYNOPSIS -Disables Request log Type. +Disables Request Log Type. .DESCRIPTION -Disables Request log Type. +Disables Request Log Type. .EXAMPLE -Disable-PodeRequestLogType +Disable-PodeLogRequestType #> -function Disable-PodeRequestLogType { +function Disable-PodeLogRequestType { [CmdletBinding()] param() @@ -292,64 +396,124 @@ function Disable-PodeRequestLogType { } if (!(Test-Path Alias:Disable-PodeRequestLogging)) { - New-Alias Disable-PodeRequestLogging -Value Disable-PodeRequestLogType + New-Alias Disable-PodeRequestLogging -Value Disable-PodeLogRequestType } <# .SYNOPSIS -Enables Error log Type using a supplied log Method. +Enables Error Log Type using a supplied Log Method. .DESCRIPTION -Enables Error log Type using a supplied log Method. +Enables Error Log Type using a supplied Log Method. .PARAMETER Method -The log Method ID to use for output the log entry. +One or more Log Method IDs to use for outputting the log entry. .PARAMETER Levels -The Levels of errors that should be logged (Default: Error) +The Levels of errors that should be logged (Default: Emergency, Alert, Critical, Error) + +.PARAMETER LogFormat +The format to use for the log output. (Default: Server default, or 'None' if no default set). + +.PARAMETER LogScriptBlock +A ScriptBlock to use for custom log formatting of the log entry. + +.PARAMETER SerialiseFormat +Specifies the format to use for serialising the log entry. (Default: Server default, or 'Custom' if no default set, or 'None' if Raw is supplied). + +.PARAMETER SerialiseScriptBlock +A ScriptBlock to use for custom serialisation of the log entry. + +.PARAMETER ScriptBlock +A ScriptBlock to use for custom data selection and transforming of the log entry. (Default: inbuilt logic). + +.PARAMETER ArgumentList +An array of arguments to supply to the Custom Log Type's ScriptBlock and SerialiseScriptBlock. + +.PARAMETER SyslogInfo +A hashtable containing Syslog configuration information, built using New-PodeSyslogInfo. .PARAMETER Raw If supplied, the log item returned will be the raw Error item as a hashtable and not a string (for Custom methods). .EXAMPLE -New-PodeLogTerminalMethod | Enable-PodeErrorLogType +New-PodeLogTerminalMethod | Enable-PodeLogErrorType + +.EXAMPLE +New-PodeLogTerminalMethod | Enable-PodeLogErrorType -SerialiseFormat 'Json' + +.EXAMPLE +New-PodeLogTerminalMethod | Enable-PodeLogErrorType -SerialiseFormat 'Custom' -SerialiseScriptBlock { + param($data) + return $data | ConvertTo-PodeString +} + +.EXAMPLE +New-PodeLogTerminalMethod | Enable-PodeLogErrorType -ScriptBlock { + param($logEvent) + return @{ + StackTrace = $logEvent.Data.StackTrace + } +} + +.EXAMPLE +$syslogInfo = New-PodeLogSyslogInfo -Format RFC3164 +New-PodeLogTerminalMethod | Enable-PodeLogErrorType -LogFormat Syslog -SyslogInfo $syslogInfo #> -function Enable-PodeErrorLogType { - [CmdletBinding()] +function Enable-PodeLogErrorType { + [CmdletBinding(DefaultParameterSetName = 'ScriptBlock')] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string[]] $Method, [Parameter()] - [ValidateSet('Error', 'Warning', 'Informational', 'Verbose', 'Debug', '*')] + [ValidateSet('Emergency', 'Alert', 'Critical', 'Error', 'Warning', 'Notice', 'Informational', 'Verbose', 'Debug', '*')] [string[]] - $Levels = @('Error'), + $Levels = @('Emergency', 'Alert', 'Critical', 'Error'), + + [Parameter()] + [Pode.Utilities.Logging.PodeLogFormat] + $LogFormat = 'None', + + [Parameter()] + [scriptblock] + $LogScriptBlock, + + [Parameter()] + [Pode.Utilities.Logging.PodeSerialiseFormat] + $SerialiseFormat = 'Custom', + + [Parameter()] + [scriptblock] + $SerialiseScriptBlock, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [scriptblock] + $ScriptBlock, + + [Parameter(ParameterSetName = 'ScriptBlock')] + [object[]] + $ArgumentList, + + [Parameter()] + [hashtable] + $SyslogInfo, + [Parameter(ParameterSetName = 'Raw')] [switch] $Raw ) begin { - # error if it's already enabled + # get error log type name $name = [PodeLogger]::ERROR_LOG_TYPE_NAME - if ($PodeContext.Server.Logging.Types.Contains($name)) { - # Error Logging has already been enabled - throw ($PodeLocale.errorLoggingAlreadyEnabledExceptionMessage) - } - # setup array for log methods being piped in + # setup array for Log Methods being piped in $pipelineMethods = @() } process { - # ensure the Method exists - if (!(Test-PodeLogMethod -Id $_)) { - # The supplied logging Method for Request Logging doesn't exist - throw ($PodeLocale.loggingMethodDoesNotExistExceptionMessage -f $_) - } - - # add to pipeline methods array $pipelineMethods += $_ } @@ -359,43 +523,84 @@ function Enable-PodeErrorLogType { $Method = $pipelineMethods } - # all errors? - if ($Levels -contains '*') { - $Levels = @('Error', 'Warning', 'Informational', 'Verbose', 'Debug') + # base params for adding the Error Log Type + $params = @{ + Name = $name + Method = $Method + Levels = $Levels + LogScriptBlock = $LogScriptBlock + SyslogInfo = $SyslogInfo + XmlRootName = 'Error' } - # add the error log type, associated with the supplied log method(s) - $PodeContext.Server.Logging.Types[$name] = @{ - Method = $Method - ScriptBlock = Get-PodeLoggingInbuiltType -Type Errors - Levels = $Levels - Raw = $Raw.IsPresent - Arguments = @{} + # add LogFormat if supplied + if ($PSBoundParameters.ContainsKey('LogFormat')) { + $params['LogFormat'] = $LogFormat } - $PodeContext.Server.Logging.Logger.RegisterType([PodeLogType]::new($name, $Levels)) - # then associate the supplied log method(s) with the request log type - foreach ($methodId in $Method) { - Register-PodeLogTypeToMethod -TypeName $name -MethodId $methodId + # add SerialiseFormat if supplied, otherwise use local default + if ($PSBoundParameters.ContainsKey('SerialiseFormat')) { + $params['SerialiseFormat'] = $SerialiseFormat + } + else { + $defSerialiseFormat = Get-PodeLogDefaultSerialiseFormat + if ($null -eq $defSerialiseFormat) { + if ($Raw) { + $params['SerialiseFormat'] = 'None' + } + else { + $params['SerialiseFormat'] = $SerialiseFormat + } + } + } + + # add SerialiseScriptBlock if supplied, otherwise use local default + if ($null -eq $SerialiseScriptBlock) { + $SerialiseScriptBlock = { + param($data) + $msg = @(foreach ($key in $data.Keys) { + "$($key): $($data[$key])" + }) -join "`n" + + return "$($msg)`n" + } + } + $params['SerialiseScriptBlock'] = $SerialiseScriptBlock + + # do we only want the raw data? + if ($Raw) { + $params['Raw'] = $true + } + + # if not, add the scriptblock for transforming error data + else { + if ($null -eq $ScriptBlock) { + $ScriptBlock = Get-PodeLoggingInbuiltType -Type Errors + } + $params['ScriptBlock'] = $ScriptBlock + $params['ArgumentList'] = $ArgumentList + $params['Version'] = 2 } + + Add-PodeLogType @params } } if (!(Test-Path Alias:Enable-PodeErrorLogging)) { - New-Alias Enable-PodeErrorLogging -Value Enable-PodeErrorLogType + New-Alias Enable-PodeErrorLogging -Value Enable-PodeLogErrorType } <# .SYNOPSIS -Disables Error log Type. +Disables Error Log Type. .DESCRIPTION -Disables Error log Type. +Disables Error Log Type. .EXAMPLE -Disable-PodeErrorLogType +Disable-PodeLogErrorType #> -function Disable-PodeErrorLogType { +function Disable-PodeLogErrorType { [CmdletBinding()] param() @@ -403,21 +608,21 @@ function Disable-PodeErrorLogType { } if (!(Test-Path Alias:Disable-PodeErrorLogging)) { - New-Alias Disable-PodeErrorLogging -Value Disable-PodeErrorLogType + New-Alias Disable-PodeErrorLogging -Value Disable-PodeLogErrorType } <# .SYNOPSIS -Adds a custom Logging type for parsing custom log items. +Adds a custom Log Type for parsing custom log items. .DESCRIPTION -Adds a custom Logging type for parsing custom log items. +Adds a custom Log Type for parsing custom log items. .PARAMETER Name -A unique Name for the Log type. +A unique Name for the Log Type. .PARAMETER Method -The logging Method ID to use for outputting the log entry. +The Log Method ID to use for outputting the log entry. .PARAMETER ScriptBlock The ScriptBlock defining logic that transforms an item, and returns it for outputting. @@ -426,7 +631,37 @@ The ScriptBlock defining logic that transforms an item, and returns it for outpu The Levels of log items that should be logged. (Default: Informational) .PARAMETER ArgumentList -An array of arguments to supply to the Custom Log type's ScriptBlock. +An array of arguments to supply to the Custom Log Type's ScriptBlock and SerialiseScriptBlock. + +.PARAMETER Version +The version of the Log Type, this determines the arguments which are supplied to the ScriptBlock. (Default: 1) +Arguments supplied depending on the version: + +- Version 1: Log Event Data, ArgumentList +- Version 2: Log Event, ArgumentList + +Under Version 2, the "Log Event" contains references to the log event's data, level, timestamp, and metadata. + +.PARAMETER LogFormat +The format to use for the log output. (Default: Server default, or 'None' if no default set). + +.PARAMETER LogScriptBlock +A ScriptBlock to use for custom log formatting of the log entry. + +.PARAMETER SerialiseFormat +Specifies the format to use for serialising the log entry. (Default: Server default, or 'None' if no default set). + +.PARAMETER SerialiseScriptBlock +A ScriptBlock defining custom logic for serialising the log data. + +.PARAMETER SyslogInfo +A hashtable containing Syslog configuration information, built using New-PodeSyslogInfo. + +.PARAMETER XmlRootName +An optional name to use for the root element of the log item when serialising to XML. + +.PARAMETER Metadata +A hashtable of metadata to associate with the Log Type. This data can be retrieved via Get-PodeLogType. .PARAMETER Raw If supplied, the log item returned will be the raw Request item as a hashtable and not a string. @@ -436,6 +671,15 @@ New-PodeLogTerminalMethod | Add-PodeLogType -Name 'LogTypeName' -ScriptBlock { / .EXAMPLE New-PodeLogTerminalMethod | Add-PodeLogType -Name 'LogTypeName' -Raw + +.EXAMPLE +New-PodeLogTerminalMethod | Add-PodeLogType -Name 'LogTypeName' -SerialiseFormat 'Json' + +.EXAMPLE +New-PodeLogTerminalMethod | Add-PodeLogType -Name 'LogTypeName' -SerialiseFormat 'Custom' -SerialiseScriptBlock { + param($data) + return $data | ConvertTo-PodeString +} #> function Add-PodeLogType { [CmdletBinding(DefaultParameterSetName = 'ScriptBlock')] @@ -451,7 +695,7 @@ function Add-PodeLogType { [Parameter(Mandatory = $true, ParameterSetName = 'ScriptBlock')] [ValidateScript({ if (Test-PodeIsEmpty $_) { - # A non-empty ScriptBlock is required for the logging method + # A non-empty ScriptBlock is required for the Log Method throw ($PodeLocale.nonEmptyScriptBlockRequiredForLoggingMethodExceptionMessage) } @@ -461,7 +705,7 @@ function Add-PodeLogType { $ScriptBlock, [Parameter()] - [ValidateSet('Error', 'Warning', 'Informational', 'Verbose', 'Debug', '*')] + [ValidateSet('Emergency', 'Alert', 'Critical', 'Error', 'Warning', 'Notice', 'Informational', 'Verbose', 'Debug', '*')] [string[]] $Levels = @('Informational'), @@ -469,6 +713,39 @@ function Add-PodeLogType { [object[]] $ArgumentList, + [Parameter(ParameterSetName = 'ScriptBlock')] + [ValidateSet(1, 2)] + [int] + $Version = 1, + + [Parameter()] + [Pode.Utilities.Logging.PodeLogFormat] + $LogFormat = 'None', + + [Parameter()] + [scriptblock] + $LogScriptBlock, + + [Parameter()] + [Pode.Utilities.Logging.PodeSerialiseFormat] + $SerialiseFormat = 'None', + + [Parameter()] + [scriptblock] + $SerialiseScriptBlock, + + [Parameter()] + [hashtable] + $SyslogInfo, + + [Parameter()] + [string] + $XmlRootName, + + [Parameter()] + [hashtable] + $Metadata, + [Parameter(ParameterSetName = 'Raw')] [switch] $Raw @@ -477,22 +754,15 @@ function Add-PodeLogType { begin { # ensure the name doesn't already exist if ($PodeContext.Server.Logging.Types.ContainsKey($Name)) { - # Logging method already defined + # Log Method already defined throw ($PodeLocale.loggingMethodAlreadyDefinedExceptionMessage -f $Name) } - # setup array for log methods being piped in + # setup array for Log Methods being piped in $pipelineMethods = @() } process { - # ensure the Method exists - if (!(Test-PodeLogMethod -Id $_)) { - # The supplied logging Method for Request Logging doesn't exist - throw ($PodeLocale.loggingMethodDoesNotExistExceptionMessage -f $_) - } - - # add to pipeline methods array $pipelineMethods += $_ } @@ -502,28 +772,90 @@ function Add-PodeLogType { $Method = $pipelineMethods } + # ensure the methods exists + foreach ($methodId in $Method) { + if (!(Test-PodeLogMethod -Id $methodId)) { + # The supplied Log Method for Custom Logging doesn't exist + throw ($PodeLocale.loggingMethodDoesNotExistExceptionMessage -f $methodId) + } + } + # all errors? if ($Levels -contains '*') { - $Levels = @('Error', 'Warning', 'Informational', 'Verbose', 'Debug') + $Levels = @('Emergency', 'Alert', 'Critical', 'Error', 'Warning', 'Notice', 'Informational', 'Verbose', 'Debug') } - # check for scoped vars + # default log formatter + if (!$PSBoundParameters.ContainsKey('LogFormat')) { + $LogFormat = Protect-PodeValue -Value (Get-PodeLogDefaultFormat) -Default $LogFormat -EnumType ([PodeLogFormat]) + } + + # do we need default syslog info? + if (($LogFormat -eq [PodeLogFormat]::Syslog) -and ($null -eq $SyslogInfo)) { + $SyslogInfo = New-PodeLogSyslogInfo + } + + # are we using a custom log scriptblock? + if ($LogFormat -eq [PodeLogFormat]::Custom) { + if ($null -eq $LogScriptBlock) { + # A non-empty ScriptBlock is required for the Custom log format + throw ($PodeLocale.nonEmptyScriptBlockRequiredForCustomLogExceptionMessage) + } + + $LogScriptBlock, $logUsingVars = Convert-PodeScopedVariables -ScriptBlock $LogScriptBlock -PSSession $PSCmdlet.SessionState + } + + # default serialise formatter + if (!$PSBoundParameters.ContainsKey('SerialiseFormat')) { + $SerialiseFormat = Protect-PodeValue -Value (Get-PodeLogDefaultSerialiseFormat) -Default $SerialiseFormat -EnumType ([PodeSerialiseFormat]) + } + + # if custom serialisation, ensure we have a scriptblock, and check for scoped vars in scriptblock + if ($SerialiseFormat -eq [PodeSerialiseFormat]::Custom) { + if ($null -eq $SerialiseScriptBlock) { + # A non-empty ScriptBlock is required for the Custom logging serialisation format + throw ($PodeLocale.nonEmptyScriptBlockRequiredForCustomSerialisationExceptionMessage) + } + + $SerialiseScriptBlock, $serialiseUsingVars = Convert-PodeScopedVariables -ScriptBlock $SerialiseScriptBlock -PSSession $PSCmdlet.SessionState + } + + # check for scoped vars in scriptblock if ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') { $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState } - # add custom log method to server, associated with the supplied log method(s) + # add custom Log Method to server, associated with the supplied Log Method(s) $PodeContext.Server.Logging.Types[$Name] = @{ + Custom = $true Method = $Method ScriptBlock = $ScriptBlock UsingVariables = $usingVars Levels = $Levels Raw = $Raw.IsPresent + Version = $Version + Metadata = $Metadata + Options = @{ + Formatting = @{ + Log = @{ + Format = $LogFormat + ScriptBlock = $LogScriptBlock + UsingVariables = $logUsingVars + } + SyslogInfo = $SyslogInfo + Serialise = @{ + Type = $SerialiseFormat + ScriptBlock = $SerialiseScriptBlock + UsingVariables = $serialiseUsingVars + XmlRootName = Protect-PodeValue -Value $XmlRootName -Default 'Log' + } + } + } Arguments = $ArgumentList } $PodeContext.Server.Logging.Logger.RegisterType([PodeLogType]::new($Name, $Levels)) - # then associate the supplied log method(s) with the request log type + # then associate the supplied Log Method(s) with the custom Log Type foreach ($methodId in $Method) { Register-PodeLogTypeToMethod -TypeName $Name -MethodId $methodId } @@ -556,18 +888,18 @@ function Remove-PodeLogType { ) process { - # get the log type + # get the Log Type $type = Get-PodeLogType -Name $Name if ($null -eq $type) { return } - # unregister log type from method + # unregister Log Type from method foreach ($methodId in $type.Method) { Unregister-PodeLogTypeFromMethod -TypeName $Name -MethodId $methodId } - # remove the log type + # remove the Log Type $null = $PodeContext.Server.Logging.Types.Remove($Name) $PodeContext.Server.Logging.Logger.UnregisterType($Name) } @@ -599,7 +931,7 @@ function Remove-PodeLogMethod { ) process { - # get the log method + # get the Log Method $method = Get-PodeLogMethod -Id $Id if ($null -eq $method) { return @@ -614,17 +946,17 @@ function Remove-PodeLogMethod { $PodeContext.RunspacePools.Logs.Pool.SetMaxRunspaces($maxRunspaces - 1) } - # dispose the log method queue + # dispose the Log Method queue if ($null -ne $method.Queue) { $method.Queue.Dispose() } - # unregister log method from types that reference it + # unregister Log Method from types that reference it foreach ($typeName in $method.Types) { Unregister-PodeLogMethodFromType -TypeName $typeName -MethodId $Id } - # remove the log method + # remove the Log Method $null = $PodeContext.Server.Logging.Methods.Remove($Id) } } @@ -674,7 +1006,7 @@ function Clear-PodeLogMethods { Remove-PodeLogMethod -Id $methodId } - # dispose of any log method queues + # dispose of any Log Method queues foreach ($method in $PodeContext.Server.Logging.Methods.Values) { if ($null -ne $method.Queue) { $method.Queue.Dispose() @@ -701,6 +1033,9 @@ A Message to write. .PARAMETER Level The Level of the error being logged. (Default: Error) +.PARAMETER Metadata +An optional hashtable of Metadata to include with the log item. + .PARAMETER CheckInnerException If supplied, any exceptions are check for inner exceptions. If one is present, this is also logged. @@ -729,54 +1064,60 @@ function Write-PodeErrorLog { $Message, [Parameter()] - [ValidateSet('Error', 'Warning', 'Informational', 'Verbose', 'Debug')] + [ValidateSet('Emergency', 'Alert', 'Critical', 'Error', 'Warning', 'Notice', 'Informational', 'Verbose', 'Debug')] [string] $Level = 'Error', + [Parameter()] + [hashtable] + $Metadata, + [Parameter(ParameterSetName = 'Exception')] [switch] $CheckInnerException ) - # do nothing if error logging isn't setup - if (!$PodeContext.Server.Logging.Logger.IsErrorLoggingEnabled) { - return - } + process { + # do nothing if error logging isn't setup + if (!$PodeContext.Server.Logging.Logger.IsErrorLoggingEnabled) { + return + } - # attempt to get current contextId - $contextId = Get-PodeLoggingContextId + # attempt to get current contextId + $contextId = Get-PodeLoggingContextId - # log error object appropriately based on parameter set - switch ($PSCmdlet.ParameterSetName) { - 'Exception' { - $PodeContext.Server.Logging.Logger.AddException($Exception, $contextId, $Level, [int]$ThreadId) - } + # log error object appropriately based on parameter set + switch ($PSCmdlet.ParameterSetName) { + 'Exception' { + $PodeContext.Server.Logging.Logger.AddException($Exception, $contextId, $Level, $Metadata, [int]$ThreadId) + } - 'Error' { - $PodeContext.Server.Logging.Logger.AddException($ErrorRecord.CategoryInfo.ToString(), $ErrorRecord.Exception.Message, $ErrorRecord.ScriptStackTrace, $contextId, $Level, [int]$ThreadId) - } + 'Error' { + $PodeContext.Server.Logging.Logger.AddException($ErrorRecord.CategoryInfo.ToString(), $ErrorRecord.Exception.Message, $ErrorRecord.ScriptStackTrace, $contextId, $Level, $Metadata, [int]$ThreadId) + } - 'Message' { - $category = (Get-PSCallStack)[1].Location - $PodeContext.Server.Logging.Logger.AddException($category, $Message, [string]::Empty, $contextId, $Level, [int]$ThreadId) + 'Message' { + $category = (Get-PSCallStack)[1].Location + $PodeContext.Server.Logging.Logger.AddException($category, $Message, [string]::Empty, $contextId, $Level, $Metadata, [int]$ThreadId) + } } - } - # for exceptions, check the inner exception - if ($CheckInnerException -and ($null -ne $Exception.InnerException) -and ![string]::IsNullOrWhiteSpace($Exception.InnerException.Message)) { - $Exception.InnerException | Write-PodeErrorLog + # for exceptions, check the inner exception + if ($CheckInnerException -and ($null -ne $Exception.InnerException) -and ![string]::IsNullOrWhiteSpace($Exception.InnerException.Message)) { + $Exception.InnerException | Write-PodeErrorLog + } } } <# .SYNOPSIS -Write an object to a configured custom Logging method. +Write an object to a configured custom Log Method. .DESCRIPTION -Write an object to a configured custom Logging method. +Write an object to one or more configured custom Log Methods. .PARAMETER Name -The Name of the Logging type to use. +One or more custom Log Type Names. .PARAMETER Level The Level of the log item being logged. (Default: Informational) @@ -784,28 +1125,45 @@ The Level of the log item being logged. (Default: Informational) .PARAMETER InputObject The Object to write. +.PARAMETER Metadata +An optional hashtable of Metadata to include with the log item. + .EXAMPLE $object | Write-PodeLog -Name 'LogTypeName' + +.EXAMPLE +$object | Write-PodeLog -Name 'LogTypeName1', 'LogTypeName2' + +.EXAMPLE +$object | Write-PodeLog -Name 'LogTypeName' -Level 'Debug' -Metadata @{ Key = 'Value' } #> function Write-PodeLog { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - [string] + [string[]] $Name, [Parameter()] - [ValidateSet('Error', 'Warning', 'Informational', 'Verbose', 'Debug')] + [ValidateSet('Emergency', 'Alert', 'Critical', 'Error', 'Warning', 'Notice', 'Informational', 'Verbose', 'Debug')] [string] $Level = 'Informational', [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [object] - $InputObject + $InputObject, + + [Parameter()] + [hashtable] + $Metadata ) - # add the item to be processed - $PodeContext.Server.Logging.Logger.Add($Name, $Level, $InputObject) + process { + foreach ($n in $Name) { + $logEvent = [PodeLogEvent]::new($n, $Level, $InputObject, $Metadata) + $PodeContext.Server.Logging.Logger.Add($logEvent) + } + } } <# @@ -831,14 +1189,23 @@ function Protect-PodeLogItem { $Item ) - # do nothing if there are no masks - if (Test-PodeIsEmpty $PodeContext.Server.Logging.Masking.Patterns) { - return $item - } + process { + # do nothing if there are no masks + if (($null -eq $PodeContext.Server.Logging.Masking.Patterns) -or ($PodeContext.Server.Logging.Masking.Patterns.Count -eq 0)) { + return $Item + } + + # do nothing if the item is null or empty + if ([string]::IsNullOrWhiteSpace($Item)) { + return $Item + } + + # attempt to apply each mask + foreach ($mask in $PodeContext.Server.Logging.Masking.Patterns) { + if ($Item -inotmatch $mask) { + continue + } - # attempt to apply each mask - foreach ($mask in $PodeContext.Server.Logging.Masking.Patterns) { - if ($Item -imatch $mask) { # has both keep before/after if ($Matches.ContainsKey('keep_before') -and $Matches.ContainsKey('keep_after')) { $Item = ($Item -ireplace $mask, "`${keep_before}$($PodeContext.Server.Logging.Masking.Mask)`${keep_after}") @@ -859,9 +1226,9 @@ function Protect-PodeLogItem { $Item = ($Item -ireplace $mask, $PodeContext.Server.Logging.Masking.Mask) } } - } - return $Item + return $Item + } } <# @@ -928,14 +1295,14 @@ function New-PodeLogBatchInfo { <# .SYNOPSIS -Create a new Terminal logging Method. +Create a new Terminal Log Method. .DESCRIPTION -Creates a new Terminal logging Method for outputting log items to the terminal. -Can be used with Enable-PodeRequestLogType, Enable-PodeErrorLogType, or Add-PodeLogType. +Creates a new Terminal Log Method for outputting log items to the terminal. +Can be used with Enable-PodeLogRequestType, Enable-PodeLogErrorType, or Add-PodeLogType. .PARAMETER Id -An optional ID to assign to the logging method. If not supplied, a random ID will be generated. +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. .PARAMETER BatchInfo An optional hashtable containing batch configuration for writing log items in bulk. @@ -971,14 +1338,14 @@ function New-PodeLogTerminalMethod { <# .SYNOPSIS -Create a new File logging Method. +Create a new File Log Method. .DESCRIPTION -Creates a new File logging Method for outputting log items to files. -Can be used with Enable-PodeRequestLogType, Enable-PodeErrorLogType, or Add-PodeLogType. +Creates a new File Log Method for outputting log items to files. +Can be used with Enable-PodeLogRequestType, Enable-PodeLogErrorType, or Add-PodeLogType. .PARAMETER Id -An optional ID to assign to the logging method. If not supplied, a random ID will be generated. +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. .PARAMETER Name The File Name to prepend new log files using. @@ -1057,14 +1424,14 @@ function New-PodeLogFileMethod { <# .SYNOPSIS -Create a new Event Viewer logging Method. +Create a new Event Viewer Log Method. .DESCRIPTION -Creates a new Event Viewer logging Method for outputting log items to the Windows Event Viewer. -Can be used with Enable-PodeRequestLogType, Enable-PodeErrorLogType, or Add-PodeLogType. +Creates a new Event Viewer Log Method for outputting log items to the Windows Event Viewer. +Can be used with Enable-PodeLogRequestType, Enable-PodeLogErrorType, or Add-PodeLogType. .PARAMETER Id -An optional ID to assign to the logging method. If not supplied, a random ID will be generated. +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. .PARAMETER EventLogName An Optional Log Name for the Event Viewer (Default: Application) @@ -1136,14 +1503,14 @@ function New-PodeLogEventViewerMethod { <# .SYNOPSIS -Create a new Custom logging Method. +Create a new Custom Log Method. .DESCRIPTION -Creates a new Custom logging Method for outputting log items using custom logic defined in a ScriptBlock. -Can be used with Enable-PodeRequestLogType, Enable-PodeErrorLogType, or Add-PodeLogType. +Creates a new Custom Log Method for outputting log items using custom logic defined in a ScriptBlock. +Can be used with Enable-PodeLogRequestType, Enable-PodeLogErrorType, or Add-PodeLogType. .PARAMETER Id -An optional ID to assign to the logging method. If not supplied, a random ID will be generated. +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. .PARAMETER ScriptBlock The ScriptBlock that defines how to output a log item. @@ -1151,6 +1518,15 @@ The ScriptBlock that defines how to output a log item. .PARAMETER ArgumentList An array of arguments to supply to the Custom Logging output method's ScriptBlock. +.PARAMETER Version +The version of the Log Method, this determines the arguments which are supplied to the ScriptBlock. (Default: 1) +Arguments supplied depending on the version: + +- Version 1: Transformed Log Items, ArgumentList, Raw Log Items (legacy) +- Version 2: Log Items, ArgumentList + +Under Version 2, the "Log Items" contains references to the transformed and raw log items. + .PARAMETER BatchInfo An optional hashtable containing batch configuration for writing log items in bulk. Should be created using New-PodeLogBatchInfo. @@ -1190,6 +1566,11 @@ function New-PodeLogCustomMethod { [object[]] $ArgumentList, + [Parameter(ParameterSetName = 'ScriptBlock')] + [ValidateSet(1, 2)] + [int] + $Version = 1, + [Parameter()] [hashtable] $BatchInfo = $null @@ -1207,5 +1588,1194 @@ function New-PodeLogCustomMethod { UsingVariables = $usingVars } Arguments = $ArgumentList + Version = $Version + } +} + +<# +.SYNOPSIS +Creates a new API Log Method. + +.DESCRIPTION +Creates a new API Log Method for outputting log items to custom API endpoints. + +.PARAMETER Id +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. + +.PARAMETER Type +An optional Type to assign to the Log Method. (Default: API) + +.PARAMETER Url +The URL of the API endpoint to send log items to. + +.PARAMETER ContentType +An optional Content-Type header to include in the API request. (Default: application/json) + +.PARAMETER Method +An optional HTTP Method to use for the API request. (Default: POST) + +.PARAMETER Headers +An optional hashtable of headers to include in the API request, typically used for the Authorization header. + +.PARAMETER HeadersScriptBlock +An optional ScriptBlock that returns a hashtable of headers to include in the API request. +Useful for dynamically generating headers based on the request body or other elements. + +.PARAMETER HeadersArguments +An optional hashtable of arguments to pass to the HeadersScriptBlock. + +.PARAMETER BodyScriptBlock +A ScriptBlock that returns the body of the API request as a valid string, based on a collection of log items. +Protect-PodeLogItem will be automatically applied to the returned string. + +.PARAMETER BodyArguments +An optional hashtable of arguments to pass to the BodyScriptBlock. + +.PARAMETER BatchInfo +An optional hashtable containing batch configuration for writing log items in bulk. +Should be created using New-PodeLogBatchInfo. + +.PARAMETER SkipCertificateCheck +If supplied, the API request will skip certificate validation checks. + +.PARAMETER Compress +If supplied, the API request will include a Content-Encoding: gzip header and compress the request body. + +.EXAMPLE +$headers = @{ 'Authorization' = 'Bearer ' } +$methodId = New-PodeLogApiMethod -Url 'https://api.example.com/logs' -Headers $headers -Compress -BodyScriptBlock { + param($logItems) + return $logItems.Data | ConvertTo-Json -Compress +} +#> +function New-PodeLogApiMethod { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter()] + [string] + $Id, + + [Parameter()] + [string] + $Type, + + [Parameter(Mandatory = $true)] + [string] + $Url, + + [Parameter()] + [ValidatePattern('^\w+\/[\w\.\+-]+$')] + [string] + $ContentType = 'application/json', + + [Parameter()] + [ValidateSet('Get', 'Post', 'Put', 'Patch')] + [string] + $Method = 'Post', + + [Parameter()] + [hashtable] + $Headers = @{}, + + [Parameter()] + [scriptblock] + $HeadersScriptBlock, + + [Parameter()] + [hashtable] + $HeadersArguments, + + [Parameter(Mandatory = $true)] + [scriptblock] + $BodyScriptBlock, + + [Parameter()] + [hashtable] + $BodyArguments, + + [Parameter()] + [hashtable] + $BatchInfo = $null, + + [switch] + $SkipCertificateCheck, + + [switch] + $Compress + ) + + # default type + if ([string]::IsNullOrWhiteSpace($Type)) { + $Type = 'API' + } + + # default headers + if ($null -eq $Headers) { + $Headers = @{} + } + + if ($Compress -and !$Headers.ContainsKey('Content-Encoding')) { + $Headers['Content-Encoding'] = 'gzip' + } + + # headers scriptblock + using vars + if ($null -ne $HeadersScriptBlock) { + $HeadersScriptBlock, $headerUsingVars = Convert-PodeScopedVariables -ScriptBlock $HeadersScriptBlock -PSSession $PSCmdlet.SessionState + } + + # body scriptblock + using vars + if ($null -ne $BodyScriptBlock) { + $BodyScriptBlock, $bodyUsingVars = Convert-PodeScopedVariables -ScriptBlock $BodyScriptBlock -PSSession $PSCmdlet.SessionState + } + + # add method to server + return Add-PodeLogMethod -Id $Id -Batch $BatchInfo -Metadata @{ + Type = $Type + ScriptBlock = Get-PodeLoggingApiMethod + Arguments = @{ + Url = $Url + ContentType = $ContentType + Method = $Method + Compress = $Compress.IsPresent + Headers = @{ + Value = $Headers + ScriptBlock = $HeadersScriptBlock + UsingVariables = $headerUsingVars + Arguments = $HeadersArguments + } + Body = @{ + ScriptBlock = $BodyScriptBlock + UsingVariables = $bodyUsingVars + Arguments = $BodyArguments + } + SkipCertificateCheck = $SkipCertificateCheck.IsPresent + } + } +} + +<# +.SYNOPSIS +Creates a new Splunk Log Method. + +.DESCRIPTION +Creates a new Splunk Log Method for outputting log items to a Splunk HTTP Event Collector (HEC) endpoint. + +.PARAMETER Id +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. + +.PARAMETER BaseUrl +The base URL of the Splunk HEC endpoint, e.g., 'https://splunk.example.com:8088'. + +.PARAMETER Token +The authentication token for the Splunk HEC endpoint. + +.PARAMETER SourceType +An optional source type to include with the log items. + +.PARAMETER Source +An optional source to include with the log items. (Default: the server's AppName) + +.PARAMETER Index +An optional index to include with the log items. + +.PARAMETER BatchInfo +An optional hashtable containing batch configuration for writing log items in bulk. +Should be created using New-PodeLogBatchInfo. + +.PARAMETER SkipCertificateCheck +If supplied, the API request will skip certificate validation checks. + +.EXAMPLE +$methodId = New-PodeLogSplunkMethod -BaseUrl 'https://splunk.example.com:8088' -Token '' -Source 'my_source' + +.EXAMPLE +$batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 +$methodId = New-PodeLogSplunkMethod -BaseUrl 'https://splunk.example.com:8088' -Token '' -SourceType 'syslog' -BatchInfo $batchInfo + +.EXAMPLE +$methodId = New-PodeLogSplunkMethod -BaseUrl 'https://localhost:8088' -Token '' -SkipCertificateCheck +#> +function New-PodeLogSplunkMethod { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter()] + [string] + $Id, + + [Parameter(Mandatory = $true)] + [string] + $BaseUrl, + + [Parameter(Mandatory = $true)] + [string] + $Token, + + [Parameter()] + [string] + $SourceType, + + [Parameter()] + [string] + $Source, + + [Parameter()] + [string] + $Index, + + [Parameter()] + [hashtable] + $BatchInfo = $null, + + [switch] + $SkipCertificateCheck + ) + + # default source + $Source = Protect-PodeValue -Value $Source -Default $PodeContext.Server.AppName + + # build body scriptblock + $bodyScriptBlock = { + param($logItems, $options) + + # build array of events + $events = @(foreach ($item in $logItems) { + # build base event object + $evt = @{ + event = $item.Data + host = $PodeContext.Server.ComputerName + time = ConvertTo-PodeUnixEpoch -DateTime $item.Event.Timestamp + fields = @{ + severity = ConvertTo-PodeSplunkLevel -Level $item.Event.Level + } + } + + # add source type + $sourceType = Protect-PodeValue -Value $item.Event.Metadata['SourceType'] -Default $options.SourceType + if (![string]::IsNullOrEmpty($sourceType)) { + $evt.sourcetype = $sourceType + } + + # add source + $source = Protect-PodeValue -Value $item.Event.Metadata['Source'] -Default $options.Source + if (![string]::IsNullOrEmpty($source)) { + $evt.source = $source + } + + # add index + $index = Protect-PodeValue -Value $item.Event.Metadata['Index'] -Default $options.Index + if (![string]::IsNullOrEmpty($index)) { + $evt.index = $index + } + + $evt + }) + + # convert to json and return + return $events | ConvertTo-Json -Compress -Depth 10 + } + + # default headers + $headers = @{ + Authorization = "Splunk $($Token)" + } + + # add method to server + $bodyArgs = @{ + SourceType = $SourceType + Source = $Source + Index = $Index + } + + return New-PodeLogApiMethod ` + -Id $Id ` + -Type 'Splunk' ` + -BatchInfo $BatchInfo ` + -Url "$($BaseUrl.TrimEnd('/'))/services/collector" ` + -Headers $headers ` + -BodyScriptBlock $bodyScriptBlock ` + -BodyArguments $bodyArgs ` + -SkipCertificateCheck:$SkipCertificateCheck.IsPresent ` + -Compress +} + +<# +.SYNOPSIS +Creates a new Datadog Log Method. + +.DESCRIPTION +Creates a new Datadog Log Method for outputting log items to a Datadog's HTTP v2 logging API endpoint. + +.PARAMETER Id +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. + +.PARAMETER BaseUrl +The base URL of the Datadog API endpoint, e.g., 'https://http-intake.logs.datadoghq.com'. + +.PARAMETER ApiKey +The API key used for authenticating with the Datadog API. + +.PARAMETER Tags +An optional hashtable of tags to include with the log items. + +.PARAMETER Service +An optional service name to include with the log items. (Default: the server's AppName) + +.PARAMETER Source +An optional source name to include with the log items. + +.PARAMETER BatchInfo +An optional hashtable containing batch configuration for writing log items in bulk. +Should be created using New-PodeLogBatchInfo. + +.PARAMETER SkipCertificateCheck +If supplied, the API request will skip certificate validation checks. + +.EXAMPLE +$methodId = New-PodeLogDatadogMethod -BaseUrl 'https://http-intake.logs.datadoghq.com' -ApiKey '' -Source 'my_source' + +.EXAMPLE +$batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 +$methodId = New-PodeLogDatadogMethod -BaseUrl 'https://http-intake.logs.datadoghq.eu' -ApiKey '' -Tags @{ env = 'prod' } -BatchInfo $batchInfo + +.EXAMPLE +$methodId = New-PodeLogDatadogMethod -BaseUrl 'https://http-intake.logs.datadoghq.com' -ApiKey '' -Service 'my_service' -Source 'my_source' -SkipCertificateCheck +#> +function New-PodeLogDatadogMethod { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter()] + [string] + $Id, + + [Parameter(Mandatory = $true)] + [string] + $BaseUrl, + + [Parameter(Mandatory = $true)] + [string] + $ApiKey, + + [Parameter()] + [hashtable] + $Tags, + + [Parameter()] + [string] + $Service, + + [Parameter()] + [string] + $Source, + + [Parameter()] + [hashtable] + $BatchInfo = $null, + + [switch] + $SkipCertificateCheck + ) + + # default service + $Service = Protect-PodeValue -Value $Service -Default $PodeContext.Server.AppName + + # build body scriptblock + $bodyScriptBlock = { + param($logItems, $options) + + # build array of events + $events = @(foreach ($item in $logItems) { + # build base event object + $evt = @{ + message = $item.Data + hostname = $PodeContext.Server.ComputerName + status = ConvertTo-PodeDatadogLevel -Level $item.Event.Level + timestamp = $item.Event.Timestamp.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } + + # add service + $service = Protect-PodeValue -Value $item.Event.Metadata['Service'] -Default $options.Service + if (![string]::IsNullOrEmpty($service)) { + $evt.service = $service + } + + # add source + $source = Protect-PodeValue -Value $item.Event.Metadata['Source'] -Default $options.Source + if (![string]::IsNullOrEmpty($source)) { + $evt.ddsource = $source + } + + # add tags + if ($options.Tags.Count -gt 0) { + $evt.ddtags = @(foreach ($key in $options.Tags.Keys) { "$($key):$($options.Tags[$key])" }) -join ',' + } + + $evt + }) + + # convert to json and return + return $events | ConvertTo-Json -Compress -Depth 10 + } + + # default headers + $headers = @{ + 'DD-API-KEY' = $ApiKey + } + + # default tags + if ($null -eq $Tags) { + $Tags = @{} + } + + # add method to server + $bodyArgs = @{ + Service = $Service + Source = $Source + Tags = $Tags + } + + return New-PodeLogApiMethod ` + -Id $Id ` + -Type 'Datadog' ` + -BatchInfo $BatchInfo ` + -Url "$($BaseUrl.TrimEnd('/'))/api/v2/logs" ` + -Headers $headers ` + -BodyScriptBlock $bodyScriptBlock ` + -BodyArguments $bodyArgs ` + -SkipCertificateCheck:$SkipCertificateCheck.IsPresent ` + -Compress +} + +<# +.SYNOPSIS +Creates a new Azure Log Analytics Log Method. + +.DESCRIPTION +Creates a new Azure Log Analytics Log Method for outputting log items to an Azure Log Analytics workspace. +The Azure Log Method created will either use the legacy Workspace method, or the new Data Collection logic. + +.PARAMETER Id +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. + +.PARAMETER WorkspaceId +The Workspace ID of the Azure Log Analytics workspace. + +.PARAMETER SharedKey +The Shared Key of the Azure Log Analytics workspace. + +.PARAMETER LogType +The Log Type to use for the log items in Azure Log Analytics, used for the Log-Type header. + +.PARAMETER Endpoint +The Data Collection Endpoint used for ingestion into Azure Monitor/Azure Log Analytics. + +.PARAMETER ImmutableId +The Data Collection Rule Immutable ID. + +.PARAMETER StreamName +The Stream Name in the Data Collection Rule that should handle the logs. + +.PARAMETER ClientId +The Client ID from registering a new app. + +.PARAMETER ClientSecret +The Client Secret from registering a new app. + +.PARAMETER TenantId +The Directory/Tenant ID from registering a new app. + +.PARAMETER Source +An optional source to include with the log items. (Default: the server's AppName) + +.PARAMETER BatchInfo +An optional hashtable containing batch configuration for writing log items in bulk. +Should be created using New-PodeLogBatchInfo. + +.PARAMETER SkipCertificateCheck +If supplied, the API request will skip certificate validation checks. + +.EXAMPLE +$methodId = New-PodeLogAzureMethod -WorkspaceId '' -SharedKey '' -LogType 'MyCustomLog' + +.EXAMPLE +$batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 +$methodId = New-PodeLogAzureMethod -WorkspaceId '' -SharedKey '' -LogType 'MyCustomLog' -BatchInfo $batchInfo -Source 'my_source' +#> +function New-PodeLogAzureMethod { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter()] + [string] + $Id, + + [Parameter(Mandatory = $true, ParameterSetName = 'Workspace')] + [string] + $WorkspaceId, + + [Parameter(Mandatory = $true, ParameterSetName = 'Workspace')] + [string] + $SharedKey, + + [Parameter(Mandatory = $true, ParameterSetName = 'Workspace')] + [string] + $LogType, + + [Parameter(Mandatory = $true, ParameterSetName = 'DataCollection')] + [string] + $Endpoint, + + [Parameter(Mandatory = $true, ParameterSetName = 'DataCollection')] + [string] + $ImmutableId, + + [Parameter(Mandatory = $true, ParameterSetName = 'DataCollection')] + [string] + $StreamName, + + [Parameter(Mandatory = $true, ParameterSetName = 'DataCollection')] + [string] + $ClientId, + + [Parameter(Mandatory = $true, ParameterSetName = 'DataCollection')] + [string] + $ClientSecret, + + [Parameter(Mandatory = $true, ParameterSetName = 'DataCollection')] + [string] + $TenantId, + + [Parameter()] + [string] + $Source, + + [Parameter()] + [hashtable] + $BatchInfo = $null, + + [switch] + $SkipCertificateCheck + ) + + # default source + $Source = Protect-PodeValue -Value $Source -Default $PodeContext.Server.AppName + + # return appropriate azure logging + switch ($PSCmdlet.ParameterSetName) { + 'Workspace' { + return New-PodeLogAzureWorkspaceMethod ` + -Id $Id ` + -WorkspaceId $WorkspaceId ` + -SharedKey $SharedKey ` + -LogType $LogType ` + -Source $Source ` + -BatchInfo $BatchInfo ` + -SkipCertificateCheck:$SkipCertificateCheck + } + + 'DataCollection' { + return New-PodeLogAzureDataCollectionMethod ` + -Id $Id ` + -Endpoint $Endpoint ` + -ImmutableId $ImmutableId ` + -StreamName $StreamName ` + -ClientId $ClientId ` + -ClientSecret $ClientSecret ` + -TenantId $TenantId ` + -Source $Source ` + -BatchInfo $BatchInfo ` + -SkipCertificateCheck:$SkipCertificateCheck + } + } +} + +<# +.SYNOPSIS +Creates a new AWS CloudWatch Log Method. + +.DESCRIPTION +Creates a new AWS CloudWatch Log Method for outputting log items to an AWS CloudWatch Logs group and stream. + +.PARAMETER Id +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. + +.PARAMETER LogGroupName +The name of the AWS CloudWatch Logs group to which log events will be sent. + +.PARAMETER LogStreamName +The name of the AWS CloudWatch Logs stream within the specified log group. + +.PARAMETER Token +The authentication bearer token for the AWS CloudWatch Logs API. + +.PARAMETER Region +The AWS region where the CloudWatch Logs group and stream are located. + +.PARAMETER SourceType +The source type for the log events. + +.PARAMETER Source +The source for the log events. (Default: the server's AppName) + +.PARAMETER Index +The index for the log events. + +.PARAMETER BatchInfo +An optional hashtable containing batch configuration for writing log items in bulk. +Should be created using New-PodeLogBatchInfo. + +.PARAMETER SkipCertificateCheck +If supplied, the API request will skip certificate validation checks. + +.EXAMPLE +$methodId = New-PodeLogAwsMethod -LogGroupName 'my-log-group' -LogStreamName 'my-log-stream' -Region 'us-east-1' -Token '' + +.EXAMPLE +$batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 +$methodId = New-PodeLogAwsMethod -LogGroupName 'my-log-group' -LogStreamName 'my-log-stream' -Region 'us-east-1' -Token '' -BatchInfo $batchInfo +#> +function New-PodeLogAwsMethod { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter()] + [string] + $Id, + + [Parameter(Mandatory = $true)] + [string] + $LogGroupName, + + [Parameter(Mandatory = $true)] + [string] + $LogStreamName, + + [Parameter(Mandatory = $true)] + [string] + $Region, + + [Parameter(Mandatory = $true)] + [string] + $Token, + + [Parameter()] + [string] + $SourceType, + + [Parameter()] + [string] + $Source, + + [Parameter()] + [string] + $Index, + + [Parameter()] + [hashtable] + $BatchInfo = $null, + + [switch] + $SkipCertificateCheck + ) + + # default source + $Source = Protect-PodeValue -Value $Source -Default $PodeContext.Server.AppName + + # build body scriptblock + $bodyScriptBlock = { + param($logItems, $options) + + # build array of events + $events = @(foreach ($item in $logItems) { + # build base event object + $evt = @{ + event = $item.Data + host = $PodeContext.Server.ComputerName + time = ConvertTo-PodeUnixEpoch -DateTime $item.Event.Timestamp + severity = ConvertTo-PodeSplunkLevel -Level $item.Event.Level + } + + # add source type + $sourceType = Protect-PodeValue -Value $item.Event.Metadata['SourceType'] -Default $options.SourceType + if (![string]::IsNullOrEmpty($sourceType)) { + $evt.sourcetype = $sourceType + } + + # add source + $source = Protect-PodeValue -Value $item.Event.Metadata['Source'] -Default $options.Source + if (![string]::IsNullOrEmpty($source)) { + $evt.source = $source + } + + # add index + $index = Protect-PodeValue -Value $item.Event.Metadata['Index'] -Default $options.Index + if (![string]::IsNullOrEmpty($index)) { + $evt.index = $index + } + + $evt + }) + + # convert to json and return + return $events | ConvertTo-Json -Compress -Depth 10 + } + + # default headers + $headers = @{ + Authorization = "Bearer $($Token)" + } + + # add method to server + $bodyArgs = @{ + SourceType = $SourceType + Source = $Source + Index = $Index + } + + return New-PodeLogApiMethod ` + -Id $Id ` + -Type 'AWS' ` + -BatchInfo $BatchInfo ` + -Url "https://logs.$($Region).amazonaws.com/services/collector/event?logGroup=$($LogGroupName)&logStream=$($LogStreamName)" ` + -Headers $headers ` + -BodyScriptBlock $bodyScriptBlock ` + -BodyArguments $bodyArgs ` + -SkipCertificateCheck:$SkipCertificateCheck.IsPresent ` + -Compress +} + +<# +.SYNOPSIS +Creates a new network Log Method. + +.DESCRIPTION +Creates a new network Log Method for outputting log items to a remote server over UDP, TCP, or TLS. + +.PARAMETER Id +An optional ID to assign to the Log Method. If not supplied, a random ID will be generated. + +.PARAMETER Server +The address (IP or Hostname) of the remote server to send log items to. + +.PARAMETER Transport +The transport protocol to use for sending log items. (Default: 'Udp') + +.PARAMETER Port +The port number to use. (Default: 514) + +.PARAMETER BatchInfo +An optional hashtable containing batch configuration for writing log items in bulk. +Should be created using New-PodeLogBatchInfo. + +.PARAMETER SkipCertificateCheck +A switch parameter to skip certificate validation when using TLS transport. + +.EXAMPLE +$methodId = New-PodeLogNetworkMethod -Server '192.168.1.100' -Transport 'Tcp' -Port 514 + +.EXAMPLE +$methodId = New-PodeLogNetworkMethod -Server 'logs.example.com' -Transport 'Tls' -Port 6514 -SkipCertificateCheck + +.EXAMPLE +$batchInfo = New-PodeLogBatchInfo -Size 10 -Timeout 10 +$methodId = New-PodeLogNetworkMethod -Server 'logs.example.com' -Transport 'Udp' -BatchInfo $batchInfo +#> +function New-PodeLogNetworkMethod { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter()] + [string] + $Id, + + [Parameter(Mandatory = $true)] + [string] + $Server, + + [Parameter()] + [ValidateSet('Udp', 'Tcp', 'Tls')] + [string] + $Transport = 'Udp', + + [Parameter()] + [ValidateRange(1, 65535)] + [int] + $Port = 514, + + [Parameter()] + [hashtable] + $BatchInfo = $null, + + [switch] + $SkipCertificateCheck + ) + + # add method to server + return Add-PodeLogMethod -Id $Id -Batch $BatchInfo -Metadata @{ + Type = 'Network' + ScriptBlock = Get-PodeLoggingNetworkMethod + Arguments = @{ + Server = $Server + Transport = $Transport + Port = $Port + SkipCertificateCheck = $SkipCertificateCheck.IsPresent + } + } +} + +<# +.SYNOPSIS +Creates a new syslog information object. + +.DESCRIPTION +This function creates a new syslog information object with the specified parameters. + +.PARAMETER Facility +The syslog facility code. (Default: 16, which corresponds to local0 for web/app logs) + +.PARAMETER AppName +The name of the application generating the log message. (Default: the server's AppName) + +.PARAMETER Tags +An optional hashtable of tags to include in the syslog message. + +.PARAMETER Format +The syslog format to use. (Default: Server default, or 'RFC5424' if no default set) + +.EXAMPLE +$info = New-PodeLogSyslogInfo -Format 'RFC3164' + +.EXAMPLE +$info = New-PodeLogSyslogInfo -Facility 18 -AppName 'MyApp' -Tags @{ Environment = 'Production'; Version = '1.0' } -Format 'RFC5424' + +.EXAMPLE +$info = New-PodeLogSyslogInfo -AppName 'MyApp' +#> +function New-PodeLogSyslogInfo { + [CmdletBinding()] + [OutputType([hashtable])] + param( + [Parameter()] + [ValidateRange(0, 23)] + [int] + $Facility = 16, # local0 for web/app logs + + [Parameter()] + [string] + $AppName, + + [Parameter()] + [hashtable] + $Tags, + + [Parameter()] + [Pode.Utilities.Logging.PodeSyslogFormat] + $Format = 'RFC5424' + ) + + # check default format if not supplied + if (!$PSBoundParameters.ContainsKey('Format')) { + $Format = Protect-PodeValue -Value (Get-PodeLogDefaultSyslogFormat) -Default $Format -EnumType ([PodeSyslogFormat]) + } + + # return syslog info + return @{ + Facility = $Facility + AppName = $AppName + Tags = $Tags + Format = $Format + } +} + +<# +.SYNOPSIS +Converts a log message to a syslog formatted string. + +.DESCRIPTION +Converts a log message to a syslog formatted string based on the specified parameters. + +.PARAMETER Message +The log message to be converted. + +.PARAMETER Level +The log level of the message. + +.PARAMETER Timestamp +The timestamp of the log message. + +.PARAMETER Facility +The syslog facility code. (Default: 16, which corresponds to local0 for web/app logs) + +.PARAMETER AppName +The name of the application generating the log message. (Default: the server's AppName) + +.PARAMETER Tags +A hashtable of tags to include in the syslog message. + +.PARAMETER Format +The syslog format to use. (Default: Server default, or 'RFC5424' if no default set) + +.EXAMPLE +$msg = ConvertTo-PodeSyslog -Message "This is a test log message" -Level 'Info' -Timestamp (Get-Date) -Tags @{ Environment = "Production"; Version = "1.0" } + +.EXAMPLE +$msg = ConvertTo-PodeSyslog -Message "This is a test log message" -Level 'Error' -Timestamp (Get-Date) -Facility 16 -AppName "MyApp" -Tags @{ Environment = "Staging"; Version = "2.0" } -Format 'RFC3164' +#> +function ConvertTo-PodeSyslog { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [object] + $Message, + + [Parameter(Mandatory = $true)] + [Pode.Utilities.Logging.PodeLogLevel] + $Level, + + [Parameter(Mandatory = $true)] + [datetime] + $Timestamp, + + [Parameter()] + [ValidateRange(0, 23)] + [int] + $Facility = 16, # local0 for web/app logs + + [Parameter()] + [string] + $AppName, + + [Parameter()] + [hashtable] + $Tags, + + [Parameter()] + [Pode.Utilities.Logging.PodeSyslogFormat] + $Format = 'RFC5424' + ) + + process { + # set default format + if (!$PSBoundParameters.ContainsKey('Format')) { + $Format = Protect-PodeValue -Value (Get-PodeLogDefaultSyslogFormat) -Default $Format -EnumType ([PodeSyslogFormat]) + } + + # set default app-name + $AppName = Protect-PodeValue -Value $AppName -Default $PodeContext.Server.AppName + if ([string]::IsNullOrWhiteSpace($AppName)) { + $AppName = '-' + } + + # generate priority value + $priority = ($Facility * 8) + (ConvertTo-PodeSyslogLevel -Level $Level) + + # get process ID + $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id + + # ensure message is a string, and escape newlines and carriage returns in message + if ($Message -isnot [string]) { + $Message = $Message | ConvertTo-PodeString + } + + $Message = $Message.Trim().Replace("`n", '\n').Replace("`r", '\r') + + # build message based on format + switch ($Format) { + 'RFC3164' { + $strTimestamp = $Timestamp.ToString('MMM dd HH:mm:ss') + $result = "<$($priority)>$($strTimestamp) $($PodeContext.Server.ComputerName) $($AppName)[$($processId)]: $($Message)" + } + + 'RFC5424' { + $strTimestamp = $Timestamp.ToString('yyyy-MM-ddTHH:mm:ss.fffK') + + $strTags = '-' + if ($Tags.Count -gt 0) { + $strTags = @( + foreach ($key in $Tags.Keys) { + $value = $Tags[$key].Replace('\', '\\').Replace('"', '\"').Replace("`n", '\n').Replace("`r", '\r').Replace(']', '\]') + "$($key)=`"$($value)`"" + } + ) -join ' ' + + $strTags = "[$($strTags)]" + } + + $result = "<$($priority)>1 $($strTimestamp) $($PodeContext.Server.ComputerName) $($AppName) $($processId) - $strTags $($Message)" + } + } + + return $result + } +} + +<# +.SYNOPSIS +Retrieve the default log format for logging. + +.DESCRIPTION +Retrieves the default log format used by the logging system. + +.EXAMPLE +$format = Get-PodeLogDefaultFormat +#> +function Get-PodeLogDefaultFormat { + [CmdletBinding()] + [OutputType([Pode.Utilities.Logging.PodeLogFormat])] + param() + + return $PodeContext.Server.Logging.Formatting.Log +} + +<# +.SYNOPSIS +Sets the default log format for logging. + +.DESCRIPTION +Sets the default log format used by the logging system. + +.PARAMETER Format +The log format to set as the default. + +.EXAMPLE +Set-PodeLogDefaultFormat -Format 'Syslog' +#> +function Set-PodeLogDefaultFormat { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [Pode.Utilities.Logging.PodeLogFormat] + $Format + ) + + $PodeContext.Server.Logging.Formatting.Log = $Format +} + +<# +.SYNOPSIS +Retrieve the default syslog format for logging. + +.DESCRIPTION +Retrieves the default syslog format used by the logging system. + +.EXAMPLE +$format = Get-PodeLogDefaultSyslogFormat +#> +function Get-PodeLogDefaultSyslogFormat { + [CmdletBinding()] + [OutputType([Pode.Utilities.Logging.PodeSyslogFormat])] + param() + + return $PodeContext.Server.Logging.Formatting.Syslog +} + +<# +.SYNOPSIS +Set the default syslog format for logging. + +.DESCRIPTION +Sets the default syslog format used by the logging system. + +.PARAMETER Format +The syslog format to set as the default. + +.EXAMPLE +Set-PodeLogDefaultSyslogFormat -Format 'RFC5424' +#> +function Set-PodeLogDefaultSyslogFormat { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [Pode.Utilities.Logging.PodeSyslogFormat] + $Format + ) + + $PodeContext.Server.Logging.Formatting.Syslog = $Format +} + +<# +.SYNOPSIS +Retrieve the default serialise format for logging. + +.DESCRIPTION +Retrieves the default serialise format used by the logging system. + +.EXAMPLE +$format = Get-PodeLogDefaultSerialiseFormat +#> +function Get-PodeLogDefaultSerialiseFormat { + [CmdletBinding()] + [OutputType([Pode.Utilities.Logging.PodeSerialiseFormat])] + param() + + return $PodeContext.Server.Logging.Formatting.Serialise +} + +<# +.SYNOPSIS +Set the default serialise format for logging. + +.DESCRIPTION +Sets the default serialise format used by the logging system. + +.PARAMETER Format +The serialise format to set as the default. + +.EXAMPLE +Set-PodeLogDefaultSerialiseFormat -Format 'Json' +#> +function Set-PodeLogDefaultSerialiseFormat { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [Pode.Utilities.Logging.PodeSerialiseFormat] + $Format + ) + + $PodeContext.Server.Logging.Formatting.Serialise = $Format +} + +<# +.SYNOPSIS +Convert a Pode Log Item or Collection to a string. + +.DESCRIPTION +Converts a Pode Log Item or a collection of Pode Log Items into a string representation. + +.PARAMETER Collection +An optional PodeLogItemCollection to convert to a string, converting all items in the collection. + +.PARAMETER Item +An optional single PodeLogItem to convert to a string. + +.EXAMPLE +Convert-PodeLogItemToString -Collection $logCollection + +.EXAMPLE +Convert-PodeLogItemToString -Item $logItem +#> +function Convert-PodeLogItemToString { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Collection')] + [Pode.Utilities.Logging.IPodeLogItemCollection] + $Collection, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Item')] + [Pode.Utilities.Logging.IPodeLogItem] + $Item + ) + + process { + switch ($PSCmdlet.ParameterSetName) { + 'Collection' { + return ($Collection.Items.Data | ConvertTo-PodeString) -join ([Environment]::NewLine) + } + 'Item' { + return $Item.Data | ConvertTo-PodeString + } + } } } \ No newline at end of file diff --git a/src/Public/MCP.ps1 b/src/Public/MCP.ps1 index fce0b4c30..cbf677240 100644 --- a/src/Public/MCP.ps1 +++ b/src/Public/MCP.ps1 @@ -93,7 +93,7 @@ Builds a Textual response object for an MCP tool, which can be returned to the c .DESCRIPTION This function creates a hashtable representing a textual response for an MCP tool. The hashtable includes a 'type' key with the value 'text', and a 'text' key containing the provided value. If the provided value is not a string, -it will be converted to a string using Out-String. +it will be converted to a string. .PARAMETER Value The value to be included in the textual response. This can be any object, which will be converted to a string if necessary. @@ -116,16 +116,9 @@ function New-PodeMcpTextContent { $Value ) - if ($null -eq $Value) { - $Value = [string]::Empty - } - elseif ($Value -isnot [string]) { - $Value = $Value | Out-String - } - return @{ type = 'text' - text = $Value + text = $Value | ConvertTo-PodeString } } diff --git a/src/Public/OpenApi.ps1 b/src/Public/OpenApi.ps1 index 1f8d5ed77..9c1d7a6c4 100644 --- a/src/Public/OpenApi.ps1 +++ b/src/Public/OpenApi.ps1 @@ -790,7 +790,7 @@ function Remove-PodeOAResponse { .PARAMETER AllowNonStandardBody Allows methods like DELETE and GET to include a request body, which is generally discouraged by RFC 7231. - This can be used to relax the default restriction and enable a body for HTTP methods that don’t typically support it. + This can be used to relax the default restriction and enable a body for HTTP methods that don't typically support it. .PARAMETER PassThru If specified, returns the original route object for additional chaining after setting the request properties. diff --git a/src/Public/SSE.ps1 b/src/Public/SSE.ps1 index 636292fc6..ac74575c4 100644 --- a/src/Public/SSE.ps1 +++ b/src/Public/SSE.ps1 @@ -862,7 +862,7 @@ function Register-PodeSseEvent { $Name, [Parameter(Mandatory = $true)] - [PodeClientConnectionEventType] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType] $Type, [Parameter(Mandatory = $true)] @@ -942,7 +942,7 @@ function Unregister-PodeSseEvent { $Name, [Parameter(Mandatory = $true)] - [PodeClientConnectionEventType] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType] $Type, [Parameter(Mandatory = $true)] @@ -994,7 +994,7 @@ function Test-PodeSseEvent { $Name, [Parameter(Mandatory = $true)] - [PodeClientConnectionEventType[]] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType[]] $Type, [Parameter()] @@ -1038,7 +1038,7 @@ function Get-PodeSseEvent { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [PodeClientConnectionEventType[]] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType[]] $Type, [Parameter()] diff --git a/src/Public/Signals.ps1 b/src/Public/Signals.ps1 index 0357fedef..a522343dd 100644 --- a/src/Public/Signals.ps1 +++ b/src/Public/Signals.ps1 @@ -843,7 +843,7 @@ function Register-PodeSignalEvent { $Name, [Parameter(Mandatory = $true)] - [PodeClientConnectionEventType] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType] $Type, [Parameter(Mandatory = $true)] @@ -922,7 +922,7 @@ function Unregister-PodeSignalEvent { $Name, [Parameter(Mandatory = $true)] - [PodeClientConnectionEventType] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType] $Type, [Parameter(Mandatory = $true)] @@ -974,7 +974,7 @@ function Test-PodeSignalEvent { $Name, [Parameter(Mandatory = $true)] - [PodeClientConnectionEventType[]] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType[]] $Type, [Parameter()] @@ -1018,7 +1018,7 @@ function Get-PodeSignalEvent { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [PodeClientConnectionEventType[]] + [Pode.Protocols.Http.Client.PodeClientConnectionEventType[]] $Type, [Parameter()] diff --git a/src/Public/Utilities.ps1 b/src/Public/Utilities.ps1 index 3300207e5..51b423978 100644 --- a/src/Public/Utilities.ps1 +++ b/src/Public/Utilities.ps1 @@ -73,6 +73,42 @@ function Get-PodeServerPath { return $PodeContext.Server.Root } +<# +.SYNOPSIS +Returns the name of the server. + +.DESCRIPTION +Returns the name of the server. + +.EXAMPLE +$serverName = Get-PodeServerName +#> +function Get-PodeServerName { + [CmdletBinding()] + [OutputType([string])] + param() + + return $PodeContext.Server.Name +} + +<# +.SYNOPSIS +Returns the name of the application. + +.DESCRIPTION +Returns the name of the application. + +.EXAMPLE +$appName = Get-PodeAppName +#> +function Get-PodeAppName { + [CmdletBinding()] + [OutputType([string])] + param() + + return $PodeContext.Server.AppName +} + <# .SYNOPSIS Starts a Stopwatch on some ScriptBlock, and outputs the duration at the end. @@ -460,46 +496,51 @@ function Import-PodeSnapin { <# .SYNOPSIS - Resolves and protects a value by ensuring it defaults to a specified fallback and optionally parses it as an enum. +Resolves and protects a value by ensuring it defaults to a specified fallback and optionally parses it as an enum. .DESCRIPTION - The `Protect-PodeValue` function ensures that a given value is resolved. If the value is empty, a default value is used instead. - Additionally, the function can parse the resolved value as an enum type with optional case sensitivity. +This function ensures that a given value is not null or empty. If the value is empty, a default value is used instead. +Additionally, the function can parse the resolved value as an enum type with optional case sensitivity. .PARAMETER Value - The input value to be resolved. +The input value to be resolved. .PARAMETER Default - The default value to fall back to if the input value is empty. +The default value to fall back to if the input value is empty. .PARAMETER EnumType - The type of enum to parse the resolved value into. If specified, the resolved value must be a valid enum member. +The type of enum to parse the resolved value into. If specified, the resolved value must be a valid enum member. .PARAMETER CaseSensitive - Specifies whether the enum parsing should be case-sensitive. By default, parsing is case-insensitive. +Specifies whether the enum parsing should be case-sensitive. By default, parsing is case-insensitive. + +.PARAMETER AllowNullEnum +If specified, allows the resolved value to be null or empty when parsing as an enum. +If not specified, a null or empty value will throw an error. .OUTPUTS - [object] - Returns the resolved value, either as the original value, the default value, or a parsed enum. +[object] +Returns the resolved value, either as the original value, the default value, or a parsed enum. .EXAMPLE - # Example 1: Resolve a value with a default fallback - $resolved = Protect-PodeValue -Value $null -Default "Fallback" - Write-Output $resolved # Output: Fallback +# Example 1: Resolve a value with a default fallback +$resolved = Protect-PodeValue -Value $null -Default "Fallback" +Write-Output $resolved # Output: Fallback .EXAMPLE - # Example 2: Resolve and parse a value as a case-insensitive enum - $resolvedEnum = Protect-PodeValue -Value "red" -Default "Blue" -EnumType ([type][System.ConsoleColor]) - Write-Output $resolvedEnum # Output: Red +# Example 2: Resolve and parse a value as a case-insensitive enum +$resolvedEnum = Protect-PodeValue -Value "red" -Default "Blue" -EnumType ([System.ConsoleColor]) +Write-Output $resolvedEnum # Output: Red .EXAMPLE - # Example 3: Resolve and parse a value as a case-sensitive enum - $resolvedEnum = Protect-PodeValue -Value "red" -Default "Blue" -EnumType ([type][System.ConsoleColor]) -CaseSensitive - # Throws an error if "red" does not match an enum member exactly (case-sensitive). - -.NOTES - This function resolves values using `Resolve-PodeValue` and validates enums using `[enum]::IsDefined`. +# Example 3: Resolve and parse a value as a case-sensitive enum +$resolvedEnum = Protect-PodeValue -Value "red" -Default "Blue" -EnumType ([System.ConsoleColor]) -CaseSensitive +# Throws an error if "red" does not match an enum member exactly (case-sensitive). +.EXAMPLE +# Example 4: Allow null or empty enum value +$resolvedEnum = Protect-PodeValue -Value "" -Default $null -EnumType ([System.ConsoleColor]) -AllowNullEnum +Write-Output $resolvedEnum # Output: null #> function Protect-PodeValue { [CmdletBinding()] @@ -516,13 +557,20 @@ function Protect-PodeValue { $EnumType, [switch] - $CaseSensitive + $CaseSensitive, + + [switch] + $AllowNullEnum ) $resolvedValue = Resolve-PodeValue -Check (Test-PodeIsEmpty $Value) -TrueValue $Default -FalseValue $Value - if ($null -ne $EnumType -and [enum]::IsDefined($EnumType, $resolvedValue)) { - # Use $CaseSensitive to determine if case sensitivity should apply + if ($null -ne $EnumType) { + # -and ![string]::IsNullOrWhiteSpace($resolvedValue) -and [enum]::IsDefined($EnumType, $resolvedValue)) { + if ($AllowNullEnum -and [string]::IsNullOrWhiteSpace($resolvedValue)) { + return $null + } + return [enum]::Parse($EnumType, $resolvedValue, !$CaseSensitive.IsPresent) } @@ -1008,7 +1056,7 @@ function Write-PodeHost { # explode object if needed if ($Explode) { - $strObject = ($Object | Out-String).TrimEnd() + $strObject = ($Object | ConvertTo-PodeString).TrimEnd() $meta = @() # add label if needed @@ -1689,5 +1737,49 @@ function Start-PodeSleep { } } +<# +.SYNOPSIS +Converts an input object to a string representation. + +.DESCRIPTION +This function takes an input object and converts it to a string representation. +It handles various types of input, including null values, strings, value types, and other objects. +The resulting string is trimmed of any trailing newlines in the case of complex objects. + +.PARAMETER InputObject +The object to be converted to a string. + +.EXAMPLE +$stringValue = ConvertTo-PodeString -InputObject $someObject + +.EXAMPLE +$stringValue = $someObject | ConvertTo-PodeString +#> +function ConvertTo-PodeString { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(ValueFromPipeline = $true)] + $InputObject + ) + process { + # empty for nulls + if ([string]::IsNullOrEmpty($InputObject)) { + return [string]::Empty + } + + # return if a string + if ($InputObject -is [string]) { + return $InputObject + } + # ToString for value types + if ($InputObject -is [System.ValueType]) { + return $InputObject.ToString() + } + + # convert other types to string, and trim any trailing newlines + return ($InputObject | Out-String).TrimEnd("`r", "`n") + } +} \ No newline at end of file diff --git a/tests/integration/Authentication.Tests.ps1 b/tests/integration/Authentication.Tests.ps1 index 90e13b114..df3e5a988 100644 --- a/tests/integration/Authentication.Tests.ps1 +++ b/tests/integration/Authentication.Tests.ps1 @@ -14,7 +14,7 @@ Describe 'Authentication Requests' { Start-PodeServer -Quiet -ScriptBlock { Add-PodeEndpoint -Address localhost -Port $using:Port -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeRoute -Method Get -Path '/close' -ScriptBlock { Close-PodeServer } diff --git a/tests/integration/Endpoints.Tests.ps1 b/tests/integration/Endpoints.Tests.ps1 index e8b72c100..91d87d89e 100644 --- a/tests/integration/Endpoints.Tests.ps1 +++ b/tests/integration/Endpoints.Tests.ps1 @@ -17,7 +17,7 @@ Describe 'Endpoint Requests' { Add-PodeEndpoint -Address localhost -Port $using:Port1 -Protocol Http -Name 'Endpoint1' Add-PodeEndpoint -Address localhost -Port $using:Port2 -Protocol Http -Name 'Endpoint2' - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeRoute -Method Get -Path '/close' -ScriptBlock { Close-PodeServer } diff --git a/tests/integration/RestApi.Https.Tests.ps1 b/tests/integration/RestApi.Https.Tests.ps1 index 14347ee84..1158e090f 100644 --- a/tests/integration/RestApi.Https.Tests.ps1 +++ b/tests/integration/RestApi.Https.Tests.ps1 @@ -44,7 +44,7 @@ Describe 'REST API HTTPS Requests' { Start-PodeServer -RootPath $using:PSScriptRoot -Quiet -ScriptBlock { Add-PodeEndpoint -Address localhost -Port $using:Port -Protocol Https -SelfSigned - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeRoute -Method Get -Path '/close' -ScriptBlock { Close-PodeServer } diff --git a/tests/integration/RestApi.Tests.ps1 b/tests/integration/RestApi.Tests.ps1 index 6046eb93a..a66d2ce40 100644 --- a/tests/integration/RestApi.Tests.ps1 +++ b/tests/integration/RestApi.Tests.ps1 @@ -18,7 +18,7 @@ Describe 'REST API Requests' { Start-PodeServer -RootPath $using:PSScriptRoot -Quiet -ScriptBlock { Add-PodeEndpoint -Address localhost -Port $using:Port -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeRoute -Method Get -Path '/close' -ScriptBlock { Close-PodeServer } diff --git a/tests/integration/Schedules.Tests.ps1 b/tests/integration/Schedules.Tests.ps1 index 1a1f26ee6..675baf4b3 100644 --- a/tests/integration/Schedules.Tests.ps1 +++ b/tests/integration/Schedules.Tests.ps1 @@ -14,7 +14,7 @@ Describe 'Schedules' { Start-PodeServer -RootPath $using:PSScriptRoot -Quiet -ScriptBlock { Add-PodeEndpoint -Address localhost -Port $using:Port -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeRoute -Method Get -Path '/close' -ScriptBlock { Close-PodeServer } diff --git a/tests/integration/Timers.Tests.ps1 b/tests/integration/Timers.Tests.ps1 index d41b207a8..d2f214605 100644 --- a/tests/integration/Timers.Tests.ps1 +++ b/tests/integration/Timers.Tests.ps1 @@ -13,7 +13,7 @@ Describe 'Timers' { Start-PodeServer -RootPath $using:PSScriptRoot -Quiet -ScriptBlock { Add-PodeEndpoint -Address localhost -Port $using:Port -Protocol Http - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeRoute -Method Get -Path '/close' -ScriptBlock { Close-PodeServer } diff --git a/tests/integration/WebSocket.Tests.ps1 b/tests/integration/WebSocket.Tests.ps1 index 059835c2c..d4bfc203d 100644 --- a/tests/integration/WebSocket.Tests.ps1 +++ b/tests/integration/WebSocket.Tests.ps1 @@ -15,7 +15,7 @@ Describe 'WebSocket' { Add-PodeEndpoint -Address localhost -Port $using:Port -Protocol Http Add-PodeEndpoint -Address localhost -Port $using:Port -Protocol Ws - New-PodeLogTerminalMethod | Enable-PodeErrorLogType + New-PodeLogTerminalMethod | Enable-PodeLogErrorType Add-PodeRoute -Method Get -Path '/close' -ScriptBlock { Close-PodeServer } diff --git a/tests/unit/Helpers.Tests.ps1 b/tests/unit/Helpers.Tests.ps1 index 66eaa773a..c8e9f20fd 100644 --- a/tests/unit/Helpers.Tests.ps1 +++ b/tests/unit/Helpers.Tests.ps1 @@ -1821,4 +1821,41 @@ InModuleScope -ModuleName 'Pode' { Protect-PodePath -Path '/assets/[brackets].txt' -NoEscape | Should -Be '/assets/[brackets].txt' } } + + Describe 'ConvertTo-PodeString' { + It 'Returns empty for null' { + ConvertTo-PodeString -InputObject $null | Should -Be '' + } + + It 'Returns empty for empty string' { + ConvertTo-PodeString -InputObject '' | Should -Be '' + } + + It 'Returns string for string' { + ConvertTo-PodeString -InputObject 'Hello' | Should -Be 'Hello' + } + + It 'Returns string for number' { + ConvertTo-PodeString -InputObject 123 | Should -Be '123' + } + + It 'Returns string for boolean' { + ConvertTo-PodeString -InputObject $true | Should -Be 'True' + ConvertTo-PodeString -InputObject $false | Should -Be 'False' + } + + It 'Returns string for array' { + $array = @(1, 2, 3) + $strArray = ($array | Out-String).TrimEnd("`r", "`n") + + ConvertTo-PodeString -InputObject $array | Should -Be $strArray + } + + It 'Returns string for hashtable' { + $hashtable = @{ Key1 = 'Value1'; Key2 = 'Value2' } + $strHashtable = ($hashtable | Out-String).TrimEnd("`r", "`n") + + ConvertTo-PodeString -InputObject $hashtable | Should -Be $strHashtable + } + } } \ No newline at end of file diff --git a/tests/unit/Logging.Tests.ps1 b/tests/unit/Logging.Tests.ps1 index 597121dd4..6d0fe4b07 100644 --- a/tests/unit/Logging.Tests.ps1 +++ b/tests/unit/Logging.Tests.ps1 @@ -54,10 +54,10 @@ InModuleScope -ModuleName 'Pode' { $PodeContext.Server.Logging.Logger.Count | Should -Be 1 - $logItem = $null - $PodeContext.Server.Logging.Logger.TryTake([ref]$logItem, [System.Threading.CancellationToken]::None) | Should -Be $true - $logItem.Name | Should -Be 'test' - $logItem.Item | Should -Be 'test' + $logEvent = $null + $PodeContext.Server.Logging.Logger.TryTake([ref]$logEvent, [System.Threading.CancellationToken]::None) | Should -Be $true + $logEvent.Name | Should -Be 'test' + $logEvent.Data | Should -Be 'test' } } @@ -98,9 +98,9 @@ InModuleScope -ModuleName 'Pode' { $PodeContext.Server.Logging.Logger.Count | Should -Be 1 - $logItem = $null - $PodeContext.Server.Logging.Logger.TryTake([ref]$logItem, [System.Threading.CancellationToken]::None) | Should -Be $true - $logItem.Item.Message | Should -Be 'some error' + $logEvent = $null + $PodeContext.Server.Logging.Logger.TryTake([ref]$logEvent, [System.Threading.CancellationToken]::None) | Should -Be $true + $logEvent.Data.Message | Should -Be 'some error' } It 'Adds an exception log item' { @@ -110,9 +110,9 @@ InModuleScope -ModuleName 'Pode' { $exp = [exception]::new('some error') Write-PodeErrorLog -Exception $exp - $logItem = $null - $PodeContext.Server.Logging.Logger.TryTake([ref]$logItem, [System.Threading.CancellationToken]::None) | Should -Be $true - $logItem.Item.Message | Should -Be 'some error' + $logEvent = $null + $PodeContext.Server.Logging.Logger.TryTake([ref]$logEvent, [System.Threading.CancellationToken]::None) | Should -Be $true + $logEvent.Data.Message | Should -Be 'some error' } It 'Does not log as Verbose not allowed' { @@ -189,4 +189,79 @@ InModuleScope -ModuleName 'Pode' { Protect-PodeLogItem -Item $item | Should -Be 'Password=********Hunter2, Email' } } + + Describe 'ConvertTo-PodeSyslog' { + BeforeAll { + $PodeContext = @{ + Server = @{ + ComputerName = 'localhost' + AppName = 'Pode' + } + } + } + + It 'Converts a log item to RFC5424 syslog format' { + $now = [datetime]::UtcNow + $strNow = $now.ToString('yyyy-MM-ddTHH:mm:ss.fffK') + $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id + + $msg = ConvertTo-PodeSyslog -Message 'example' -Level 'Error' -Timestamp $now + $msg | Should -Be "<131>1 $($strNow) localhost Pode $($processId) - - example" + } + + It 'Converts a log item to RFC5424 syslog format, with custom AppName' { + $now = [datetime]::UtcNow + $strNow = $now.ToString('yyyy-MM-ddTHH:mm:ss.fffK') + $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id + + $msg = ConvertTo-PodeSyslog -Message 'example' -Level 'Error' -Timestamp $now -AppName 'CustomApp' + $msg | Should -Be "<131>1 $($strNow) localhost CustomApp $($processId) - - example" + } + + It 'Converts a log item to RFC5424 syslog format, with Facility' { + $now = [datetime]::UtcNow + $strNow = $now.ToString('yyyy-MM-ddTHH:mm:ss.fffK') + $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id + + $msg = ConvertTo-PodeSyslog -Message 'example' -Level 'Error' -Timestamp $now -Facility 19 + $msg | Should -Be "<155>1 $($strNow) localhost Pode $($processId) - - example" + } + + It 'Converts a log item to RFC5424 syslog format, with Tags' { + $now = [datetime]::UtcNow + $strNow = $now.ToString('yyyy-MM-ddTHH:mm:ss.fffK') + $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id + $tags = @{ tag1 = 'value1' } + + $msg = ConvertTo-PodeSyslog -Message 'example' -Level 'Error' -Timestamp $now -Tags $tags + $msg | Should -Be "<131>1 $($strNow) localhost Pode $($processId) - [tag1=`"value1`"] example" + } + + It 'Converts a log item to RFC3164 syslog format' { + $now = [datetime]::UtcNow + $strNow = $now.ToString('MMM dd HH:mm:ss') + $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id + + $msg = ConvertTo-PodeSyslog -Message 'example' -Level 'Error' -Timestamp $now -Format 'RFC3164' + $msg | Should -Be "<131>$($strNow) localhost Pode[$($processId)]: example" + } + + It 'Converts a log item to RFC3164 syslog format, with custom AppName' { + $now = [datetime]::UtcNow + $strNow = $now.ToString('MMM dd HH:mm:ss') + $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id + + $msg = ConvertTo-PodeSyslog -Message 'example' -Level 'Error' -Timestamp $now -Format 'RFC3164' -AppName 'CustomApp' + $msg | Should -Be "<131>$($strNow) localhost CustomApp[$($processId)]: example" + } + + It 'Converts a log item to RFC3164 syslog format, with Facility' { + $now = [datetime]::UtcNow + $strNow = $now.ToString('MMM dd HH:mm:ss') + $processId = [System.Diagnostics.Process]::GetCurrentProcess().Id + + $msg = ConvertTo-PodeSyslog -Message 'example' -Level 'Error' -Timestamp $now -Format 'RFC3164' -Facility 19 + $msg | Should -Be "<155>$($strNow) localhost Pode[$($processId)]: example" + } + } } \ No newline at end of file