Skip to content

Commit 8cad812

Browse files
committed
Also log errors from source process
1 parent 0eb1b46 commit 8cad812

7 files changed

Lines changed: 162 additions & 118 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using LoggerVisualizerSource;
2+
using System.Security.Principal;
3+
using System.Windows;
4+
5+
namespace LoggerVisualizer
6+
{
7+
internal static class ExceptionExtensions
8+
{
9+
internal static void Track(this Exception ex)
10+
{
11+
Telemetry.TrackException(ex, WindowsIdentity.GetCurrent().Name, (int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight);
12+
}
13+
}
14+
}

src/LoggerVisualizer/ExtensionEntrypoint.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.Extensions.DependencyInjection;
1+
using LoggerVisualizerSource;
2+
using Microsoft.Extensions.DependencyInjection;
23
using Microsoft.VisualStudio.Extensibility;
34

45
namespace LoggerVisualizer
@@ -31,7 +32,7 @@ protected override void InitializeServices(IServiceCollection serviceCollection)
3132
}
3233
catch (Exception ex)
3334
{
34-
Telemetry.TrackException(ex);
35+
ex.Track();
3536
throw;
3637
}
3738

src/LoggerVisualizer/LoggerDebuggerVisualizerProvider.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers;
1+
using LoggerVisualizer.Models;
2+
using LoggerVisualizerSource;
3+
using Microsoft.Extensions.Logging;
24
using Microsoft.VisualStudio.Extensibility;
5+
using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers;
36
using Microsoft.VisualStudio.RpcContracts.RemoteUI;
4-
using Microsoft.Extensions.Logging;
5-
using LoggerVisualizerSource;
67
using System.Collections.ObjectModel;
7-
using LoggerVisualizer.Models;
88
using System.Windows;
99

