Skip to content

Add basic diagnostic logging support #19

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 27, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/PowerShellEditorServices.Host/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//

using Microsoft.PowerShell.EditorServices.Transport.Stdio;
using Microsoft.PowerShell.EditorServices.Utility;
using System;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -38,11 +39,30 @@ static void Main(string[] args)
}
}
#endif
// Catch unhandled exceptions for logging purposes
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

// Initialize the logger
// TODO: Set the level based on command line parameter
Logger.Initialize(minimumLogLevel: LogLevel.Verbose);
Logger.Write(LogLevel.Normal, "PowerShell Editor Services Host started!");

// TODO: Select host, console host, and transport based on command line arguments

IHost host = new StdioHost();
host.Start();
}

static void CurrentDomain_UnhandledException(
object sender,
UnhandledExceptionEventArgs e)
{
// Log the exception
Logger.Write(
LogLevel.Error,
string.Format(
"FATAL UNHANDLED EXCEPTION:\r\n\r\n{0}",
e.ExceptionObject.ToString()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ public MessageBase ParseMessage(string messageJson)
// Parse the JSON string to a JObject
JObject messageObject = JObject.Parse(messageJson);

// Log the message
Logger.Write(
LogLevel.Verbose,
string.Format(
"PARSE MESSAGE:\r\n\r\n{0}",
messageObject.ToString(Formatting.Indented)));

// Get the message type and name from the JSON object
if (!this.TryGetMessageTypeAndName(
messageObject,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using Microsoft.PowerShell.EditorServices.Utility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
using System.Text;

Expand All @@ -18,6 +19,10 @@ public class MessageWriter
private bool includeContentLength;
private MessageTypeResolver messageTypeResolver;

private JsonSerializer loggingSerializer =
JsonSerializer.Create(
Constants.JsonSerializerSettings);

#endregion

#region Constructors
Expand Down Expand Up @@ -57,8 +62,18 @@ public void WriteMessage(MessageBase messageToWrite)
// Insert the message's type name before serializing
messageToWrite.PayloadType = messageTypeName;

// Log the JSON representation of the message
Logger.Write(
LogLevel.Verbose,
string.Format(
"WRITE MESSAGE:\r\n\r\n{0}",
JsonConvert.SerializeObject(
messageToWrite,
Formatting.Indented,
Constants.JsonSerializerSettings)));

// Serialize the message
string serializedMessage =
string serializedMessage =
JsonConvert.SerializeObject(
messageToWrite,
Constants.JsonSerializerSettings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
<Compile Include="Session\ScriptFileMarker.cs" />
<Compile Include="Session\ScriptRegion.cs" />
<Compile Include="Session\Workspace.cs" />
<Compile Include="Utility\Logger.cs" />
<Compile Include="Utility\Validate.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
180 changes: 180 additions & 0 deletions src/PowerShellEditorServices/Utility/Logger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;

namespace Microsoft.PowerShell.EditorServices.Utility
{
/// <summary>
/// Defines the level indicators for log messages.
/// </summary>
public enum LogLevel
{
/// <summary>
/// Indicates a verbose log message.
/// </summary>
Verbose,

/// <summary>
/// Indicates a normal, non-verbose log message.
/// </summary>
Normal,

/// <summary>
/// Indicates a warning message.
/// </summary>
Warning,

/// <summary>
/// Indicates an error message.
/// </summary>
Error
}

/// <summary>
/// Provides a simple logging interface. May be replaced with a
/// more robust solution at a later date.
/// </summary>
public static class Logger
{
private static LogWriter logWriter;

/// <summary>
/// Initializes the Logger for the current session.
/// </summary>
/// <param name="logFilePath">
/// Optional. Specifies the path at which log messages will be written.
/// </param>
/// <param name="minimumLogLevel">
/// Optional. Specifies the minimum log message level to write to the log file.
/// </param>
public static void Initialize(
string logFilePath = "EditorServices.log",
LogLevel minimumLogLevel = LogLevel.Normal)
{
if (logWriter != null)
{
logWriter.Dispose();
}

// TODO: Parameterize this
logWriter =
new LogWriter(
minimumLogLevel,
logFilePath,
true);
}

/// <summary>
/// Closes the Logger.
/// </summary>
public static void Close()
{
if (logWriter != null)
{
logWriter.Dispose();
}
}

/// <summary>
/// Writes a message to the log file.
/// </summary>
/// <param name="logLevel">The level at which the message will be written.</param>
/// <param name="logMessage">The message text to be written.</param>
/// <param name="callerName">The name of the calling method.</param>
/// <param name="callerSourceFile">The source file path where the calling method exists.</param>
/// <param name="callerLineNumber">The line number of the calling method.</param>
public static void Write(
LogLevel logLevel,
string logMessage,
[CallerMemberName] string callerName = null,
[CallerFilePath] string callerSourceFile = null,
[CallerLineNumber] int callerLineNumber = 0)
{
if (logWriter != null)
{
logWriter.Write(
logLevel,
logMessage,
callerName,
callerSourceFile,
callerLineNumber);
}
}
}

internal class LogWriter : IDisposable
{
private TextWriter textWriter;
private LogLevel minimumLogLevel = LogLevel.Verbose;

public LogWriter(LogLevel minimumLogLevel, string logFilePath, bool deleteExisting)
{
this.minimumLogLevel = minimumLogLevel;

// Ensure that we have a usable log file path
if (!Path.IsPathRooted(logFilePath))
{
logFilePath =
Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
logFilePath);
}

// Open the log file for writing with UTF8 encoding
this.textWriter =
new StreamWriter(
new FileStream(
logFilePath,
deleteExisting ?
FileMode.Create :
FileMode.Append),
Encoding.UTF8);
}

public void Write(
LogLevel logLevel,
string logMessage,
string callerName = null,
string callerSourceFile = null,
int callerLineNumber = 0)
{
if (logLevel >= this.minimumLogLevel)
{
// Print the timestamp and log level
this.textWriter.WriteLine(
"{0} [{1}] - Method \"{2}\" at line {3} of {4}\r\n",
DateTime.Now,
logLevel.ToString().ToUpper(),
callerName,
callerLineNumber,
callerSourceFile);

// Print out indented message lines
foreach (var messageLine in logMessage.Split('\n'))
{
this.textWriter.WriteLine(" " + messageLine.TrimEnd());
}

// Finish with a newline and flush the writer
this.textWriter.WriteLine();
this.textWriter.Flush();
}
}

public void Dispose()
{
if (this.textWriter != null)
{
this.textWriter.Flush();
this.textWriter.Dispose();
this.textWriter = null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Console\ConsoleServiceTests.cs" />
<Compile Include="Session\ScriptFileTests.cs" />
<Compile Include="Utility\LoggerTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
Expand Down
Loading