/
InteractiveLoggerProvider.cs
126 lines (103 loc) · 4.24 KB
/
InteractiveLoggerProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Extensions.Logging;
namespace Microsoft.DotNet.Interactive.AspNetCore;
internal class InteractiveLoggerProvider : ILoggerProvider
{
private volatile ExecutionContext _pocketLoggerEC;
public event Action<LogMessage> Posted;
public ILogger CreateLogger(string categoryName)
{
return new InteractiveLogger(this, categoryName);
}
public IDisposable SubscribePocketLogerWithCurrentEC() => new PocketLoggerSubscription(this);
public void Dispose()
{
}
private class InteractiveLogger : ILogger
{
private readonly InteractiveLoggerProvider _loggerProvider;
private readonly string _categoryName;
public InteractiveLogger(InteractiveLoggerProvider loggerProvider, string categoryName)
{
_loggerProvider = loggerProvider;
_categoryName = categoryName;
}
public IDisposable BeginScope<TState>(TState state)
{
// InteractiveLoggerProvider.Dispose() no-ops
return _loggerProvider;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var logMessage = new LogMessage
{
LogLevel = logLevel,
Category = _categoryName,
EventId = eventId,
Message = formatter(state, exception),
Exception = exception
};
_loggerProvider.Posted?.Invoke(logMessage);
PostPocketLog(logMessage);
}
private void PostPocketLog(LogMessage logMessage)
{
static void PocketLogCallback(object state)
{
var logMessage = (LogMessage)state;
static Pocket.LogLevel ToPocketLogLevel(LogLevel logLevel) => logLevel switch
{
LogLevel.Trace => Pocket.LogLevel.Trace,
LogLevel.Debug => Pocket.LogLevel.Debug,
LogLevel.Information => Pocket.LogLevel.Information,
LogLevel.Warning => Pocket.LogLevel.Warning,
LogLevel.Error => Pocket.LogLevel.Error,
LogLevel.Critical => Pocket.LogLevel.Critical,
_ => throw new NotSupportedException()
};
Pocket.Logger.Log.Post(new Pocket.LogEntry(
logLevel: ToPocketLogLevel(logMessage.LogLevel),
messageTemplate: logMessage.Message,
exception: logMessage.Exception,
category: logMessage.Category,
operationName: logMessage.EventId.ToString()));
}
if (_loggerProvider._pocketLoggerEC is { } currentEc)
{
ExecutionContext.Run(currentEc.CreateCopy(), PocketLogCallback, logMessage);
}
else
{
PocketLogCallback(logMessage);
}
}
}
private class PocketLoggerSubscription : IDisposable
{
private readonly InteractiveLoggerProvider _loggerProvider;
private readonly ExecutionContext _previousEC;
// This is only used to assert that loggerProvider._pocketLoggerEC hasn't changed in Dispose.
private readonly ExecutionContext _currentEC;
public PocketLoggerSubscription(InteractiveLoggerProvider loggerProvider)
{
_loggerProvider = loggerProvider;
_previousEC = loggerProvider._pocketLoggerEC;
_currentEC = ExecutionContext.Capture();
loggerProvider._pocketLoggerEC = _currentEC;
}
public void Dispose()
{
Debug.Assert(ReferenceEquals(_loggerProvider._pocketLoggerEC, _currentEC),
"SubscribePocketLogerWithCurrentEC() should never be called concurrently.");
_loggerProvider._pocketLoggerEC = _previousEC;
}
}
}