1010
namespace LoggerVisualizer
1111
{
1212
[VisualStudioContribution]
1313
internal class LoggerDebuggerVisualizerProvider : DebuggerVisualizerProvider
1414
{
15-
[System.Diagnostics.CodeAnalysis.SuppressMessage("ConstantExpressionEvaluator", "CEE0027:String not localized", Justification = "")]
1615
public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new(
1716
new VisualizerTargetType("Logger Visualizer", "Microsoft.Extensions.Logging.Logger, Microsoft.Extensions.Logging, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"),
1817
new VisualizerTargetType("Logger Visualizer", typeof(Logger<>)),
@@ -31,14 +30,14 @@ public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerT
3130
}
3231
catch (Exception ex)
3332
{
34-
Telemetry.TrackException(ex);
33+
ex.Track();
3534
throw;
3635
}
3736

3837
return await Task.FromResult<IRemoteUserControl>(new LoggerVisualizerUserControl(ToViewModel(model)));
3938
}
4039

41-
private LoggerRootViewModel? ToViewModel(LoggerModel? logger)
40+
private static LoggerRootViewModel? ToViewModel(LoggerModel? logger)
4241
{
4342
if (logger == null) return null;
4443
var viewModel = new LoggerRootViewModel

src/LoggerVisualizer/LoggerVisualizer.csproj

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,16 @@
99
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
1010
</PropertyGroup>
1111

12+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
13+
<DebugType>embedded</DebugType>
14+
</PropertyGroup>
15+
16+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
17+
<DebugType>embedded</DebugType>
18+
</PropertyGroup>
19+
1220
<ItemGroup>
13-
<PackageReference Include="Elmah.Io.Client" Version="5.2.118" />
21+
<PackageReference Include="Elmah.Io.Client" Version="5.3.131" />
1422
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
1523
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.11.40261" />
1624
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.11.40261" />
@@ -27,11 +35,14 @@
2735
<Content Include="..\LoggerVisualizerSource\bin\$(Configuration)\netstandard2.0\LoggerVisualizerSource.dll" Link="netstandard2.0\LoggerVisualizerSource.dll">
2836
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2937
</Content>
30-
<Content Include="bin\$(Configuration)\net8.0-windows\Microsoft.Extensions.Logging.dll" Link="netstandard2.0\Microsoft.Extensions.Logging.dll">
31-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
32-
</Content>
38+
<Content Include="bin\$(Configuration)\net8.0-windows\Microsoft.Extensions.Logging.dll" Link="netstandard2.0\Microsoft.Extensions.Logging.dll">
39+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
40+
</Content>
41+
<Content Include="bin\$(Configuration)\net8.0-windows\Elmah.Io.Client.dll" Link="netstandard2.0\Elmah.Io.Client.dll">
42+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
43+
</Content>
3344

34-
<None Remove="icon.png" />
45+
<None Remove="icon.png" />
3546
<Content Include="icon.png">
3647
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
3748
</Content>

src/LoggerVisualizerSource/LoggerObjectSource.cs

Lines changed: 101 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -11,127 +11,139 @@ public class LoggerObjectSource : VisualizerObjectSource
1111
{
1212
public override void GetData(object target, Stream outgoingData)
1313
{
14-
if (target.GetType() == typeof(LoggerFactory))
14+
Telemetry.Initialize("Unknown");
15+
16+
try
1517
{
16-
var field = GetPrivateField(target, "_providerRegistrations");
17-
var result = new LoggerModel
18+
if (target.GetType() == typeof(LoggerFactory))
1819
{
19-
Loggers = []
20-
};
20+
var field = GetPrivateField(target, "_providerRegistrations");
21+
var result = new LoggerModel
22+
{
23+
Loggers = []
24+
};
25+
26+
Type listType = field.GetType();
27+
if (typeof(IEnumerable).IsAssignableFrom(listType))
28+
{
29+
// Cast the value to IEnumerable to access the elements
30+
IEnumerable list = (IEnumerable)field;
31+
32+
foreach (var item in list)
33+
{
34+
var provider = GetPublicField(item, "Provider") as ILoggerProvider;
35+
var providerTypeValue = provider?.GetType();
36+
37+
Logger loggerModel = new()
38+
{
39+
Name = providerTypeValue?.FullName,
40+
};
41+
42+
switch (provider?.GetType().FullName)
43+
{
44+
case "Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider":
45+
{
46+
var optionsMonitor = GetPrivateField(provider, "_options");
47+
var options = GetPublicProperty(optionsMonitor, "CurrentValue");
48+
loggerModel = ConsoleOptions(options, loggerModel);
49+
break;
50+
}
51+
case "Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider":
52+
{
53+
loggerModel = EventLogSettings(provider, loggerModel);
54+
break;
55+
}
56+
case "Elmah.Io.Extensions.Logging.ElmahIoLoggerProvider":
57+
{
58+
loggerModel = ElmahIoOptions(provider, loggerModel);
59+
break;
60+
}
61+
}
62+
63+
result.Loggers.Add(loggerModel);
64+
}
65+
}
2166

22-
Type listType = field.GetType();
23-
if (typeof(IEnumerable).IsAssignableFrom(listType))
67+
SerializeAsJson(outgoingData, result);
68+
}
69+
else if ((target.GetType().IsGenericType && target.GetType().GetGenericTypeDefinition() == typeof(Logger<>))
70+
|| target.GetType().FullName == "Microsoft.Extensions.Logging.Logger")
2471
{
25-
// Cast the value to IEnumerable to access the elements
26-
IEnumerable list = (IEnumerable)field;
72+
int i = 0;
73+
var r = 42 / i;
74+
ILogger logger = target.GetType().FullName == "Microsoft.Extensions.Logging.Logger"
75+
? target as ILogger
76+
: GetPrivateField(target, "_logger") as ILogger;
2777

28-
foreach (var item in list)
29-
{
30-
var provider = GetPublicField(item, "Provider") as ILoggerProvider;
31-
var providerTypeValue = provider?.GetType();
78+
if (logger == null) return;
79+
80+
var loggers = GetPublicProperty(logger, "Loggers") as Array;
81+
var messageLoggers = GetPublicProperty(logger, "MessageLoggers") as Array;
82+
83+
// Microsoft.Extensions.Loggging 8 has the name in _categoryName
84+
var name = GetPrivateField(logger, "_categoryName")?.ToString();
85+
86+
var minLevel = (CalculateEnabledLogLevel(logger) ?? LogLevel.None).ToString();
87+
var enabled = (minLevel != null).ToString();
3288

89+
var result = new LoggerModel
90+
{
91+
Name = name,
92+
MinLevel = minLevel,
93+
Enabled = enabled,
94+
Loggers = []
95+
};
96+
foreach (var l in loggers)
97+
{
98+
// If name was not resolved from _categoryName use the Category property from the first logger
99+
if (string.IsNullOrWhiteSpace(result.Name))
100+
{
101+
result.Name = GetPublicProperty(l, "Category")?.ToString();
102+
}
103+
var providerTypeValue = GetPublicProperty(l, "ProviderType") as Type;
104+
var internalLogger = GetPublicProperty(l, "Logger") as ILogger;
105+
var messageLogger = FirstOrNull(messageLoggers, internalLogger);
33106
Logger loggerModel = new()
34107
{
35-
Name = providerTypeValue?.FullName,
108+
Name = providerTypeValue.FullName,
109+
ExternalScope = GetPublicProperty(l, "ExternalScope").ToString(),
110+
MinLevel = (GetPublicProperty(messageLogger, "MinLevel") ?? LogLevel.None).ToString(),
36111
};
37112

38-
switch (provider?.GetType().FullName)
113+
switch (providerTypeValue.FullName)
39114
{
40115
case "Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider":
41116
{
42-
var optionsMonitor = GetPrivateField(provider, "_options");
43-
var options = GetPublicProperty(optionsMonitor, "CurrentValue");
117+
var options = GetNonPublicProperty(internalLogger, "Options");
44118
loggerModel = ConsoleOptions(options, loggerModel);
45119
break;
46120
}
47121
case "Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider":
48122
{
49-
loggerModel = EventLogSettings(provider, loggerModel);
123+
loggerModel = EventLogSettings(internalLogger, loggerModel);
50124
break;
51125
}
52126
case "Elmah.Io.Extensions.Logging.ElmahIoLoggerProvider":
53127
{
54-
loggerModel = ElmahIoOptions(provider, loggerModel);
128+
loggerModel = ElmahIoOptions(internalLogger, loggerModel);
55129
break;
56130
}
57131
}
58132

59133
result.Loggers.Add(loggerModel);
60134
}
61-
}
62135

63-
SerializeAsJson(outgoingData, result);
136+
SerializeAsJson(outgoingData, result);
137+
}
64138
}
65-
else if ((target.GetType().IsGenericType && target.GetType().GetGenericTypeDefinition() == typeof(Logger<>))
66-
|| target.GetType().FullName == "Microsoft.Extensions.Logging.Logger")
139+
catch (Exception e)
67140
{
68-
ILogger logger = target.GetType().FullName == "Microsoft.Extensions.Logging.Logger"
69-
? target as ILogger
70-
: GetPrivateField(target, "_logger") as ILogger;
71-
72-
if (logger == null) return;
73-
74-
var loggers = GetPublicProperty(logger, "Loggers") as Array;
75-
var messageLoggers = GetPublicProperty(logger, "MessageLoggers") as Array;
76-
77-
// Microsoft.Extensions.Loggging 8 has the name in _categoryName
78-
var name = GetPrivateField(logger, "_categoryName")?.ToString();
79-
80-
var minLevel = (CalculateEnabledLogLevel(logger) ?? LogLevel.None).ToString();
81-
var enabled = (minLevel != null).ToString();
82-
83-
var result = new LoggerModel
84-
{
85-
Name = name,
86-
MinLevel = minLevel,
87-
Enabled = enabled,
88-
Loggers = []
89-
};
90-
foreach (var l in loggers)
91-
{
92-
// If name was not resolved from _categoryName use the Category property from the first logger
93-
if (string.IsNullOrWhiteSpace(result.Name))
94-
{
95-
result.Name = GetPublicProperty(l, "Category")?.ToString();
96-
}
97-
var providerTypeValue = GetPublicProperty(l, "ProviderType") as Type;
98-
var internalLogger = GetPublicProperty(l, "Logger") as ILogger;
99-
var messageLogger = FirstOrNull(messageLoggers, internalLogger);
100-
Logger loggerModel = new()
101-
{
102-
Name = providerTypeValue.FullName,
103-
ExternalScope = GetPublicProperty(l, "ExternalScope").ToString(),
104-
MinLevel = (GetPublicProperty(messageLogger, "MinLevel") ?? LogLevel.None).ToString(),
105-
};
106-
107-
switch (providerTypeValue.FullName)
108-
{
109-
case "Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider":
110-
{
111-
var options = GetNonPublicProperty(internalLogger, "Options");
112-
loggerModel = ConsoleOptions(options, loggerModel);
113-
break;
114-
}
115-
case "Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider":
116-
{
117-
loggerModel = EventLogSettings(internalLogger, loggerModel);
118-
break;
119-
}
120-
case "Elmah.Io.Extensions.Logging.ElmahIoLoggerProvider":
121-
{
122-
loggerModel = ElmahIoOptions(internalLogger, loggerModel);
123-
break;
124-
}
125-
}
126-
127-
result.Loggers.Add(loggerModel);
128-
}
129-
130-
SerializeAsJson(outgoingData, result);
141+
Telemetry.TrackException(e);
142+
throw;
131143
}
132144
}
133145

134-
private Logger ElmahIoOptions(object objWithOptions, Logger loggerModel)
146+
private static Logger ElmahIoOptions(object objWithOptions, Logger loggerModel)
135147
{
136148
var options = GetPrivateField(objWithOptions, "_options");
137149
loggerModel.ApiKey = GetPublicProperty(options, "ApiKey")?.ToString();
@@ -140,7 +152,7 @@ private Logger ElmahIoOptions(object objWithOptions, Logger loggerModel)
140152
return loggerModel;
141153
}
142154

143-
private Logger EventLogSettings(object objWithSettings, Logger loggerModel)
155+
private static Logger EventLogSettings(object objWithSettings, Logger loggerModel)
144156
{
145157
var settings = GetPrivateField(objWithSettings, "_settings");
146158
loggerModel.LogName = GetPublicProperty(settings, "LogName")?.ToString();
@@ -149,7 +161,7 @@ private Logger EventLogSettings(object objWithSettings, Logger loggerModel)
149161
return loggerModel;
150162
}
151163

152-
private Logger ConsoleOptions(object options, Logger loggerModel)
164+
private static Logger ConsoleOptions(object options, Logger loggerModel)
153165
{
154166
loggerModel.FormatterName = GetPublicProperty(options, "FormatterName")?.ToString();
155167
loggerModel.TimestampFormat = GetPublicProperty(options, "TimestampFormat")?.ToString();
@@ -239,14 +251,12 @@ private static object GetPrivateField(object obj, string name)
239251
return theValue;
240252
}
241253

242-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", Justification = "")]
243254
private static object GetPublicField(object obj, string name)
244255
{
245256
FieldInfo fieldInfo = obj.GetType().GetField(name, BindingFlags.Public | BindingFlags.Instance);
246257
if (fieldInfo == null) return null;
247258
var theValue = fieldInfo.GetValue(obj);
248259
return theValue;
249260
}
250-
251261
}
252262
}

src/LoggerVisualizerSource/LoggerVisualizerSource.csproj

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@
55
<LangVersion>12</LangVersion>
66
</PropertyGroup>
77

8+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
9+
<DebugType>embedded</DebugType>
10+
</PropertyGroup>
11+
12+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
13+
<DebugType>embedded</DebugType>
14+
</PropertyGroup>
15+
816
<ItemGroup>
9-
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
17+
<PackageReference Include="Elmah.Io.Client" Version="5.3.131" />
18+
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
1019
<PackageReference Include="Microsoft.VisualStudio.DebuggerVisualizers" Version="17.6.1032901" />
1120
</ItemGroup>
1221

0 commit comments

Comments
 (0)