Skip to content

Commit

Permalink
Implement support in the LSIF generator for logging to stderr
Browse files Browse the repository at this point in the history
LSIF tools can implement an optional logging format which is written
to stderr; this implements that format.
  • Loading branch information
jasonmalinowski committed Sep 28, 2023
1 parent 475fe65 commit 4bc3379
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 11 deletions.
58 changes: 58 additions & 0 deletions src/Features/Lsif/Generator/Logging/LsifFormatLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.IO;
using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Writing;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Logging
{
internal sealed class LsifFormatLogger : ILogger
{
private readonly TextWriter _writer;
private readonly object _writerGate = new object();

public LsifFormatLogger(TextWriter writer)
{
_writer = writer;
}

public IDisposable BeginScope<TState>(TState state)
{
throw new NotImplementedException();
}

public bool IsEnabled(LogLevel logLevel)
{
return logLevel >= LogLevel.Information;
}

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
var message = formatter(state, exception);

var severity = logLevel switch { LogLevel.Information => "Info", LogLevel.Warning => "Warning", LogLevel.Error => "Error", LogLevel.Critical => "Critical", _ => throw ExceptionUtilities.UnexpectedValue(logLevel) };

// If we have an exception, we'll use the exception message, otherwise the logged message. It doesn't appear we can log both at once.
var command = new CommandWithParameters("log",
new LogCommandParameters(severity, exception?.Message ?? message, exception?.GetType().ToString(), exception?.StackTrace));
var serializedCommand = JsonConvert.SerializeObject(command, LineModeLsifJsonWriter.SerializerSettings);

lock (_writerGate)
{
_writer.Write(serializedCommand);
}
}

private sealed record CommandWithParameters(string Command, object Parameters);
private sealed record LogCommandParameters(
string Severity,
string Message,
string? Exception,
string? CallStack);
}
}
2 changes: 2 additions & 0 deletions src/Features/Lsif/Generator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ private static async Task GenerateAsync(
ILogger logger;
if (logFile is not null)
logger = new PlainTextLogger(logFile);
else if (logFileName == "stderr")
logger = new LsifFormatLogger(Console.Error);
else
logger = NullLogger.Instance;

Expand Down
22 changes: 11 additions & 11 deletions src/Features/Lsif/Generator/Writing/LineModeLsifJsonWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,26 @@ namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Writing
/// </summary>
internal sealed partial class LineModeLsifJsonWriter : ILsifJsonWriter
{
public static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.None,
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
TypeNameHandling = TypeNameHandling.None,
Converters = new[] { new LsifConverter() }
};

private readonly object _writeGate = new object();
private readonly TextWriter _outputWriter;
private readonly JsonSerializerSettings _settings;

public LineModeLsifJsonWriter(TextWriter outputWriter)
{
_settings = new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.None,
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
TypeNameHandling = TypeNameHandling.None,
Converters = new[] { new LsifConverter() }
};
_outputWriter = outputWriter;
}

public void Write(Element element)
{
var line = JsonConvert.SerializeObject(element, _settings);
var line = JsonConvert.SerializeObject(element, SerializerSettings);

lock (_writeGate)
{
Expand All @@ -46,7 +46,7 @@ public void WriteAll(List<Element> elements)
{
var lines = new List<string>();
foreach (var element in elements)
lines.Add(JsonConvert.SerializeObject(element, _settings));
lines.Add(JsonConvert.SerializeObject(element, SerializerSettings));

lock (_writeGate)
{
Expand Down
35 changes: 35 additions & 0 deletions src/Features/Lsif/GeneratorTest/LsifFormatLoggerTests.vb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
' 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.

Imports System.IO
Imports Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Logging
Imports Microsoft.Extensions.Logging

Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests
Public Class LsifFormatLoggerTests
<Theory>
<InlineData(LogLevel.Information, "Info")>
<InlineData(LogLevel.Warning, "Warning")>
<InlineData(LogLevel.Error, "Error")>
<InlineData(LogLevel.Critical, "Critical")>
Public Sub TestSimpleMessage(logLevel As LogLevel, expectedSeverity As String)
Dim writer = New StringWriter
Dim logger = New LsifFormatLogger(writer)

logger.Log(logLevel, "Test message")

Assert.Equal("{""command"":""log"",""parameters"":{""severity"":""" + expectedSeverity + """,""message"":""Test message""}}", writer.ToString())
End Sub

<Fact>
Public Sub TestException()
Dim writer = New StringWriter
Dim logger = New LsifFormatLogger(writer)

logger.LogError(New Exception("Exception!"), "An exception was thrown")

Assert.Equal("{""command"":""log"",""parameters"":{""severity"":""Error"",""message"":""Exception!"",""exception"":""System.Exception""}}", writer.ToString())
End Sub
End Class
End Namespace

0 comments on commit 4bc3379

Please sign in to comment.