-
Notifications
You must be signed in to change notification settings - Fork 355
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add console file logging Added "logopen" and "logclose" commands to control console file logging. Added the IConsoleFileLoggingService and implementation to control the console file logging. Issue: #3095 Add internal diagnostic logging to a file also. Added the IDiagnosticLoggingService and implementation to control internal diagnostic logging. Move Tracer.cs to Microsoft.Diagnostics.DebugService.Implementation. Add tests for logging commands
- Loading branch information
Showing
15 changed files
with
597 additions
and
96 deletions.
There are no files selected for viewing
166 changes: 166 additions & 0 deletions
166
src/Microsoft.Diagnostics.DebugServices.Implementation/DiagnosticLoggingService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using Microsoft.Diagnostics.DebugServices; | ||
using System; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Security; | ||
|
||
namespace Microsoft.Diagnostics.ExtensionCommands | ||
{ | ||
public class DiagnosticLoggingService : IDiagnosticLoggingService | ||
{ | ||
private const string ListenerName = "SOS.LoggingListener"; | ||
private IConsoleService _consoleService; | ||
private IConsoleFileLoggingService _fileLoggingService; | ||
private StreamWriter _writer; | ||
|
||
public static DiagnosticLoggingService Instance { get; } = new DiagnosticLoggingService(); | ||
|
||
private DiagnosticLoggingService() | ||
{ | ||
} | ||
|
||
#region IDiagnosticLoggingService | ||
|
||
/// <summary> | ||
/// Returns true if logging to console or file | ||
/// </summary> | ||
public bool IsEnabled => Trace.Listeners[ListenerName] is not null; | ||
|
||
/// <summary> | ||
/// The file path if logging to file. | ||
/// </summary> | ||
public string FilePath => (_writer?.BaseStream as FileStream)?.Name; | ||
|
||
/// <summary> | ||
/// Enable diagnostics logging. | ||
/// </summary> | ||
/// <param name="filePath">log file path or null if log to console</param> | ||
/// <remarks>see File.Open for possible exceptions thrown</remarks> | ||
public void Enable(string filePath) | ||
{ | ||
if (filePath is not null) | ||
{ | ||
FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); | ||
CloseLogging(); | ||
_writer = new StreamWriter(stream) { | ||
AutoFlush = true | ||
}; | ||
_fileLoggingService?.AddStream(stream); | ||
} | ||
if (Trace.Listeners[ListenerName] is null) | ||
{ | ||
Trace.Listeners.Add(new LoggingListener(this)); | ||
Trace.AutoFlush = true; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Disable diagnostics logging (close if logging to file). | ||
/// </summary> | ||
public void Disable() | ||
{ | ||
CloseLogging(); | ||
Trace.Listeners.Remove(ListenerName); | ||
} | ||
|
||
#endregion | ||
|
||
/// <summary> | ||
/// Initializes the diagnostic logging service. Reads the DOTNET_ENABLED_SOS_LOGGING | ||
/// environment variable to log to console or file. | ||
/// </summary> | ||
/// <param name="logfile"></param> | ||
public static void Initialize(string logfile = null) | ||
{ | ||
try | ||
{ | ||
if (string.IsNullOrWhiteSpace(logfile)) | ||
{ | ||
logfile = Environment.GetEnvironmentVariable("DOTNET_ENABLED_SOS_LOGGING"); | ||
} | ||
if (!string.IsNullOrWhiteSpace(logfile)) | ||
{ | ||
Instance.Enable(logfile == "1" ? null : logfile); | ||
} | ||
} | ||
catch (Exception ex) when ( ex is IOException || ex is NotSupportedException || ex is SecurityException || ex is UnauthorizedAccessException) | ||
{ | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the console service and the console file logging control service. | ||
/// </summary> | ||
/// <param name="consoleService">This is used for to log to the console</param> | ||
/// <param name="fileLoggingService">This is used to hook the command console output to write the diagnostic log file.</param> | ||
public void SetConsole(IConsoleService consoleService, IConsoleFileLoggingService fileLoggingService = null) | ||
{ | ||
_consoleService = consoleService; | ||
_fileLoggingService = fileLoggingService; | ||
} | ||
|
||
private void CloseLogging() | ||
{ | ||
if (_writer is not null) | ||
{ | ||
_fileLoggingService?.RemoveStream(_writer.BaseStream); | ||
_writer.Flush(); | ||
_writer.Close(); | ||
_writer = null; | ||
} | ||
} | ||
|
||
class LoggingListener : TraceListener | ||
{ | ||
private readonly DiagnosticLoggingService _diagnosticLoggingService; | ||
|
||
internal LoggingListener(DiagnosticLoggingService diagnosticLoggingService) | ||
: base(ListenerName) | ||
{ | ||
_diagnosticLoggingService = diagnosticLoggingService; | ||
} | ||
|
||
public override void Close() | ||
{ | ||
_diagnosticLoggingService.CloseLogging(); | ||
base.Close(); | ||
} | ||
|
||
public override void Write(string message) | ||
{ | ||
if (_diagnosticLoggingService._writer is not null) | ||
{ | ||
try | ||
{ | ||
_diagnosticLoggingService._writer.Write(message); | ||
return; | ||
} | ||
catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException) | ||
{ | ||
} | ||
} | ||
_diagnosticLoggingService._consoleService?.Write(message); | ||
} | ||
|
||
public override void WriteLine(string message) | ||
{ | ||
if (_diagnosticLoggingService._writer is not null) | ||
{ | ||
try | ||
{ | ||
_diagnosticLoggingService._writer.WriteLine(message); | ||
return; | ||
} | ||
catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException) | ||
{ | ||
} | ||
} | ||
_diagnosticLoggingService._consoleService?.WriteLine(message); | ||
} | ||
} | ||
} | ||
} |
171 changes: 171 additions & 0 deletions
171
src/Microsoft.Diagnostics.DebugServices.Implementation/FileLoggingConsoleService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Threading; | ||
|
||
namespace Microsoft.Diagnostics.DebugServices.Implementation | ||
{ | ||
/// <summary> | ||
/// Log to file console service wrapper | ||
/// </summary> | ||
public class FileLoggingConsoleService : IConsoleService, IConsoleFileLoggingService, IDisposable | ||
{ | ||
private readonly IConsoleService _consoleService; | ||
private readonly List<StreamWriter> _writers; | ||
private FileStream _consoleStream; | ||
|
||
public FileLoggingConsoleService(IConsoleService consoleService) | ||
{ | ||
_consoleService = consoleService; | ||
_writers = new List<StreamWriter>(); | ||
} | ||
|
||
public void Dispose() => Disable(); | ||
|
||
#region IConsoleFileLoggingService | ||
|
||
/// <summary> | ||
/// The log file path if enabled, otherwise null. | ||
/// </summary> | ||
public string FilePath => _consoleStream?.Name; | ||
|
||
/// <summary> | ||
/// Enable console file logging. | ||
/// </summary> | ||
/// <param name="filePath">log file path</param> | ||
/// <remarks>see File.Open for more exceptions</remarks> | ||
public void Enable(string filePath) | ||
{ | ||
FileStream consoleStream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); | ||
Disable(); | ||
AddStream(consoleStream); | ||
_consoleStream = consoleStream; | ||
} | ||
|
||
/// <summary> | ||
/// Disable/close console file logging | ||
/// </summary> | ||
public void Disable() | ||
{ | ||
if (_consoleStream is not null) | ||
{ | ||
RemoveStream(_consoleStream); | ||
_consoleStream.Close(); | ||
_consoleStream = null; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Add to the list of file streams to write the console output. | ||
/// </summary> | ||
/// <param name="stream">Stream to add. Lifetime managed by caller.</param> | ||
public void AddStream(Stream stream) | ||
{ | ||
Debug.Assert(stream is not null); | ||
_writers.Add(new StreamWriter(stream) { | ||
AutoFlush = true | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Remove the specified file stream from the writers. | ||
/// </summary> | ||
/// <param name="stream">Stream passed to add. Stream not closed or disposed.</param> | ||
public void RemoveStream(Stream stream) | ||
{ | ||
if (stream is not null) | ||
{ | ||
foreach (StreamWriter writer in _writers) | ||
{ | ||
if (writer.BaseStream == stream) | ||
{ | ||
_writers.Remove(writer); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
#endregion | ||
|
||
#region IConsoleService | ||
|
||
public void Write(string text) | ||
{ | ||
_consoleService.Write(text); | ||
foreach (StreamWriter writer in _writers) | ||
{ | ||
try | ||
{ | ||
writer.Write(text); | ||
} | ||
catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException) | ||
{ | ||
} | ||
} | ||
} | ||
|
||
public void WriteWarning(string text) | ||
{ | ||
_consoleService.WriteWarning(text); | ||
foreach (StreamWriter writer in _writers) | ||
{ | ||
try | ||
{ | ||
writer.Write(text); | ||
} | ||
catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException) | ||
{ | ||
} | ||
} | ||
} | ||
|
||
public void WriteError(string text) | ||
{ | ||
_consoleService.WriteError(text); | ||
foreach (StreamWriter writer in _writers) | ||
{ | ||
try | ||
{ | ||
writer.Write(text); | ||
} | ||
catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException) | ||
{ | ||
} | ||
} | ||
} | ||
|
||
public bool SupportsDml => _consoleService.SupportsDml; | ||
|
||
public void WriteDml(string text) | ||
{ | ||
_consoleService.WriteDml(text); | ||
foreach (StreamWriter writer in _writers) | ||
{ | ||
try | ||
{ | ||
// TODO: unwrap the DML? | ||
writer.Write(text); | ||
} | ||
catch (Exception ex) when (ex is IOException || ex is ObjectDisposedException || ex is NotSupportedException) | ||
{ | ||
} | ||
} | ||
} | ||
|
||
public CancellationToken CancellationToken | ||
{ | ||
get { return _consoleService.CancellationToken; } | ||
set { _consoleService.CancellationToken = value; } | ||
} | ||
|
||
public int WindowWidth => _consoleService.WindowWidth; | ||
|
||
#endregion | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.