Skip to content
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

[dotnet] Possibility to output internal log messages to file #13249

Merged
merged 85 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
0688c8b
INitial implementation
nvborisenko Oct 20, 2023
091ee36
With context
nvborisenko Oct 23, 2023
eea4ab6
Update HttpCommandExecutor.cs
nvborisenko Oct 23, 2023
f8c27f8
Nullable handlers
nvborisenko Oct 24, 2023
3cee19d
Don't capture logger
nvborisenko Oct 24, 2023
c722c4e
Log message issuer
nvborisenko Oct 24, 2023
16c3cae
Simplify things
nvborisenko Oct 24, 2023
d7ceb67
Merge remote-tracking branch 'upstream/trunk' into dotnet-logging
nvborisenko Nov 10, 2023
2a85e27
Continue
nvborisenko Nov 10, 2023
4fca9a3
Merge remote-tracking branch 'upstream/trunk' into dotnet-logging
nvborisenko Nov 13, 2023
4c7ba77
Update nunit adapter to work with dotnet 7 and be more friendly with …
nvborisenko Nov 13, 2023
0dc4b35
Merge remote-tracking branch 'upstream/trunk' into dotnet-logging
nvborisenko Nov 13, 2023
1042d4e
Rename to LogEventLevel
nvborisenko Nov 22, 2023
eae7f4c
Typo
nvborisenko Nov 22, 2023
0b7c610
Introduce LogContextManager
nvborisenko Nov 22, 2023
1390b1e
Typo again
nvborisenko Nov 22, 2023
e87773e
Rename to Timestamp
nvborisenko Nov 23, 2023
b874730
Make ILogger as static field
nvborisenko Nov 23, 2023
fcf811c
Support hierarchical contexts
nvborisenko Nov 28, 2023
3a9dbaf
Rename to EmitMessage
nvborisenko Nov 28, 2023
ca73016
Do not emit message to parent context
nvborisenko Nov 28, 2023
bf004d6
Deep copy of loggers and handlers per context
nvborisenko Nov 28, 2023
88cb872
Make fields private
nvborisenko Nov 28, 2023
9795e8f
Static works with current log context
nvborisenko Nov 28, 2023
6f3e484
Create context with minimum level
nvborisenko Nov 28, 2023
478ae5a
Set minimum level for context
nvborisenko Nov 28, 2023
5f54cfa
Rename to WithHandler
nvborisenko Nov 28, 2023
571479a
Set minimum level per issuer
nvborisenko Nov 28, 2023
edb14d3
Simplify getting internal logger
nvborisenko Nov 28, 2023
4573398
Use DateTimeOffset
nvborisenko Nov 28, 2023
044fc86
Docs for log event level
nvborisenko Nov 28, 2023
13b00f6
Docs for ILogger
nvborisenko Nov 28, 2023
34b9b40
Docs for Logger
nvborisenko Nov 28, 2023
cec3811
Docs for others
nvborisenko Nov 28, 2023
3cf6e48
Make ILogger interface as internal
nvborisenko Nov 28, 2023
c7dd8ed
Revert "Make ILogger interface as internal"
nvborisenko Nov 28, 2023
7a02443
First test
nvborisenko Nov 28, 2023
339e4f9
Merge remote-tracking branch 'upstream/trunk' into dotnet-logging-test
nvborisenko Nov 28, 2023
9cb6a14
Update LogTest.cs
nvborisenko Nov 28, 2023
c4af631
Fix build error
nvborisenko Nov 28, 2023
684d565
Info minimum log level by default
nvborisenko Nov 28, 2023
cba7a68
Remove unnecessary log call in ChromeDriver
nvborisenko Nov 28, 2023
84da458
Adjust log levels in console output
nvborisenko Nov 29, 2023
48bd407
Make it length fixed
nvborisenko Nov 29, 2023
cd46467
Make webdriver assembly internals visible to tests
nvborisenko Nov 29, 2023
1d1cfc5
Make ILogger hidden from user
nvborisenko Nov 29, 2023
d7d22b2
More tests for log context
nvborisenko Nov 29, 2023
f96fadb
Init
nvborisenko Nov 29, 2023
a6271e7
Rename back to AddHandler
nvborisenko Nov 29, 2023
fbc36ab
Make format script happy?
nvborisenko Nov 29, 2023
0514324
Make format script happy?
nvborisenko Nov 29, 2023
3314553
Rename back to SetLevel
nvborisenko Nov 29, 2023
479c357
Console handler by default
nvborisenko Nov 29, 2023
20a66ae
Output logs to stderr
nvborisenko Nov 29, 2023
5fb6acf
New api to mange log handlers
nvborisenko Nov 29, 2023
4681724
Merge branch 'trunk' into dotnet-logging
nvborisenko Nov 29, 2023
e3255a6
Use logging in DriverFactory
nvborisenko Nov 30, 2023
0167dfa
Revert "Use logging in DriverFactory"
nvborisenko Nov 30, 2023
7ac3376
Verbose driver creation in tests
nvborisenko Dec 1, 2023
225474c
Search driver type in loaded assemblies
nvborisenko Dec 1, 2023
9b61d8a
Decalare internals visible to in csproj to not conflict with bazel
nvborisenko Dec 1, 2023
3e35ca7
Clean specific assembly name for driver type
nvborisenko Dec 1, 2023
b7bd127
Merge branch 'trunk' into dotnet-logging
nvborisenko Dec 1, 2023
9663bc3
Merge branch 'trunk' into dotnet-logging
nvborisenko Dec 1, 2023
5ff197f
Merge remote-tracking branch 'upstream/trunk' into dotnet-logging
nvborisenko Dec 1, 2023
0885002
Old school using to make bazel happy
nvborisenko Dec 1, 2023
1138648
Fix targeting packs for test targets
nvborisenko Dec 1, 2023
bcf9e0c
Merge branch 'dotnet-logging' into dotnet-logging-file
nvborisenko Dec 1, 2023
049e646
It works
nvborisenko Dec 1, 2023
8c952e9
Small clean up
nvborisenko Dec 1, 2023
9f47cb5
Lock in ctor
nvborisenko Dec 1, 2023
1a6803e
Dispose at process exit
nvborisenko Dec 2, 2023
bea874f
Merge remote-tracking branch 'upstream/trunk' into dotnet-logging-file
nvborisenko Dec 5, 2023
418ae88
Remove redundant Clone for log handlers
nvborisenko Dec 5, 2023
b7e09d6
Dispose log handlers when context finishes
nvborisenko Dec 5, 2023
5814e27
Lock writing to the disk globally
nvborisenko Dec 5, 2023
001641b
Fix new list of log handlers for context
nvborisenko Dec 5, 2023
aa9626f
Don't lock in ctor
nvborisenko Dec 6, 2023
1a4b87b
Add docs
nvborisenko Dec 6, 2023
2b19228
Change format of datetime in file log
nvborisenko Dec 6, 2023
dbebb14
Thread safe disposing
nvborisenko Dec 6, 2023
d368244
Add finilizer
nvborisenko Dec 6, 2023
060cf77
Docs for finilizer
nvborisenko Dec 6, 2023
db63f60
Add tests
nvborisenko Dec 6, 2023
8076316
Recreating missing dirs
nvborisenko Dec 6, 2023
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
9 changes: 0 additions & 9 deletions dotnet/src/webdriver/Internal/Logging/ConsoleLogHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,5 @@ public void Handle(LogEvent logEvent)
{
Console.Error.WriteLine($"{logEvent.Timestamp:HH:mm:ss.fff} {_levels[(int)logEvent.Level]} {logEvent.IssuedBy.Name}: {logEvent.Message}");
}

