Skip to content

Commit e8e8475

Browse files
authored
[Ignore] Add an easy to use execution timer (#734)
1 parent f88b93e commit e8e8475

File tree

3 files changed

+128
-5
lines changed

3 files changed

+128
-5
lines changed

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

+4-5
Original file line numberDiff line numberDiff line change
@@ -1523,11 +1523,10 @@ private static async Task DelayThenInvokeDiagnostics(
15231523
ScriptFileMarker[] semanticMarkers = null;
15241524
if (isScriptAnalysisEnabled && editorSession.AnalysisService != null)
15251525
{
1526-
Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath);
1527-
1528-
semanticMarkers = await editorSession.AnalysisService.GetSemanticMarkersAsync(scriptFile);
1529-
1530-
Logger.Write(LogLevel.Verbose, "Analysis complete.");
1526+
using (Logger.LogExecutionTime($"Script analysis of {scriptFile.FilePath} completed."))
1527+
{
1528+
semanticMarkers = await editorSession.AnalysisService.GetSemanticMarkersAsync(scriptFile);
1529+
}
15311530
}
15321531
else
15331532
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Runtime.CompilerServices;
4+
using System.Text;
5+
6+
namespace Microsoft.PowerShell.EditorServices.Utility
7+
{
8+
/// <summary>
9+
/// Simple timer to be used with `using` to time executions.
10+
/// </summary>
11+
/// <example>
12+
/// An example showing how ExecutionTimer is intended to be used
13+
/// <code>
14+
/// using (ExecutionTimer.Start(logger, "Execution of MyMethod completed."))
15+
/// {
16+
/// MyMethod(various, arguments);
17+
/// }
18+
/// </code>
19+
/// This will print a message like "Execution of MyMethod completed. [50ms]" to the logs.
20+
/// </example>
21+
public struct ExecutionTimer : IDisposable
22+
{
23+
[ThreadStatic]
24+
private static Stopwatch t_stopwatch;
25+
26+
private readonly ILogger _logger;
27+
28+
private readonly string _message;
29+
30+
private readonly string _callerMemberName;
31+
32+
private readonly string _callerFilePath;
33+
34+
private readonly int _callerLineNumber;
35+
36+
/// <summary>
37+
/// Create a new execution timer and start it.
38+
/// </summary>
39+
/// <param name="logger">The logger to log the execution timer message in.</param>
40+
/// <param name="message">The message to prefix the execution time with.</param>
41+
/// <param name="callerMemberName">The name of the calling method or property.</param>
42+
/// <param name="callerFilePath">The path to the source file of the caller.</param>
43+
/// <param name="callerLineNumber">The line where the timer is called.</param>
44+
/// <returns>A new, started execution timer.</returns>
45+
public static ExecutionTimer Start(
46+
ILogger logger,
47+
string message,
48+
[CallerMemberName] string callerMemberName = null,
49+
[CallerFilePath] string callerFilePath = null,
50+
[CallerLineNumber] int callerLineNumber = -1)
51+
{
52+
var timer = new ExecutionTimer(logger, message, callerMemberName, callerFilePath, callerLineNumber);
53+
t_stopwatch.Start();
54+
return timer;
55+
}
56+
57+
internal ExecutionTimer(
58+
ILogger logger,
59+
string message,
60+
string callerMemberName,
61+
string callerFilePath,
62+
int callerLineNumber)
63+
{
64+
_logger = logger;
65+
_message = message;
66+
_callerMemberName = callerMemberName;
67+
_callerFilePath = callerFilePath;
68+
_callerLineNumber = callerLineNumber;
69+
}
70+
71+
/// <summary>
72+
/// Dispose of the execution timer by stopping the stopwatch and then printing
73+
/// the elapsed time in the logs.
74+
/// </summary>
75+
public void Dispose()
76+
{
77+
t_stopwatch.Stop();
78+
79+
string logMessage = new StringBuilder()
80+
.Append(_message)
81+
.Append(" [")
82+
.Append(t_stopwatch.ElapsedMilliseconds)
83+
.Append("ms]")
84+
.ToString();
85+
86+
t_stopwatch.Reset();
87+
88+
_logger.Write(
89+
LogLevel.Verbose,
90+
logMessage,
91+
callerName: _callerMemberName,
92+
callerSourceFile: _callerFilePath,
93+
callerLineNumber: _callerLineNumber);
94+
}
95+
96+
private Stopwatch Stopwatch => t_stopwatch ?? (t_stopwatch = new Stopwatch());
97+
}
98+
}

src/PowerShellEditorServices/Utility/Logging.cs

+26
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,30 @@ private static LogEventLevel ConvertLogLevel(LogLevel logLevel)
239239
throw new ArgumentException($"Unknown LogLevel: '{logLevel}')", nameof(logLevel));
240240
}
241241
}
242+
243+
/// <summary>
244+
/// Extension method class for the ILogger class.
245+
/// </summary>
246+
public static class ILoggerExtensions
247+
{
248+
/// <summary>
249+
/// Log the amount of time an execution takes. The intended usage is to call this method in the
250+
/// header of a `using` block to time the interior of the block.
251+
/// </summary>
252+
/// <param name="logger">The ILogger to log the execution time with.</param>
253+
/// <param name="message">The message to log about what has been executed.</param>
254+
/// <param name="callerMemberName">The name of the member calling this method.</param>
255+
/// <param name="callerFilePath">The file where this method has been called.</param>
256+
/// <param name="callerLineNumber">The line number where this method has been called.</param>
257+
/// <returns></returns>
258+
public static ExecutionTimer LogExecutionTime(
259+
this ILogger logger,
260+
string message,
261+
[CallerMemberName] string callerMemberName = null,
262+
[CallerFilePath] string callerFilePath = null,
263+
[CallerLineNumber] int callerLineNumber = -1)
264+
{
265+
return ExecutionTimer.Start(logger, message, callerMemberName, callerFilePath, callerLineNumber);
266+
}
267+
}
242268
}

0 commit comments

Comments
 (0)