-
Notifications
You must be signed in to change notification settings - Fork 126
/
Copy pathFileLogger.cs
293 lines (242 loc) · 9.81 KB
/
FileLogger.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
//
// FileLogger.cs
//
// Copyright (c) 2018 Couchbase, Inc All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Couchbase.Lite.Internal.Logging;
using Couchbase.Lite.Support;
using Couchbase.Lite.Sync;
using Couchbase.Lite.Util;
using LiteCore;
using LiteCore.Interop;
using LiteCore.Util;
using Constants = Couchbase.Lite.Info.Constants;
namespace Couchbase.Lite.Logging
{
/// <summary>
/// A class that describes the file configuration for the <see cref="FileLogger"/>
/// class. These options must be set atomically so they won't take effect unless
/// a new configuration object is set on the logger. Attempting to modify an in-use
/// configuration object will result in an exception being thrown.
/// </summary>
public sealed class LogFileConfiguration
{
#region Constants
private const string Tag = nameof(LogFileConfiguration);
#endregion
#region Variables
private readonly Freezer _freezer = new Freezer();
private int _maxRotateCount = Constants.DefaultLogFileMaxRotateCount;
private long _maxSize = Constants.DefaultLogFileMaxSize;
private bool _usePlaintext = Constants.DefaultLogFileUsePlaintext;
#endregion
#region Properties
/// <summary>
/// Gets the directory that the log files are stored in.
/// </summary>
public string Directory { get; }
/// <summary>
/// Gets or sets the number of rotated logs that are saved (i.e.
/// if the value is 1, then 2 logs will be present: the 'current'
/// and the 'rotated')
/// Default value is <see cref="Constants.DefaultLogFileMaxRotateCount" />
/// </summary>
public int MaxRotateCount
{
get => _maxRotateCount;
set => _freezer.SetValue(ref _maxRotateCount, value);
}
/// <summary>
/// Gets or sets the max size of the log files in bytes. If a log file
/// passes this size then a new log file will be started. This
/// number is a best effort and the actual size may go over slightly.
/// Default value is <see cref="Constants.DefaultLogFileMaxSize" />
/// </summary>
public long MaxSize
{
get => _maxSize;
set => _freezer.SetValue(ref _maxSize, value);
}
/// <summary>
/// Gets or sets whether or not to log in plaintext. The default is
/// to log in a binary encoded format that is more CPU and I/O friendly
/// and enabling plaintext is not recommended in production.
/// Default value is <see cref="Constants.DefaultLogFileUsePlaintext" />
/// </summary>
public bool UsePlaintext
{
get => _usePlaintext;
set => _freezer.SetValue(ref _usePlaintext, value);
}
#endregion
#region Constructors
/// <summary>
/// Constructs a file configuration object with the given directory
/// </summary>
/// <param name="directory">The directory that logs will be written to</param>
public LogFileConfiguration(string directory)
{
Directory = CBDebug.MustNotBeNull(WriteLog.To.Database, Tag, nameof(directory), directory);
}
/// <summary>
/// Constructs a file configuration object based on another one so
/// that it may be modified
/// </summary>
/// <param name="other">The other configuration to copy settings from</param>
public LogFileConfiguration(LogFileConfiguration other)
{
CBDebug.MustNotBeNull(WriteLog.To.Database, Tag, nameof(other), other);
Directory = other.Directory;
MaxRotateCount = other.MaxRotateCount;
MaxSize = other.MaxSize;
UsePlaintext = other.UsePlaintext;
}
/// <summary>
/// Constructs a file configuration object based on another one but changing
/// the directory
/// </summary>
/// <param name="directory">The directory that logs will be written to</param>
/// <param name="other">The other configuration to copy the other settings from</param>
public LogFileConfiguration(string directory, LogFileConfiguration? other)
: this(directory)
{
if (other != null) {
MaxRotateCount = other.MaxRotateCount;
MaxSize = other.MaxSize;
UsePlaintext = other.UsePlaintext;
}
}
#endregion
#region Internal Methods
internal LogFileConfiguration Freeze()
{
var retVal = new LogFileConfiguration(this);
retVal._freezer.Freeze("Cannot modify a FileConfiguration that is currently in use");
return retVal;
}
#endregion
}
/// <summary>
/// A class that controls the file logging facility of
/// Couchbase Lite
/// </summary>
public sealed class FileLogger : ILogger
{
#region Variables
private readonly Dictionary<LogDomain,IntPtr> _domainObjects = new Dictionary<LogDomain, IntPtr>();
private LogFileConfiguration? _config;
#endregion
#region Properties
/// <summary>
/// Gets or sets the configuration currently in use on the file logger.
/// Note that once it is set, it can no longer be modified and doing so
/// will throw an exception.
/// </summary>
public LogFileConfiguration? Config
{
get => _config;
set {
if (value == null) {
WriteLog.To.Database.W("Logging", "Database.Log.File.Config is now null, meaning file logging is disabled. Log files required for product support are not being generated.");
}
_config = value?.Freeze();
UpdateConfig();
}
}
/// <summary>
/// Gets or sets the max level to log. The set level and all
/// levels under it will be logged (i.e. <c>Error</c> will log
/// only errors but <c>Warning</c> will log warnings and errors)
/// </summary>
public LogLevel Level
{
get => (LogLevel)Native.c4log_binaryFileLevel();
set {
if (Config == null) {
throw new InvalidOperationException("Cannot set logging level without a configuration");
}
Native.c4log_setBinaryFileLevel((C4LogLevel) value);
}
}
#endregion
#region Constructors
/// <summary>
/// Default Constructor
/// </summary>
public FileLogger()
{
SetupDomainObjects();
Native.c4log_setBinaryFileLevel(C4LogLevel.None);
}
#endregion
#region Private Methods
private unsafe void SetupDomainObjects()
{
var bytes = (byte *)Marshal.StringToHGlobalAnsi("Couchbase");
_domainObjects[LogDomain.Couchbase] = (IntPtr)Native.c4log_getDomain(bytes, true);
bytes = (byte *)Marshal.StringToHGlobalAnsi("DB");
_domainObjects[LogDomain.Database] = (IntPtr)Native.c4log_getDomain(bytes, true);
bytes = (byte *)Marshal.StringToHGlobalAnsi("Query");
_domainObjects[LogDomain.Query] = (IntPtr)Native.c4log_getDomain(bytes, true);
bytes = (byte *)Marshal.StringToHGlobalAnsi("Sync");
_domainObjects[LogDomain.Replicator] = (IntPtr)Native.c4log_getDomain(bytes, true);
foreach (var domain in _domainObjects) {
Native.c4log_setLevel((C4LogDomain *)domain.Value.ToPointer(),
C4LogLevel.Debug);
}
foreach (var domain in new[] {
WriteLog.LogDomainBLIP,
WriteLog.LogDomainSyncBusy,
WriteLog.LogDomainWebSocket
}) {
Native.c4log_setLevel(domain, C4LogLevel.Debug);
}
}
private unsafe void UpdateConfig()
{
if (_config != null) {
Directory.CreateDirectory(_config.Directory);
}
using (var dir = new C4String(_config?.Directory))
using (var header = new C4String(HTTPLogic.UserAgent)) {
var options = new C4LogFileOptions
{
base_path = dir.AsFLSlice(),
log_level = (C4LogLevel) Level,
max_rotate_count = _config?.MaxRotateCount ?? 1,
max_size_bytes = _config?.MaxSize ?? 1024 * 500L,
use_plaintext = _config?.UsePlaintext ?? false,
header = header.AsFLSlice()
};
LiteCoreBridge.Check(err => Native.c4log_writeToBinaryFile(options, err));
}
}
#endregion
#region ILogger
/// <inheritdoc />
public unsafe void Log(LogLevel level, LogDomain domain, string message)
{
if (level < Level || !_domainObjects.ContainsKey(domain)) {
return;
}
Native.c4slog((C4LogDomain*)_domainObjects[domain], (C4LogLevel)level, message);
}
#endregion
}
}