/
ActivityLoggerOutput.cs
247 lines (213 loc) · 8.7 KB
/
ActivityLoggerOutput.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
#region LGPL License
/*----------------------------------------------------------------------------
* This file (CK.Core\ActivityLogger\Impl\ActivityLoggerOutput.cs) is part of CiviKey.
*
* CiviKey is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CiviKey is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with CiviKey. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright © 2007-2012,
* Invenietis <http://www.invenietis.com>,
* In’Tech INFO <http://www.intechinfo.fr>,
* All rights reserved.
*-----------------------------------------------------------------------------*/
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
namespace CK.Core
{
/// <summary>
/// Implementation of <see cref="IActivityLoggerOutput"/> for <see cref="IActivityLogger.Output"/>.
/// </summary>
public class ActivityLoggerOutput : IActivityLoggerOutput
{
#if net40
internal class OutList<T> : List<T>, IReadOnlyList<T>
{
internal OutList()
: base()
{
}
internal OutList(IEnumerable<T> basedOn)
: base( basedOn )
{
}
}
OutList<IActivityLoggerClient> Clients
{
get { return _list as OutList<IActivityLoggerClient>; }
}
private IList<T> CreateNewList<T>( List<T> list = null )
{
if( list == null ) return new OutList<T>();
else return new OutList<T>( list );
}
#endif
#if net45
List<IActivityLoggerClient> Clients
{
get { return _list as List<IActivityLoggerClient>; }
}
private IList<T> CreateNewList<T>( List<T> list )
{
return new List<T>( list );
}
#endif
object _list;
readonly ActivityLoggerBridgeTarget _externalInput;
internal class EmptyOutput : IActivityLoggerOutput
{
ActivityLoggerBridgeTarget _empty = new ActivityLoggerBridgeTarget();
public IActivityLoggerClient ExternalInput
{
get { return ActivityLoggerClient.Empty; }
}
public IActivityLoggerOutput RegisterClient( IActivityLoggerClient client )
{
return this;
}
public IActivityLoggerOutput UnregisterClient( IActivityLoggerClient client )
{
return this;
}
public IReadOnlyList<IActivityLoggerClient> RegisteredClients
{
get { return CKReadOnlyListEmpty<IActivityLoggerClient>.Empty; }
}
ActivityLoggerBridgeTarget IActivityLoggerOutput.ExternalInput
{
get { throw new NotImplementedException(); }
}
}
/// <summary>
/// Empty <see cref="IActivityLoggerOutput"/> (null object design pattern).
/// </summary>
static public readonly IActivityLoggerOutput Empty = new EmptyOutput();
/// <summary>
/// Initializes a new <see cref="ActivityLoggerOutput"/> bound to a <see cref="IActivityLogger"/>.
/// </summary>
/// <param name="logger"></param>
public ActivityLoggerOutput( IActivityLogger logger )
{
#if net40
_list = new OutList<IActivityLoggerClient>();
#endif
#if net45
_list = new List<IActivityLoggerClient>();
#endif
_externalInput = new ActivityLoggerBridgeTarget( logger, true );
}
/// <summary>
/// Gets an entry point for other loggers: by registering <see cref="ActivityLoggerBridge"/> in other <see cref="IActivityLogger.Output"/>
/// bound to this <see cref="ActivityLoggerBridgeTarget"/>, log streams can easily be merged.
/// </summary>
public ActivityLoggerBridgeTarget ExternalInput
{
get { return _externalInput; }
}
/// <summary>
/// Gets the associated <see cref="IActivityLogger"/>.
/// </summary>
protected IActivityLogger Logger { get { return _externalInput.FinalLogger; } }
/// <summary>
/// Registers an <see cref="IActivityLoggerClient"/> to the <see cref="RegisteredClients"/> list.
/// Duplicate IActivityLoggerClient are silently ignored.
/// </summary>
/// <param name="client">An <see cref="IActivityLoggerClient"/> implementation.</param>
/// <returns>This object to enable fluent syntax.</returns>
public IActivityLoggerOutput RegisterClient( IActivityLoggerClient client )
{
if( client == null ) throw new ArgumentNullException( "client" );
var localClients = Clients;
if( !localClients.Contains( client ) )
{
IActivityLoggerBoundClient bound = client as IActivityLoggerBoundClient;
if( bound != null ) bound.SetLogger( Logger, false );
IList<IActivityLoggerClient> loggers = null;
do
{
localClients = Clients;
loggers = CreateNewList<IActivityLoggerClient>( localClients.ToList() );
if( !loggers.Contains( client ) ) { loggers.Insert( 0, client ); }
else break;
}
while( Interlocked.CompareExchange( ref _list, loggers, localClients ) != localClients );
}
return this;
}
/// <summary>
/// Unregisters the given <see cref="IActivityLoggerClient"/> from the <see cref="RegisteredClients"/> list.
/// Silently ignores unregistered client.
/// </summary>
/// <param name="client">An <see cref="IActivityLoggerClient"/> implementation.</param>
/// <returns>This object to enable fluent syntax.</returns>
public IActivityLoggerOutput UnregisterClient( IActivityLoggerClient client )
{
if( client == null ) throw new ArgumentNullException( "client" );
IList<IActivityLoggerClient> localClients = Clients;
if( localClients.Contains( client ) )
{
IActivityLoggerBoundClient bound = client as IActivityLoggerBoundClient;
if( bound != null ) bound.SetLogger( null, false );
IList<IActivityLoggerClient> loggers = null;
do
{
localClients = Clients;
loggers = CreateNewList<IActivityLoggerClient>( localClients.ToList() );
var idx = loggers.IndexOf( client );
if( idx >= 0 ) loggers.RemoveAt( idx );
else break;
}
while( Interlocked.CompareExchange( ref _list, loggers, localClients ) != localClients );
}
return this;
}
internal void ForceRemoveBuggyClient( IActivityLoggerClient client )
{
Debug.Assert( client != null && Clients.Contains( client ) );
if( client == null ) throw new ArgumentNullException( "client" );
IActivityLoggerBoundClient bound = client as IActivityLoggerBoundClient;
if( bound != null )
{
try
{
bound.SetLogger( null, true );
}
catch( Exception ex )
{
ActivityLogger.LoggingError.Add( ex, "While removing the buggy client." );
}
}
IList<IActivityLoggerClient> localClients = Clients;
IList<IActivityLoggerClient> loggers = null;
do
{
localClients = Clients;
loggers = CreateNewList<IActivityLoggerClient>( localClients.ToList() );
var idx = loggers.IndexOf( client );
if( idx >= 0 ) loggers.RemoveAt( idx );
else break;
}
while( Interlocked.CompareExchange( ref _list, loggers, localClients ) != localClients );
}
/// <summary>
/// Gets the list of registered <see cref="IActivityLoggerClient"/>.
/// </summary>
public IReadOnlyList<IActivityLoggerClient> RegisteredClients
{
get { return Clients; }
}
}
}