/// <summary>
/// Creates a new instance of the <see cref="ConsoleLogHandler"/> class.
/// </summary>
/// <returns>A new instance of the <see cref="ConsoleLogHandler"/> class.</returns>
public ILogHandler Clone()
{
return this;
}
}
}
94 changes: 94 additions & 0 deletions dotnet/src/webdriver/Internal/Logging/FileLogHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.IO;

namespace OpenQA.Selenium.Internal.Logging
{
/// <summary>
/// Represents a log handler that writes log events to a file.
/// </summary>
public class FileLogHandler : ILogHandler, IDisposable
{
// performance trick to avoid expensive Enum.ToString() with fixed length
private static readonly string[] _levels = { "TRACE", "DEBUG", " INFO", " WARN", "ERROR" };

private FileStream _fileStream;
private StreamWriter _streamWriter;

private readonly object _lockObj = new object();
private bool _isDisposed;

/// <summary>
/// Initializes a new instance of the <see cref="FileLogHandler"/> class with the specified file path.
/// </summary>
/// <param name="path">The path of the log file.</param>
public FileLogHandler(string path)
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved
{
if (string.IsNullOrEmpty(path)) throw new ArgumentException("File log path cannot be null or empty.", nameof(path));

var directory = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}

_fileStream = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read);
_fileStream.Seek(0, SeekOrigin.End);
_streamWriter = new StreamWriter(_fileStream, System.Text.Encoding.UTF8)
{
AutoFlush = true
};
}

