From 8b78b4f42158723b7f2d60f17bf4c8a44c27168a Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Tue, 6 Feb 2018 16:28:15 -0800 Subject: [PATCH] Cleaned up logging in build server --- ...etCore.Razor.Design.CodeGeneration.targets | 2 + .../DotnetToolTask.cs | 41 ++++++++++++++----- .../Microsoft.AspNetCore.Razor.Tasks.csproj | 6 --- .../Client.cs | 17 ++++---- .../CompilerHost.cs | 40 +++++++++++++----- .../ConnectionHost.cs | 17 ++++---- .../DefaultRequestDispatcher.cs | 19 ++++----- .../Roslyn/PlatformInformation.cs | 17 -------- .../ServerProtocol/ServerConnection.cs | 33 +++++++++------ .../ServerLogger.cs} | 33 +++++++-------- .../ServerProtocol/ServerProtocol.cs | 9 ++-- .../ServerProtocol/ServerRequest.cs | 23 +++++------ .../ServerProtocol/ServerResponse.cs | 11 +++-- .../BuildServerIntegrationTest.cs | 6 +-- .../Infrastructure/ServerUtilities.cs | 1 - 15 files changed, 149 insertions(+), 126 deletions(-) delete mode 100644 src/Microsoft.AspNetCore.Razor.Tools/Roslyn/PlatformInformation.cs rename src/Microsoft.AspNetCore.Razor.Tools/{Roslyn/CompilerServerLogger.cs => ServerProtocol/ServerLogger.cs} (84%) diff --git a/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets b/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets index 09ffa3e51a..699b4608f3 100644 --- a/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets +++ b/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets @@ -71,6 +71,7 @@ DebugTool="$(_RazorDebugTagHelperTool)" ToolAssembly="$(_RazorToolAssembly)" UseServer="$(UseRazorBuildServer)" + ForceServer="$(_RazorForceBuildServer)" PipeName="$(_RazorBuildServerPipeName)" Assemblies="@(RazorReferencePath)" ProjectRoot="$(MSBuildProjectDirectory)" @@ -118,6 +119,7 @@ DebugTool="$(_RazorDebugGenerateCodeTool)" ToolAssembly="$(_RazorToolAssembly)" UseServer="$(UseRazorBuildServer)" + ForceServer="$(_RazorForceBuildServer)" PipeName="$(_RazorBuildServerPipeName)" Sources="@(RazorGenerateWithTargetPath)" ProjectRoot="$(MSBuildProjectDirectory)" diff --git a/src/Microsoft.AspNetCore.Razor.Tasks/DotnetToolTask.cs b/src/Microsoft.AspNetCore.Razor.Tasks/DotnetToolTask.cs index 5b28b3eca9..2539d3e5ed 100644 --- a/src/Microsoft.AspNetCore.Razor.Tasks/DotnetToolTask.cs +++ b/src/Microsoft.AspNetCore.Razor.Tasks/DotnetToolTask.cs @@ -5,14 +5,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Threading; using Microsoft.AspNetCore.Razor.Tools; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -using Microsoft.CodeAnalysis.CommandLine; using Microsoft.Extensions.CommandLineUtils; -using Roslyn.Utilities; namespace Microsoft.AspNetCore.Razor.Tasks { @@ -29,6 +26,9 @@ namespace Microsoft.AspNetCore.Razor.Tasks public bool UseServer { get; set; } + // Specifies whether we should fallback to in-process execution if server execution fails. + public bool ForceServer { get; set; } + public string PipeName { get; set; } protected override string ToolName => "dotnet"; @@ -115,11 +115,11 @@ namespace Microsoft.AspNetCore.Razor.Tasks string commandLineCommands, out int result) { - CompilerServerLogger.Log("Server execution started."); + Log.LogMessage(StandardOutputLoggingImportance, "Server execution started."); using (_razorServerCts = new CancellationTokenSource()) { - CompilerServerLogger.Log($"CommandLine = '{commandLineCommands}'"); - CompilerServerLogger.Log($"ServerResponseFile = '{responseFileCommands}'"); + Log.LogMessage(StandardOutputLoggingImportance, $"CommandLine = '{commandLineCommands}'"); + Log.LogMessage(StandardOutputLoggingImportance, $"ServerResponseFile = '{responseFileCommands}'"); // The server contains the tools for discovering tag helpers and generating Razor code. var clientDir = Path.GetDirectoryName(ToolAssembly); @@ -141,14 +141,35 @@ namespace Microsoft.AspNetCore.Razor.Tasks { result = completedResponse.ReturnCode; - CompilerServerLogger.Log($"Server execution completed with return code {result}."); + if (result == 0) + { + Log.LogMessage(StandardOutputLoggingImportance, $"Server execution completed with return code {result}."); + return true; + } + else + { + Log.LogMessage( + StandardOutputLoggingImportance, + $"Server execution completed with return code {result}. For more info, check the server log file in the location specified by the RAZORBUILDSERVER_LOG environment variable."); + } + } + else + { + Log.LogMessage( + StandardOutputLoggingImportance, + $"Server execution failed with response {response.Type}. For more info, check the server log file in the location specified by the RAZORBUILDSERVER_LOG environment variable."); + } + result = -1; + + if (ForceServer) + { + // We don't want to fallback to in-process execution. return true; } - } - CompilerServerLogger.Log("Server execution failed."); - result = -1; + Log.LogMessage(StandardOutputLoggingImportance, "Fallback to in-process execution."); + } return false; } diff --git a/src/Microsoft.AspNetCore.Razor.Tasks/Microsoft.AspNetCore.Razor.Tasks.csproj b/src/Microsoft.AspNetCore.Razor.Tasks/Microsoft.AspNetCore.Razor.Tasks.csproj index b868310b42..79eefc909b 100644 --- a/src/Microsoft.AspNetCore.Razor.Tasks/Microsoft.AspNetCore.Razor.Tasks.csproj +++ b/src/Microsoft.AspNetCore.Razor.Tasks/Microsoft.AspNetCore.Razor.Tasks.csproj @@ -16,12 +16,6 @@ - - Shared\CompilerServerLogger.cs - - - Shared\PlatformInformation.cs - Shared\ServerProtocol\%(FileName) diff --git a/src/Microsoft.AspNetCore.Razor.Tools/Client.cs b/src/Microsoft.AspNetCore.Razor.Tools/Client.cs index 9a0334b8db..4f7903dec5 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/Client.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/Client.cs @@ -6,7 +6,6 @@ using System.IO; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CommandLine; namespace Microsoft.AspNetCore.Razor.Tools { @@ -39,12 +38,12 @@ namespace Microsoft.AspNetCore.Razor.Tools // Machine-local named pipes are named "\\.\pipe\". // We use the SHA1 of the directory the compiler exes live in as the pipe name. // The NamedPipeClientStream class handles the "\\.\pipe\" part for us. - CompilerServerLogger.Log("Attempt to open named pipe '{0}'", pipeName); + ServerLogger.Log("Attempt to open named pipe '{0}'", pipeName); var stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); cancellationToken.ThrowIfCancellationRequested(); - CompilerServerLogger.Log("Attempt to connect named pipe '{0}'", pipeName); + ServerLogger.Log("Attempt to connect named pipe '{0}'", pipeName); try { await stream.ConnectAsync(timeoutMilliseconds, cancellationToken); @@ -55,11 +54,11 @@ namespace Microsoft.AspNetCore.Razor.Tools // From docs: // - TimeoutException: Could not connect to the server within the specified timeout period. // - IOException: The server is connected to another client and the time-out period has expired. - CompilerServerLogger.Log($"Connecting to server timed out after {timeoutMilliseconds} ms"); + ServerLogger.Log($"Connecting to server timed out after {timeoutMilliseconds} ms"); return null; } - CompilerServerLogger.Log("Named pipe '{0}' connected", pipeName); + ServerLogger.Log("Named pipe '{0}' connected", pipeName); cancellationToken.ThrowIfCancellationRequested(); // The original code in Roslyn checks that the server pipe is owned by the same user for security. @@ -70,7 +69,7 @@ namespace Microsoft.AspNetCore.Razor.Tools } catch (Exception e) when (!(e is TaskCanceledException || e is OperationCanceledException)) { - CompilerServerLogger.LogException(e, "Exception while connecting to process"); + ServerLogger.LogException(e, "Exception while connecting to process"); return null; } } @@ -108,9 +107,9 @@ namespace Microsoft.AspNetCore.Razor.Tools try { - CompilerServerLogger.Log($"Before poking pipe {Identifier}."); + ServerLogger.Log($"Before poking pipe {Identifier}."); await Stream.ReadAsync(Array.Empty(), 0, 0, cancellationToken); - CompilerServerLogger.Log($"After poking pipe {Identifier}."); + ServerLogger.Log($"After poking pipe {Identifier}."); } catch (OperationCanceledException) { @@ -119,7 +118,7 @@ namespace Microsoft.AspNetCore.Razor.Tools { // It is okay for this call to fail. Errors will be reflected in the // IsConnected property which will be read on the next iteration. - CompilerServerLogger.LogException(e, $"Error poking pipe {Identifier}."); + ServerLogger.LogException(e, $"Error poking pipe {Identifier}."); } } } diff --git a/src/Microsoft.AspNetCore.Razor.Tools/CompilerHost.cs b/src/Microsoft.AspNetCore.Razor.Tools/CompilerHost.cs index a00ad04aa4..c4b96df6b2 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/CompilerHost.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/CompilerHost.cs @@ -2,9 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; -using Microsoft.CodeAnalysis.CommandLine; namespace Microsoft.AspNetCore.Razor.Tools { @@ -26,13 +26,33 @@ namespace Microsoft.AspNetCore.Razor.Tools return new RejectedServerResponse(); } + var exitCode = 0; + var output = string.Empty; var app = new Application(cancellationToken); var commandArgs = parsed.args.ToArray(); - var exitCode = app.Execute(commandArgs); - var output = app.Out.ToString() ?? string.Empty; + if (ServerLogger.IsLoggingEnabled) + { + using (var writer = new StringWriter()) + { + app.Out = writer; + app.Error = writer; + exitCode = app.Execute(commandArgs); + output = writer.ToString(); + ServerLogger.Log(output); + } + } + else + { + using (var writer = new StreamWriter(Stream.Null)) + { + app.Out = writer; + app.Error = writer; + exitCode = app.Execute(commandArgs); + } + } - return new CompletedServerResponse(exitCode, utf8output: false, output: output); + return new CompletedServerResponse(exitCode, utf8output: false, output: string.Empty); } private bool TryParseArguments(ServerRequest request, out (string workingDirectory, string tempDirectory, string[] args) parsed) @@ -59,16 +79,16 @@ namespace Microsoft.AspNetCore.Razor.Tools } } - CompilerServerLogger.Log($"WorkingDirectory = '{workingDirectory}'"); - CompilerServerLogger.Log($"TempDirectory = '{tempDirectory}'"); + ServerLogger.Log($"WorkingDirectory = '{workingDirectory}'"); + ServerLogger.Log($"TempDirectory = '{tempDirectory}'"); for (var i = 0; i < args.Count; i++) { - CompilerServerLogger.Log($"Argument[{i}] = '{request.Arguments[i]}'"); + ServerLogger.Log($"Argument[{i}] = '{request.Arguments[i]}'"); } if (string.IsNullOrEmpty(workingDirectory)) { - CompilerServerLogger.Log($"Rejecting build due to missing working directory"); + ServerLogger.Log($"Rejecting build due to missing working directory"); parsed = default; return false; @@ -76,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Tools if (string.IsNullOrEmpty(tempDirectory)) { - CompilerServerLogger.Log($"Rejecting build due to missing temp directory"); + ServerLogger.Log($"Rejecting build due to missing temp directory"); parsed = default; return false; @@ -84,7 +104,7 @@ namespace Microsoft.AspNetCore.Razor.Tools if (string.IsNullOrEmpty(tempDirectory)) { - CompilerServerLogger.Log($"Rejecting build due to missing temp directory"); + ServerLogger.Log($"Rejecting build due to missing temp directory"); parsed = default; return false; diff --git a/src/Microsoft.AspNetCore.Razor.Tools/ConnectionHost.cs b/src/Microsoft.AspNetCore.Razor.Tools/ConnectionHost.cs index 7da0a05497..6bda4338a8 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/ConnectionHost.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/ConnectionHost.cs @@ -6,7 +6,6 @@ using System.IO; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CommandLine; namespace Microsoft.AspNetCore.Razor.Tools { @@ -58,13 +57,13 @@ namespace Microsoft.AspNetCore.Razor.Tools PipeBufferSize, // Default input buffer PipeBufferSize);// Default output buffer - CompilerServerLogger.Log("Waiting for new connection"); + ServerLogger.Log("Waiting for new connection"); await pipeStream.WaitForConnectionAsync(cancellationToken); - CompilerServerLogger.Log("Pipe connection detected."); + ServerLogger.Log("Pipe connection detected."); if (Environment.Is64BitProcess || Memory.IsMemoryAvailable()) { - CompilerServerLogger.Log("Memory available - accepting connection"); + ServerLogger.Log("Memory available - accepting connection"); return new NamedPipeConnection(pipeStream, GetNextIdentifier()); } @@ -96,9 +95,9 @@ namespace Microsoft.AspNetCore.Razor.Tools try { - CompilerServerLogger.Log($"Before poking pipe {Identifier}."); + ServerLogger.Log($"Before poking pipe {Identifier}."); await Stream.ReadAsync(Array.Empty(), 0, 0, cancellationToken); - CompilerServerLogger.Log($"After poking pipe {Identifier}."); + ServerLogger.Log($"After poking pipe {Identifier}."); } catch (OperationCanceledException) { @@ -107,14 +106,14 @@ namespace Microsoft.AspNetCore.Razor.Tools { // It is okay for this call to fail. Errors will be reflected in the // IsConnected property which will be read on the next iteration. - CompilerServerLogger.LogException(e, $"Error poking pipe {Identifier}."); + ServerLogger.LogException(e, $"Error poking pipe {Identifier}."); } } } protected override void Dispose(bool disposing) { - CompilerServerLogger.Log($"Pipe {Identifier}: Closing."); + ServerLogger.Log($"Pipe {Identifier}: Closing."); try { @@ -126,7 +125,7 @@ namespace Microsoft.AspNetCore.Razor.Tools // for which we can no longer communicate and that's okay because the Close method indicates we are // done with the client already. var message = string.Format($"Pipe {Identifier}: Error closing pipe."); - CompilerServerLogger.LogException(ex, message); + ServerLogger.LogException(ex, message); } } } diff --git a/src/Microsoft.AspNetCore.Razor.Tools/DefaultRequestDispatcher.cs b/src/Microsoft.AspNetCore.Razor.Tools/DefaultRequestDispatcher.cs index 7725a9b3cb..26747a06b6 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/DefaultRequestDispatcher.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/DefaultRequestDispatcher.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Runtime; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CommandLine; namespace Microsoft.AspNetCore.Razor.Tools { @@ -318,7 +317,7 @@ namespace Microsoft.AspNetCore.Razor.Tools { // Unable to establish a connection with the client. The client is responsible for // handling this case. Nothing else for us to do here. - CompilerServerLogger.LogException(ex, "Error creating client named pipe"); + ServerLogger.LogException(ex, "Error creating client named pipe"); return new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted); } @@ -329,13 +328,13 @@ namespace Microsoft.AspNetCore.Razor.Tools ServerRequest request; try { - CompilerServerLogger.Log("Begin reading request."); + ServerLogger.Log("Begin reading request."); request = await ServerRequest.ReadAsync(connection.Stream, cancellationToken).ConfigureAwait(false); - CompilerServerLogger.Log("End reading request."); + ServerLogger.Log("End reading request."); } catch (Exception e) { - CompilerServerLogger.LogException(e, "Error reading build request."); + ServerLogger.LogException(e, "Error reading build request."); return new ConnectionResult(ConnectionResult.Reason.CompilationNotStarted); } @@ -379,9 +378,9 @@ namespace Microsoft.AspNetCore.Razor.Tools try { - CompilerServerLogger.Log("Begin writing response."); + ServerLogger.Log("Begin writing response."); await response.WriteAsync(connection.Stream, cancellationToken); - CompilerServerLogger.Log("End writing response."); + ServerLogger.Log("End writing response."); reason = ConnectionResult.Reason.CompilationCompleted; } @@ -405,7 +404,7 @@ namespace Microsoft.AspNetCore.Razor.Tools } catch (Exception ex) { - CompilerServerLogger.LogException(ex, "Error handling connection"); + ServerLogger.LogException(ex, "Error handling connection"); return new ConnectionResult(ConnectionResult.Reason.ClientException); } } @@ -414,11 +413,11 @@ namespace Microsoft.AspNetCore.Razor.Tools { Func func = () => { - CompilerServerLogger.Log("Begin processing request"); + ServerLogger.Log("Begin processing request"); var response = _compilerHost.Execute(buildRequest, cancellationToken); - CompilerServerLogger.Log("End processing request"); + ServerLogger.Log("End processing request"); return response; }; diff --git a/src/Microsoft.AspNetCore.Razor.Tools/Roslyn/PlatformInformation.cs b/src/Microsoft.AspNetCore.Razor.Tools/Roslyn/PlatformInformation.cs deleted file mode 100644 index 476ee889ff..0000000000 --- a/src/Microsoft.AspNetCore.Razor.Tools/Roslyn/PlatformInformation.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; - -namespace Roslyn.Utilities -{ - /// - /// This class provides simple properties for determining whether the current platform is Windows or Unix-based. - /// We intentionally do not use System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(...) because - /// it incorrectly reports 'true' for 'Windows' in desktop builds running on Unix-based platforms via Mono. - /// - internal static class PlatformInformation - { - public static bool IsWindows => Path.DirectorySeparatorChar == '\\'; - public static bool IsUnix => Path.DirectorySeparatorChar == '/'; - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerConnection.cs b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerConnection.cs index 6c27dd2d50..3f729da331 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerConnection.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerConnection.cs @@ -8,8 +8,6 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; -using Roslyn.Utilities; -using static Microsoft.CodeAnalysis.CommandLine.CompilerServerLogger; namespace Microsoft.AspNetCore.Razor.Tools { @@ -204,26 +202,26 @@ namespace Microsoft.AspNetCore.Razor.Tools // Write the request try { - Log("Begin writing request"); + ServerLogger.Log("Begin writing request"); await request.WriteAsync(client.Stream, cancellationToken).ConfigureAwait(false); - Log("End writing request"); + ServerLogger.Log("End writing request"); } catch (Exception e) { - LogException(e, "Error writing build request."); + ServerLogger.LogException(e, "Error writing build request."); return new RejectedServerResponse(); } // Wait for the compilation and a monitor to detect if the server disconnects var serverCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - Log("Begin reading response"); + ServerLogger.Log("Begin reading response"); var responseTask = ServerResponse.ReadAsync(client.Stream, serverCts.Token); var monitorTask = client.WaitForDisconnectAsync(serverCts.Token); await Task.WhenAny(responseTask, monitorTask).ConfigureAwait(false); - Log("End reading response"); + ServerLogger.Log("End reading response"); if (responseTask.IsCompleted) { @@ -234,13 +232,13 @@ namespace Microsoft.AspNetCore.Razor.Tools } catch (Exception e) { - LogException(e, "Error reading response"); + ServerLogger.LogException(e, "Error reading response"); response = new RejectedServerResponse(); } } else { - Log("Server disconnect"); + ServerLogger.Log("Server disconnect"); response = new RejectedServerResponse(); } @@ -282,7 +280,7 @@ namespace Microsoft.AspNetCore.Razor.Tools startInfo.dwFlags = NativeMethods.STARTF_USESTDHANDLES; var dwCreationFlags = NativeMethods.NORMAL_PRIORITY_CLASS | NativeMethods.CREATE_NO_WINDOW; - Log("Attempting to create process '{0}'", expectedPath); + ServerLogger.Log("Attempting to create process '{0}'", expectedPath); var builder = new StringBuilder($@"""{expectedPath}"" {processArguments}"); @@ -300,13 +298,13 @@ namespace Microsoft.AspNetCore.Razor.Tools if (success) { - Log("Successfully created process with process id {0}", processInfo.dwProcessId); + ServerLogger.Log("Successfully created process with process id {0}", processInfo.dwProcessId); NativeMethods.CloseHandle(processInfo.hProcess); NativeMethods.CloseHandle(processInfo.hThread); } else { - Log("Failed to create process. GetLastError={0}", Marshal.GetLastWin32Error()); + ServerLogger.Log("Failed to create process. GetLastError={0}", Marshal.GetLastWin32Error()); } return success; } @@ -336,4 +334,15 @@ namespace Microsoft.AspNetCore.Razor.Tools } } } + + /// + /// This class provides simple properties for determining whether the current platform is Windows or Unix-based. + /// We intentionally do not use System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(...) because + /// it incorrectly reports 'true' for 'Windows' in desktop builds running on Unix-based platforms via Mono. + /// + internal static class PlatformInformation + { + public static bool IsWindows => Path.DirectorySeparatorChar == '\\'; + public static bool IsUnix => Path.DirectorySeparatorChar == '/'; + } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Tools/Roslyn/CompilerServerLogger.cs b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerLogger.cs similarity index 84% rename from src/Microsoft.AspNetCore.Razor.Tools/Roslyn/CompilerServerLogger.cs rename to src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerLogger.cs index c2c95f00bf..21ff3a06a9 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/Roslyn/CompilerServerLogger.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerLogger.cs @@ -1,25 +1,22 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Roslyn.Utilities; using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Text; using System.Threading; -namespace Microsoft.CodeAnalysis.CommandLine +namespace Microsoft.AspNetCore.Razor.Tools { /// /// Class for logging information about what happens in the server and client parts of the - /// Roslyn command line compiler and build tasks. Useful for debugging what is going on. + /// Razor command line compiler and build tasks. Useful for debugging what is going on. /// /// - /// To use the logging, set the environment variable RoslynCommandLineLogFile to the name + /// To use the logging, set the environment variable RAZORBUILDSERVER_LOG to the name /// of a file to log to. This file is logged to by both client and server components. /// - internal class CompilerServerLogger + internal class ServerLogger { // Environment variable, if set, to enable logging and set the file to log to. private const string EnvironmentVariable = "RAZORBUILDSERVER_LOG"; @@ -30,17 +27,19 @@ namespace Microsoft.CodeAnalysis.CommandLine /// /// Static class initializer that initializes logging. /// - static CompilerServerLogger() + static ServerLogger() { s_loggingStream = null; try { // Check if the environment - string loggingFileName = Environment.GetEnvironmentVariable(EnvironmentVariable); + var loggingFileName = Environment.GetEnvironmentVariable(EnvironmentVariable); if (loggingFileName != null) { + IsLoggingEnabled = true; + // If the environment variable contains the path of a currently existing directory, // then use a process-specific name for the log file and put it in that directory. // Otherwise, assume that the environment variable specifies the name of the log file. @@ -59,6 +58,8 @@ namespace Microsoft.CodeAnalysis.CommandLine } } + public static bool IsLoggingEnabled { get; } + /// /// Set the logging prefix that describes our role. /// Typically a 3-letter abbreviation. If logging happens before this, it's logged with "---". @@ -73,11 +74,11 @@ namespace Microsoft.CodeAnalysis.CommandLine /// public static void LogException(Exception e, string reason) { - if (s_loggingStream != null) + if (IsLoggingEnabled) { Log("Exception '{0}' occurred during '{1}'. Stack trace:\r\n{2}", e.Message, reason, e.StackTrace); - int innerExceptionLevel = 0; + var innerExceptionLevel = 0; e = e.InnerException; while (e != null) @@ -94,7 +95,7 @@ namespace Microsoft.CodeAnalysis.CommandLine /// public static void Log(string format, params object[] arguments) { - if (s_loggingStream != null) + if (IsLoggingEnabled) { Log(string.Format(format, arguments)); } @@ -106,12 +107,12 @@ namespace Microsoft.CodeAnalysis.CommandLine /// public static void Log(string message) { - if (s_loggingStream != null) + if (IsLoggingEnabled) { - string prefix = GetLoggingPrefix(); + var prefix = GetLoggingPrefix(); - string output = prefix + message + "\r\n"; - byte[] bytes = Encoding.UTF8.GetBytes(output); + var output = prefix + message + "\r\n"; + var bytes = Encoding.UTF8.GetBytes(output); // Because multiple processes might be logging to the same file, we always seek to the end, // write, and flush. diff --git a/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerProtocol.cs b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerProtocol.cs index 807690d7f4..940dda2845 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerProtocol.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerProtocol.cs @@ -4,7 +4,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CommandLine; namespace Microsoft.AspNetCore.Razor.Tools { @@ -49,7 +48,7 @@ namespace Microsoft.AspNetCore.Razor.Tools var totalBytesRead = 0; do { - CompilerServerLogger.Log("Attempting to read {0} bytes from the stream", count - totalBytesRead); + ServerLogger.Log("Attempting to read {0} bytes from the stream", count - totalBytesRead); var bytesRead = await stream.ReadAsync( buffer, totalBytesRead, @@ -59,13 +58,13 @@ namespace Microsoft.AspNetCore.Razor.Tools if (bytesRead == 0) { - CompilerServerLogger.Log("Unexpected -- read 0 bytes from the stream."); + ServerLogger.Log("Unexpected -- read 0 bytes from the stream."); throw new EndOfStreamException("Reached end of stream before end of read."); } - CompilerServerLogger.Log("Read {0} bytes", bytesRead); + ServerLogger.Log("Read {0} bytes", bytesRead); totalBytesRead += bytesRead; } while (totalBytesRead < count); - CompilerServerLogger.Log("Finished read"); + ServerLogger.Log("Finished read"); } } } diff --git a/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerRequest.cs b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerRequest.cs index 5fa861717c..e1dd9308b0 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerRequest.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerRequest.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using static Microsoft.CodeAnalysis.CommandLine.CompilerServerLogger; // After the server pipe is connected, it forks off a thread to handle the connection, and creates // a new instance of the pipe to listen for new clients. When it gets a request, it validates @@ -84,9 +83,9 @@ namespace Microsoft.AspNetCore.Razor.Tools IList args, string keepAlive = null) { - Log("Creating ServerRequest"); - Log($"Working directory: {workingDirectory}"); - Log($"Temp directory: {tempDirectory}"); + ServerLogger.Log("Creating ServerRequest"); + ServerLogger.Log($"Working directory: {workingDirectory}"); + ServerLogger.Log($"Temp directory: {tempDirectory}"); var requestLength = args.Count + 1; var requestArgs = new List(requestLength) @@ -103,7 +102,7 @@ namespace Microsoft.AspNetCore.Razor.Tools for (var i = 0; i < args.Count; ++i) { var arg = args[i]; - Log($"argument[{i}] = {arg}"); + ServerLogger.Log($"argument[{i}] = {arg}"); requestArgs.Add(new RequestArgument(RequestArgument.ArgumentId.CommandLineArgument, i, arg)); } @@ -130,14 +129,14 @@ namespace Microsoft.AspNetCore.Razor.Tools { // Read the length of the request var lengthBuffer = new byte[4]; - Log("Reading length of request"); + ServerLogger.Log("Reading length of request"); await ServerProtocol.ReadAllAsync(inStream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false); var length = BitConverter.ToInt32(lengthBuffer, 0); // Back out if the request is > 1MB if (length > 0x100000) { - Log("Request is over 1MB in length, cancelling read."); + ServerLogger.Log("Request is over 1MB in length, cancelling read."); return null; } @@ -149,7 +148,7 @@ namespace Microsoft.AspNetCore.Razor.Tools cancellationToken.ThrowIfCancellationRequested(); - Log("Parsing request"); + ServerLogger.Log("Parsing request"); // Parse the request into the Request data structure. using (var reader = new BinaryReader(new MemoryStream(requestBuffer), Encoding.Unicode)) { @@ -177,7 +176,7 @@ namespace Microsoft.AspNetCore.Razor.Tools using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode)) { // Format the request. - Log("Formatting request"); + ServerLogger.Log("Formatting request"); writer.Write(ProtocolVersion); writer.Write(Arguments.Count); foreach (var arg in Arguments) @@ -195,17 +194,17 @@ namespace Microsoft.AspNetCore.Razor.Tools // Back out if the request is > 1 MB if (memoryStream.Length > 0x100000) { - Log("Request is over 1MB in length, cancelling write"); + ServerLogger.Log("Request is over 1MB in length, cancelling write"); throw new ArgumentOutOfRangeException(); } // Send the request to the server - Log("Writing length of request."); + ServerLogger.Log("Writing length of request."); await outStream .WriteAsync(BitConverter.GetBytes(length), 0, 4, cancellationToken) .ConfigureAwait(false); - Log("Writing request of size {0}", length); + ServerLogger.Log("Writing request of size {0}", length); // Write the request memoryStream.Position = 0; await memoryStream diff --git a/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerResponse.cs b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerResponse.cs index ea2c32ce44..66eb1e63f3 100644 --- a/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerResponse.cs +++ b/src/Microsoft.AspNetCore.Razor.Tools/ServerProtocol/ServerResponse.cs @@ -6,7 +6,6 @@ using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; -using static Microsoft.CodeAnalysis.CommandLine.CompilerServerLogger; // After the server pipe is connected, it forks off a thread to handle the connection, and creates // a new instance of the pipe to listen for new clients. When it gets a request, it validates @@ -55,7 +54,7 @@ namespace Microsoft.AspNetCore.Razor.Tools using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode)) { // Format the response - Log("Formatting Response"); + ServerLogger.Log("Formatting Response"); writer.Write((int)Type); AddResponseBody(writer); @@ -68,7 +67,7 @@ namespace Microsoft.AspNetCore.Razor.Tools // Write the length of the response var length = checked((int)memoryStream.Length); - Log("Writing response length"); + ServerLogger.Log("Writing response length"); // There is no way to know the number of bytes written to // the pipe stream. We just have to assume all of them are written. await outStream @@ -76,7 +75,7 @@ namespace Microsoft.AspNetCore.Razor.Tools .ConfigureAwait(false); // Write the response - Log("Writing response of size {0}", length); + ServerLogger.Log("Writing response of size {0}", length); memoryStream.Position = 0; await memoryStream .CopyToAsync(outStream, bufferSize: length, cancellationToken: cancellationToken) @@ -94,14 +93,14 @@ namespace Microsoft.AspNetCore.Razor.Tools /// public static async Task ReadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { - Log("Reading response length"); + ServerLogger.Log("Reading response length"); // Read the response length var lengthBuffer = new byte[4]; await ServerProtocol.ReadAllAsync(stream, lengthBuffer, 4, cancellationToken).ConfigureAwait(false); var length = BitConverter.ToUInt32(lengthBuffer, 0); // Read the response - Log("Reading response of length {0}", length); + ServerLogger.Log("Reading response of length {0}", length); var responseBuffer = new byte[length]; await ServerProtocol.ReadAllAsync( stream, diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildServerIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildServerIntegrationTest.cs index 1adb5152ab..d7fc51b4dd 100644 --- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildServerIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildServerIntegrationTest.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { var result = await DotnetMSBuild( "Build", - $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName}", + $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName} /p:_RazorForceBuildServer=true", msBuildProcessKind: msBuildProcessKind); Assert.BuildPassed(result); @@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { var result = await DotnetMSBuild( "Build", - $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName}"); + $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName} /p:_RazorForceBuildServer=true"); Assert.BuildPassed(result); Assert.FileExists(result, OutputPath, "SimpleMvc.dll"); @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { var result = await DotnetMSBuild( "Build", - $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName}"); + $"/p:RazorCompileOnBuild=true /p:UseRazorBuildServer=true /p:_RazorBuildServerPipeName={_pipeName} /p:_RazorForceBuildServer=true"); Assert.BuildPassed(result); Assert.FileExists(result, OutputPath, "Whitespace in name.dll"); diff --git a/test/Microsoft.AspNetCore.Razor.Tools.Test/Infrastructure/ServerUtilities.cs b/test/Microsoft.AspNetCore.Razor.Tools.Test/Infrastructure/ServerUtilities.cs index 77c30cd60a..473b608142 100644 --- a/test/Microsoft.AspNetCore.Razor.Tools.Test/Infrastructure/ServerUtilities.cs +++ b/test/Microsoft.AspNetCore.Razor.Tools.Test/Infrastructure/ServerUtilities.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CommandLine; namespace Microsoft.AspNetCore.Razor.Tools {