From 50fe5e64a802fcdc08a058119394cb9a79925abe Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Wed, 4 Dec 2024 14:33:27 +0100 Subject: [PATCH] Updated tracelogging cmdlets --- CHANGELOG.md | 2 + MIGRATE-2.0-to-3.0.md | 1 + documentation/Set-PnPTraceLog.md | 179 ---------------------------- documentation/Start-PnPTraceLog.md | 89 ++++++++++++++ documentation/Stop-PnPTraceLog.md | 37 ++++++ src/Commands/Base/BasePSCmdlet.cs | 3 +- src/Commands/Base/GetTraceLog.cs | 39 ++++++ src/Commands/Base/SetTraceLog.cs | 101 ---------------- src/Commands/Base/StartTraceLog.cs | 76 ++++++++++++ src/Commands/Base/StopTraceLog.cs | 39 ++++++ src/Commands/Model/TraceLogEntry.cs | 25 ++++ 11 files changed, 310 insertions(+), 281 deletions(-) delete mode 100644 documentation/Set-PnPTraceLog.md create mode 100644 documentation/Start-PnPTraceLog.md create mode 100644 documentation/Stop-PnPTraceLog.md create mode 100644 src/Commands/Base/GetTraceLog.cs delete mode 100644 src/Commands/Base/SetTraceLog.cs create mode 100644 src/Commands/Base/StartTraceLog.cs create mode 100644 src/Commands/Base/StopTraceLog.cs create mode 100644 src/Commands/Model/TraceLogEntry.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 66ac8d153..5a278650d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `-Force` parameter to `Set-PnPPropertyBagValue` cmdlet to toggle NoScript status of the site. - Added `-Batch` parameter to `Invoke-PnPGraphMethod` cmdlet to allow adding request in a batch. - Added `-List` parameter to `Get-PnPFolderItem`, `Get-PnPFileInFolder` and `Get-PnPFolderInFolder` which allows them to work with a document library containing more than 5,000 items [#4611](https://github.com/pnp/powershell/pull/4611) +- Added `Start-PnPTraceLog`, `Stop-PnPTraceLog` and `Get-PnPTraceLog` cmdlets to handle tracelogging. Removed `Set-PnPTraceLog` cmdlet. ### Changed @@ -111,6 +112,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Removed `-WebLogin` parameter from `Connect-PnPOnline` cmdlet. It was marked obsolete and was a security risk. - Removed `Set-PnPMinimalDownloadStrategy` as it's not applicable anymore to SharePoint Online. If you need the functionality you can always turn on the feature with `Enable-PnPFeature -Id 87294c72-f260-42f3-a41b-981a2ffce37a` or turn it off with `Disable-PnPFeature -Id 87294c72-f260-42f3-a41b-981a2ffce37a` - Removed `-SPOManagementShell` parameter from `Connect-PnPOnline` cmdlet. It reduces the risk of changes coming from Microsoft. Use your own Entra ID app instead. +- Removed `Set-PnPTraceLog` cmdlet and introduced `Start-PnPTraceLog` and `Stop-PnPTraceLog` with similar parameters. ### Contributors diff --git a/MIGRATE-2.0-to-3.0.md b/MIGRATE-2.0-to-3.0.md index d9097cefd..6dfc916b3 100644 --- a/MIGRATE-2.0-to-3.0.md +++ b/MIGRATE-2.0-to-3.0.md @@ -66,6 +66,7 @@ Recommend referring to these 2 links: | Register-PnPEntraIDAppForInteractiveLogin | Removed `-LaunchBrowser`, `-NoPopup` and credential based auth. The default auth method is now Interactive.| | Set-PnPMinimalDownloadStrategy | Removed cmdlet. If you need the functionality you can always turn on the feature with `Enable-PnPFeature -Id 87294c72-f260-42f3-a41b-981a2ffce37a` or turn it off with `Disable-PnPFeature -Id 87294c72-f260-42f3-a41b-981a2ffce37a` | | Connect-PnPOnline | Removed `-SPOManagementShell` option for authentication. It reduces the risk of changes from Microsoft which can potentially break the scripts . Use your own Entra ID app instead via `-ClientId` parameter. | +| Set-PnPTraceLog | Removed cmdlet and introduced `Start-PnPTraceLog` and `Stop-PnPTracelog` with similar parameters and functionality | ## Other notable changes diff --git a/documentation/Set-PnPTraceLog.md b/documentation/Set-PnPTraceLog.md deleted file mode 100644 index 889723df2..000000000 --- a/documentation/Set-PnPTraceLog.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -Module Name: PnP.PowerShell -title: Set-PnPTraceLog -schema: 2.0.0 -applicable: SharePoint Online -external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Set-PnPTraceLog.html ---- - -# Set-PnPTraceLog - -## SYNOPSIS -Turn log tracing on or off - -## SYNTAX - -### On -```powershell -Set-PnPTraceLog -On [-LogFile ] [-WriteToConsole] [-Level ] [-Delimiter ] - [-IndentSize ] [-AutoFlush ] -``` - -### Off -```powershell -Set-PnPTraceLog -Off -``` - -## DESCRIPTION -Defines if tracing should be turned on. PnP Core, which is the foundation of these cmdlets, uses the standard Trace functionality of .NET. With this cmdlet you can turn capturing of this trace to a log file on or off. Notice that basically only the Provisioning Engine writes to the trace log which means that cmdlets related to the engine will produce output. - -## EXAMPLES - -### EXAMPLE 1 -```powershell -Set-PnPTraceLog -On -LogFile traceoutput.txt -``` - -This turns on trace logging to the file 'traceoutput.txt' and will capture events of at least 'Information' level. - -### EXAMPLE 2 -```powershell -Set-PnPTraceLog -On -LogFile traceoutput.txt -Level Debug -``` - -This turns on trace logging to the file 'traceoutput.txt' and will capture debug events. - -### EXAMPLE 3 -```powershell -Set-PnPTraceLog -On -LogFile traceoutput.txt -Level Debug -Delimiter "," -``` - -This turns on trace logging to the file 'traceoutput.txt' and will write the entries as comma separated. Debug events are captured. - -### EXAMPLE 4 -```powershell -Set-PnPTraceLog -Off -``` - -This turns off trace logging. It will flush any remaining messages to the log file. - -## PARAMETERS - -### -AutoFlush -Auto flush the trace log. Defaults to true. - -```yaml -Type: Boolean -Parameter Sets: On - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Delimiter -If specified the trace log entries will be delimited with this value. - -```yaml -Type: String -Parameter Sets: On - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -IndentSize -Indents in the trace log will be with this amount of characters. Defaults to 4. - -```yaml -Type: Int32 -Parameter Sets: On - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Level -The level of events to capture. Possible values are 'Debug', 'Error', 'Warning', 'Information'. Defaults to 'Information'. - -```yaml -Type: LogLevel -Parameter Sets: On -Accepted values: Debug, Error, Warning, Information - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LogFile -The path and filename of the file to write the trace log to. - -```yaml -Type: String -Parameter Sets: On - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Off -Turn off tracing to log file. - -```yaml -Type: SwitchParameter -Parameter Sets: Off - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -On -Turn on tracing to log file - -```yaml -Type: SwitchParameter -Parameter Sets: On - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -WriteToConsole -Turn on console trace output. - -```yaml -Type: SwitchParameter -Parameter Sets: On - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -## RELATED LINKS - -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - diff --git a/documentation/Start-PnPTraceLog.md b/documentation/Start-PnPTraceLog.md new file mode 100644 index 000000000..c55215479 --- /dev/null +++ b/documentation/Start-PnPTraceLog.md @@ -0,0 +1,89 @@ +--- +Module Name: PnP.PowerShell +title: Set-PnPTraceLog +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Set-PnPTraceLog.html +--- + +# Start-PnPTraceLog + +## SYNOPSIS +Starts log tracing + +## SYNTAX + +```powershell +Start-PnPTraceLog [-Path ] [-Level ] [-AutoFlush ] +``` + + +## DESCRIPTION +Starts .NET tracelogging. Many cmdlets output detailed trace information when executed. Turn on the trace log with this cmdlet, optionally specify the level. By default the level is set to 'Information', but you will receive more detail by setting the level to 'Debug'. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Set-PnPTraceLog -Path ./TraceOutput.txt +``` + +This turns on trace logging to the file 'TraceOutput.txt' and will capture events of at least 'Information' level. + +### EXAMPLE 2 +```powershell +Set-PnPTraceLog -Path ./TraceOutput.txt -Level Debug +``` + +This turns on trace logging to the file 'TraceOutput.txt' and will capture debug events. + +## PARAMETERS + +### -AutoFlush +Auto flush the trace log. Defaults to true. + +```yaml +Type: Boolean +Parameter Sets: On + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Level +The level of events to capture. Possible values are 'Debug', 'Error', 'Warning', 'Information'. Defaults to 'Information'. + +```yaml +Type: LogLevel +Parameter Sets: On +Accepted values: Debug, Error, Warning, Information + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Path +The path and filename of the file to write the trace log to. + +```yaml +Type: String +Parameter Sets: On + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + diff --git a/documentation/Stop-PnPTraceLog.md b/documentation/Stop-PnPTraceLog.md new file mode 100644 index 000000000..4e1def620 --- /dev/null +++ b/documentation/Stop-PnPTraceLog.md @@ -0,0 +1,37 @@ +--- +Module Name: PnP.PowerShell +title: Set-PnPTraceLog +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Set-PnPTraceLog.html +--- + +# Stops-PnPTraceLog + +## SYNOPSIS +Stops log tracing and flushes the log buffer if any items in there. + +## SYNTAX + +```powershell +Stop-PnPTraceLog +``` + + +## DESCRIPTION +Stops .NET tracelogging. Many cmdlets output detailed trace information when executed. Turn on the trace log with Start-PnPTraceLog, optionally specify the level. By default the level is set to 'Information', but you will receive more detail by setting the level to 'Debug'. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Stop-PnPTraceLog +``` + +This turns off trace logging + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + diff --git a/src/Commands/Base/BasePSCmdlet.cs b/src/Commands/Base/BasePSCmdlet.cs index 8ae79d240..3ce49090c 100644 --- a/src/Commands/Base/BasePSCmdlet.cs +++ b/src/Commands/Base/BasePSCmdlet.cs @@ -11,6 +11,7 @@ public class BasePSCmdlet : PSCmdlet { protected override void BeginProcessing() { + Framework.Diagnostics.Log.Debug("PnPPowerShell", $"Executing {MyInvocation.MyCommand.Name}"); base.BeginProcessing(); if (MyInvocation.MyCommand.Name.ToLower() != MyInvocation.InvocationName.ToLower()) { @@ -72,7 +73,7 @@ protected override void ProcessRecord() TokenHandler.EnsureRequiredPermissionsAvailableInAccessTokenAudience(this, gex.AccessToken); } } - if(string.IsNullOrWhiteSpace(errorMessage) && gex.HttpResponse != null && gex.HttpResponse.StatusCode == System.Net.HttpStatusCode.Forbidden) + if (string.IsNullOrWhiteSpace(errorMessage) && gex.HttpResponse != null && gex.HttpResponse.StatusCode == System.Net.HttpStatusCode.Forbidden) { errorMessage = "Access denied. Check for the required permissions."; } diff --git a/src/Commands/Base/GetTraceLog.cs b/src/Commands/Base/GetTraceLog.cs new file mode 100644 index 000000000..b05bfb989 --- /dev/null +++ b/src/Commands/Base/GetTraceLog.cs @@ -0,0 +1,39 @@ + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Management.Automation; +using Microsoft.PowerShell.Cmdletization.Xml; +using PnP.PowerShell.Model; + +namespace PnP.PowerShell.Commands.Base +{ + [Cmdlet(VerbsCommon.Get, "PnPTraceLog")] + public class GetTraceLog : PSCmdlet + { + [Parameter(Mandatory = false, Position = 0, ValueFromPipeline = true, ParameterSetName = "On")] + public string Path; + + protected override void ProcessRecord() + { + if (!System.IO.Path.IsPathRooted(Path)) + { + Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); + } + if (File.Exists(Path)) + { + var lines = System.IO.File.ReadAllLines(Path); + foreach (var line in lines) + { + var items = line.Split('\t'); + WriteObject(new TraceLogEntry(items), true); + } + } else { + throw new PSArgumentException($"File {Path} does not exist"); + } + } + + + } +} \ No newline at end of file diff --git a/src/Commands/Base/SetTraceLog.cs b/src/Commands/Base/SetTraceLog.cs deleted file mode 100644 index d10c0706b..000000000 --- a/src/Commands/Base/SetTraceLog.cs +++ /dev/null @@ -1,101 +0,0 @@ - -using System; -using System.Diagnostics; -using System.Management.Automation; - -namespace PnP.PowerShell.Commands.Base -{ - [Cmdlet(VerbsCommon.Set, "PnPTraceLog")] - public class SetTraceLog : PSCmdlet - { - [Parameter(Mandatory = true, ParameterSetName = "On")] - public SwitchParameter On; - - [Parameter(Mandatory = false, ParameterSetName = "On")] - public string LogFile; - - [Parameter(Mandatory = false, ParameterSetName = "On")] - public SwitchParameter WriteToConsole; - - [Parameter(Mandatory = false, ParameterSetName = "On")] - public PnP.Framework.Diagnostics.LogLevel Level = PnP.Framework.Diagnostics.LogLevel.Information; - - [Parameter(Mandatory = false, ParameterSetName = "On")] - public string Delimiter; - - [Parameter(Mandatory = false, ParameterSetName = "On")] - public int IndentSize = 4; - - [Parameter(Mandatory = false, ParameterSetName = "On")] - public bool AutoFlush = true; - - [Parameter(Mandatory = true, ParameterSetName = "Off")] - public SwitchParameter Off; - - private const string FileListenername = "PNPPOWERSHELLFILETRACELISTENER"; - private const string ConsoleListenername = "PNPPOWERSHELLCONSOLETRACELISTENER"; - protected override void ProcessRecord() - { - - if (ParameterSetName == "On") - { - // Setup Console Listener if Console switch has been specified or No file LogFile parameter has been set - if (WriteToConsole.IsPresent || string.IsNullOrEmpty(LogFile)) - { - RemoveListener(ConsoleListenername); - ConsoleTraceListener consoleListener = new ConsoleTraceListener(false); - consoleListener.Name = ConsoleListenername; - Trace.Listeners.Add(consoleListener); - PnP.Framework.Diagnostics.Log.LogLevel = Level; - } - - // Setup File Listener - if (!string.IsNullOrEmpty(LogFile)) - { - RemoveListener(FileListenername); - - if (!System.IO.Path.IsPathRooted(LogFile)) - { - LogFile = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, LogFile); - } - // Create DelimitedListTraceListener in case Delimiter parameter has been specified, if not create TextWritterTraceListener - TraceListener listener = !string.IsNullOrEmpty(Delimiter) ? - new DelimitedListTraceListener(LogFile) { Delimiter = Delimiter, TraceOutputOptions = TraceOptions.DateTime } : - new TextWriterTraceListener(LogFile); - - listener.Name = FileListenername; - Trace.Listeners.Add(listener); - PnP.Framework.Diagnostics.Log.LogLevel = Level; - } - - Trace.AutoFlush = AutoFlush; - Trace.IndentSize = IndentSize; - } - else - { - Trace.Flush(); - RemoveListener(ConsoleListenername); - RemoveListener(FileListenername); - } - } - - private void RemoveListener(string listenerName) - { - try - { - var existingListener = Trace.Listeners[listenerName]; - if (existingListener != null) - { - existingListener.Flush(); - existingListener.Close(); - Trace.Listeners.Remove(existingListener); - } - } - catch (Exception) - { - // ignored - } - - } - } -} \ No newline at end of file diff --git a/src/Commands/Base/StartTraceLog.cs b/src/Commands/Base/StartTraceLog.cs new file mode 100644 index 000000000..099fc4eed --- /dev/null +++ b/src/Commands/Base/StartTraceLog.cs @@ -0,0 +1,76 @@ + +using System; +using System.Diagnostics; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Base +{ + [Cmdlet(VerbsLifecycle.Start, "PnPTraceLog")] + public class StartTraceLog : PSCmdlet + { + [Parameter(Mandatory = false)] + public string Path; + + [Parameter(Mandatory = false)] + public PnP.Framework.Diagnostics.LogLevel Level = PnP.Framework.Diagnostics.LogLevel.Information; + + [Parameter(Mandatory = false)] + public bool AutoFlush = true; + + private const string FileListenername = "PNPPOWERSHELLFILETRACELISTENER"; + private const string ConsoleListenername = "PNPPOWERSHELLCONSOLETRACELISTENER"; + protected override void ProcessRecord() + { + + // Setup Console Listener if Console switch has been specified or No file LogFile parameter has been set + if (string.IsNullOrEmpty(Path)) + { + RemoveListener(ConsoleListenername); + ConsoleTraceListener consoleListener = new ConsoleTraceListener(false); + consoleListener.Name = ConsoleListenername; + Trace.Listeners.Add(consoleListener); + PnP.Framework.Diagnostics.Log.LogLevel = Level; + } + + // Setup File Listener + if (!string.IsNullOrEmpty(Path)) + { + RemoveListener(FileListenername); + + if (!System.IO.Path.IsPathRooted(Path)) + { + Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); + } + // Create DelimitedListTraceListener in case Delimiter parameter has been specified, if not create TextWritterTraceListener + TraceListener listener = new TextWriterTraceListener(Path); + + listener.Name = FileListenername; + Trace.Listeners.Add(listener); + PnP.Framework.Diagnostics.Log.LogLevel = Level; + } + + Trace.AutoFlush = AutoFlush; + Trace.IndentSize = 4; + + } + + private void RemoveListener(string listenerName) + { + try + { + var existingListener = Trace.Listeners[listenerName]; + if (existingListener != null) + { + existingListener.Flush(); + existingListener.Close(); + Trace.Listeners.Remove(existingListener); + } + } + catch (Exception) + { + // ignored + } + + } + } +} \ No newline at end of file diff --git a/src/Commands/Base/StopTraceLog.cs b/src/Commands/Base/StopTraceLog.cs new file mode 100644 index 000000000..edfbb6b07 --- /dev/null +++ b/src/Commands/Base/StopTraceLog.cs @@ -0,0 +1,39 @@ + +using System; +using System.Diagnostics; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Base +{ + [Cmdlet(VerbsLifecycle.Stop, "PnPTraceLog")] + public class StopTraceLog : PSCmdlet + { + private const string FileListenername = "PNPPOWERSHELLFILETRACELISTENER"; + private const string ConsoleListenername = "PNPPOWERSHELLCONSOLETRACELISTENER"; + protected override void ProcessRecord() + { + Trace.Flush(); + RemoveListener(ConsoleListenername); + RemoveListener(FileListenername); + } + + private void RemoveListener(string listenerName) + { + try + { + var existingListener = Trace.Listeners[listenerName]; + if (existingListener != null) + { + existingListener.Flush(); + existingListener.Close(); + Trace.Listeners.Remove(existingListener); + } + } + catch (Exception) + { + // ignored + } + + } + } +} \ No newline at end of file diff --git a/src/Commands/Model/TraceLogEntry.cs b/src/Commands/Model/TraceLogEntry.cs new file mode 100644 index 000000000..0aee70074 --- /dev/null +++ b/src/Commands/Model/TraceLogEntry.cs @@ -0,0 +1,25 @@ +using System; + +namespace PnP.PowerShell.Model +{ + public class TraceLogEntry + { + public DateTime TimeStamp; + public string Category; + // public string Three; + public string Level; + public string Message; + // public string Six; + // public string Seven; + public TraceLogEntry(string[] values) + { + TimeStamp = DateTime.Parse(values[0].Split(" : ")[1]); + Category = values[1].Trim('[', ']'); + // Three = values[2]; + Level = values[3].Trim('[', ']'); + Message = values[4]; + // Six = values[5]; + // Seven = values[6]; + } + } +} \ No newline at end of file