/// <summary>
/// Handles a log event by writing it to the log file.
/// </summary>
/// <param name="logEvent">The log event to handle.</param>
public void Handle(LogEvent logEvent)
{
lock (_lockObj)
{
_streamWriter.WriteLine($"{logEvent.Timestamp:r} {_levels[(int)logEvent.Level]} {logEvent.IssuedBy.Name}: {logEvent.Message}");
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// <summary>
/// Disposes the file log handler and releases any resources used.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Finalizes the file log handler instance.
/// </summary>
~FileLogHandler()
{
Dispose(false);
}

/// <summary>
/// Disposes the file log handler and releases any resources used.
/// </summary>
/// <param name="disposing">A flag indicating whether to dispose managed resources.</param>
protected virtual void Dispose(bool disposing)
{
lock (_lockObj)
{
if (!_isDisposed)
{
if (disposing)
{
_streamWriter?.Dispose();
_streamWriter = null;
_fileStream?.Dispose();
_fileStream = null;
}

_isDisposed = true;
}
}
}
}
}
6 changes: 0 additions & 6 deletions dotnet/src/webdriver/Internal/Logging/ILogHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,5 @@ public interface ILogHandler
/// </summary>
/// <param name="logEvent">The log event to handle.</param>
void Handle(LogEvent logEvent);

/// <summary>
/// Creates a clone of the log handler.
/// </summary>
/// <returns>A clone of the log handler.</returns>
ILogHandler Clone();
}
}
26 changes: 18 additions & 8 deletions dotnet/src/webdriver/Internal/Logging/LogContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,16 @@ public ILogContext CreateContext(LogEventLevel minimumLevel)
loggers = new ConcurrentDictionary<Type, ILogger>(_loggers.Select(l => new KeyValuePair<Type, ILogger>(l.Key, new Logger(l.Value.Issuer, minimumLevel))));
}

IList<ILogHandler> handlers = null;
var context = new LogContext(minimumLevel, this, loggers, null);
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved

if (Handlers != null)
{
handlers = new List<ILogHandler>(Handlers.Select(h => h.Clone()));
}
else
{
handlers = new List<ILogHandler>();
foreach (var handler in Handlers)
{
context.Handlers.Add(handler);
}
}

var context = new LogContext(minimumLevel, this, loggers, Handlers);

Log.CurrentContext = context;

return context;
Expand Down Expand Up @@ -137,6 +134,19 @@ public ILogContext SetLevel(Type issuer, LogEventLevel level)

public void Dispose()
{
// Dispose log handlers associated with this context
// if they are hot handled by parent context
if (Handlers != null && _parentLogContext != null && _parentLogContext.Handlers != null)
{
foreach (var logHandler in Handlers)
{
if (!_parentLogContext.Handlers.Contains(logHandler))
{
(logHandler as IDisposable)?.Dispose();
}
}
}
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved

Log.CurrentContext = _parentLogContext;
}
}
Expand Down
43 changes: 43 additions & 0 deletions dotnet/test/common/Internal/Logging/FileLogHandlerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using NUnit.Framework;
using System;
using System.IO;

namespace OpenQA.Selenium.Internal.Logging
{
public class FileLogHandlerTest
{
[Test]
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved
[TestCase(null)]
[TestCase("")]
public void ShouldNotAcceptIncorrectPath(string path)
{
var act = () => new FileLogHandler(path);

Assert.That(act, Throws.ArgumentException);
}

[Test]
public void ShouldHandleLogEvent()
{
var tempFile = Path.GetTempFileName();

try
{
using (var fileLogHandler = new FileLogHandler(tempFile))
{
fileLogHandler.Handle(new LogEvent(typeof(FileLogHandlerTest), DateTimeOffset.Now, LogEventLevel.Info, "test message"));
}

Assert.That(File.ReadAllText(tempFile), Does.Contain("test message"));
}
catch (Exception)
{
throw;
}
nvborisenko marked this conversation as resolved.
Show resolved Hide resolved
finally
{
File.Delete(tempFile);
}
}
}
}