forked from exceptionless/serilog-sinks-exceptionless
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExceptionlessSink.cs
160 lines (142 loc) · 7.02 KB
/
ExceptionlessSink.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Generic;
using Exceptionless;
using Exceptionless.Dependency;
using Exceptionless.Logging;
using Exceptionless.Models;
using Exceptionless.Models.Data;
using Serilog.Core;
using Serilog.Events;
namespace Serilog.Sinks.Exceptionless {
/// <summary>
/// Exceptionless Sink
/// </summary>
public class ExceptionlessSink : ILogEventSink, IDisposable {
private readonly string[] _defaultTags;
private readonly Func<EventBuilder, EventBuilder> _additionalOperation;
private readonly bool _includeProperties;
private readonly ExceptionlessClient _client;
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionlessSink"/> class.
/// </summary>
/// <param name="apiKey">
/// The API key that will be used when sending events to the server.
/// </param>
/// <param name="serverUrl">
/// Optional URL of the server events will be sent to.
/// </param>
/// <param name="defaultTags">
/// Default tags to be added to every log event.
/// </param>
/// <param name="additionalOperation">
/// Optional operation to run against the Error Builder before submitting to Exceptionless
/// </param>
/// <param name="includeProperties">
/// If false then the Serilog properties will not be submitted to Exceptionless
/// </param>
/// <param name="restrictedToMinimumLevel">
/// The minimum log event level required in order to write an event to the sink.
/// </param>
public ExceptionlessSink(
string apiKey,
string serverUrl = null,
string[] defaultTags = null,
Func<EventBuilder, EventBuilder> additionalOperation = null,
bool includeProperties = true,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum
) {
if (apiKey == null)
throw new ArgumentNullException(nameof(apiKey));
_client = new ExceptionlessClient(config => {
if (!String.IsNullOrEmpty(apiKey) && apiKey != "API_KEY_HERE")
config.ApiKey = apiKey;
if (!String.IsNullOrEmpty(serverUrl))
config.ServerUrl = serverUrl;
config.UseInMemoryStorage();
config.UseLogger(new SelfLogLogger());
config.SetDefaultMinLogLevel(restrictedToMinimumLevel.GetLevel());
});
_defaultTags = defaultTags;
_additionalOperation = additionalOperation;
_includeProperties = includeProperties;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionlessSink"/> class.
/// </summary>
/// <param name="additionalOperation">
/// Optional operation to run against the Error Builder before submitting to Exceptionless
/// </param>
/// <param name="includeProperties">
/// If false then the Serilog properties will not be submitted to Exceptionless
/// </param>
/// <param name="client">
/// Optional instance of <see cref="ExceptionlessClient"/> to use.
/// </param>
/// <param name="restrictedToMinimumLevel">
/// The minimum log event level required in order to write an event to the sink.
/// </param>
public ExceptionlessSink(
Func<EventBuilder, EventBuilder> additionalOperation = null,
bool includeProperties = true,
ExceptionlessClient client = null,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum
) {
_additionalOperation = additionalOperation;
_includeProperties = includeProperties;
_client = client ?? ExceptionlessClient.Default;
if (_client.Configuration.Resolver.HasDefaultRegistration<IExceptionlessLog, NullExceptionlessLog>()) {
_client.Configuration.UseLogger(new SelfLogLogger());
}
_client.Configuration.SetDefaultMinLogLevel(restrictedToMinimumLevel.GetLevel());
}
public void Emit(LogEvent logEvent) {
if (logEvent == null || !_client.Configuration.IsValid)
return;
var minLogLevel = _client.Configuration.Settings.GetMinLogLevel(logEvent.GetSource());
if (logEvent.Level.GetLevel() < minLogLevel)
return;
var builder = _client.CreateFromLogEvent(logEvent).AddTags(_defaultTags);
if (_includeProperties) {
foreach (var prop in logEvent.Properties)
{
switch (prop.Key)
{
case Constants.SourceContextPropertyName:
continue;
case Event.KnownDataKeys.UserInfo when prop.Value is StructureValue uis && String.Equals(nameof(UserInfo), uis.TypeTag):
var userInfo = uis.FlattenProperties() as Dictionary<string, object>;
if (userInfo is null)
continue;
// UserDescription Data property is currently ignored.
string identity = userInfo[nameof(UserInfo.Identity)] as string;
string name = userInfo[nameof(UserInfo.Name)] as string;
if (!String.IsNullOrWhiteSpace(identity) || !String.IsNullOrWhiteSpace(name))
builder.SetUserIdentity(identity, name);
break;
case Event.KnownDataKeys.UserDescription when prop.Value is StructureValue uds && String.Equals(nameof(UserDescription), uds.TypeTag):
var userDescription = uds.FlattenProperties() as Dictionary<string, object>;
if (userDescription is null)
continue;
// UserDescription Data property is currently ignored.
string emailAddress = userDescription[nameof(UserDescription.EmailAddress)] as string;
string description = userDescription[nameof(UserDescription.Description)] as string;
if (!String.IsNullOrWhiteSpace(emailAddress) || !String.IsNullOrWhiteSpace(description))
builder.SetUserDescription(emailAddress, description);
break;
case "Tags":
builder.AddTags(prop.Value.GetTags());
break;
default:
builder.SetProperty(prop.Key, prop.Value.FlattenProperties());
break;
}
}
}
_additionalOperation?.Invoke(builder);
builder.Submit();
}
void IDisposable.Dispose() {
_client?.ProcessQueueAsync().GetAwaiter().GetResult();
}
}
}