jt
2021-06-10 5d0d028456874576560552f5a5c4e8b801786f11
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you 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.
//
#endregion
 
// .NET Compact Framework 1.0 has no support for System.Runtime.Remoting
#if !NETCF
 
using System;
using System.Collections;
using System.Threading;
 
using System.Runtime.Remoting.Messaging;
 
using log4net.Layout;
using log4net.Core;
using log4net.Util;
 
namespace log4net.Appender
{
    /// <summary>
    /// Delivers logging events to a remote logging sink. 
    /// </summary>
    /// <remarks>
    /// <para>
    /// This Appender is designed to deliver events to a remote sink. 
    /// That is any object that implements the <see cref="IRemoteLoggingSink"/>
    /// interface. It delivers the events using .NET remoting. The
    /// object to deliver events to is specified by setting the
    /// appenders <see cref="RemotingAppender.Sink"/> property.</para>
    /// <para>
    /// The RemotingAppender buffers events before sending them. This allows it to 
    /// make more efficient use of the remoting infrastructure.</para>
    /// <para>
    /// Once the buffer is full the events are still not sent immediately. 
    /// They are scheduled to be sent using a pool thread. The effect is that 
    /// the send occurs asynchronously. This is very important for a 
    /// number of non obvious reasons. The remoting infrastructure will 
    /// flow thread local variables (stored in the <see cref="CallContext"/>),
    /// if they are marked as <see cref="ILogicalThreadAffinative"/>, across the 
    /// remoting boundary. If the server is not contactable then
    /// the remoting infrastructure will clear the <see cref="ILogicalThreadAffinative"/>
    /// objects from the <see cref="CallContext"/>. To prevent a logging failure from
    /// having side effects on the calling application the remoting call must be made
    /// from a separate thread to the one used by the application. A <see cref="ThreadPool"/>
    /// thread is used for this. If no <see cref="ThreadPool"/> thread is available then
    /// the events will block in the thread pool manager until a thread is available.</para>
    /// <para>
    /// Because the events are sent asynchronously using pool threads it is possible to close 
    /// this appender before all the queued events have been sent.
    /// When closing the appender attempts to wait until all the queued events have been sent, but 
    /// this will timeout after 30 seconds regardless.</para>
    /// <para>
    /// If this appender is being closed because the <see cref="AppDomain.ProcessExit"/>
    /// event has fired it may not be possible to send all the queued events. During process
    /// exit the runtime limits the time that a <see cref="AppDomain.ProcessExit"/>
    /// event handler is allowed to run for. If the runtime terminates the threads before
    /// the queued events have been sent then they will be lost. To ensure that all events
    /// are sent the appender must be closed before the application exits. See 
    /// <see cref="log4net.Core.LoggerManager.Shutdown"/> for details on how to shutdown
    /// log4net programmatically.</para>
    /// </remarks>
    /// <seealso cref="IRemoteLoggingSink" />
    /// <author>Nicko Cadell</author>
    /// <author>Gert Driesen</author>
    /// <author>Daniel Cazzulino</author>
    public class RemotingAppender : BufferingAppenderSkeleton
    {
        #region Public Instance Constructors
 
        /// <summary>
        /// Initializes a new instance of the <see cref="RemotingAppender" /> class.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Default constructor.
        /// </para>
        /// </remarks>
        public RemotingAppender()
        {
        }
 
        #endregion Public Instance Constructors
 
        #region Public Instance Properties
 
        /// <summary>
        /// Gets or sets the URL of the well-known object that will accept 
        /// the logging events.
        /// </summary>
        /// <value>
        /// The well-known URL of the remote sink.
        /// </value>
        /// <remarks>
        /// <para>
        /// The URL of the remoting sink that will accept logging events.
        /// The sink must implement the <see cref="IRemoteLoggingSink"/>
        /// interface.
        /// </para>
        /// </remarks>
        public string Sink
        {
            get { return m_sinkUrl; }
            set { m_sinkUrl = value; }
        }
 
        #endregion Public Instance Properties
 
        #region Implementation of IOptionHandler
 
        /// <summary>
        /// Initialize the appender based on the options set
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is part of the <see cref="IOptionHandler"/> delayed object
        /// activation scheme. The <see cref="ActivateOptions"/> method must 
        /// be called on this object after the configuration properties have
        /// been set. Until <see cref="ActivateOptions"/> is called this
        /// object is in an undefined state and must not be used. 
        /// </para>
        /// <para>
        /// If any of the configuration properties are modified then 
        /// <see cref="ActivateOptions"/> must be called again.
        /// </para>
        /// </remarks>
#if NET_4_0
        [System.Security.SecuritySafeCritical]
#endif
        override public void ActivateOptions() 
        {
            base.ActivateOptions();
 
            IDictionary channelProperties = new Hashtable(); 
            channelProperties["typeFilterLevel"] = "Full";
 
            m_sinkObj = (IRemoteLoggingSink)Activator.GetObject(typeof(IRemoteLoggingSink), m_sinkUrl, channelProperties);
        }
 
        #endregion
 
        #region Override implementation of BufferingAppenderSkeleton
 
        /// <summary>
        /// Send the contents of the buffer to the remote sink.
        /// </summary>
        /// <remarks>
        /// The events are not sent immediately. They are scheduled to be sent
        /// using a pool thread. The effect is that the send occurs asynchronously.
        /// This is very important for a number of non obvious reasons. The remoting
        /// infrastructure will flow thread local variables (stored in the <see cref="CallContext"/>),
        /// if they are marked as <see cref="ILogicalThreadAffinative"/>, across the 
        /// remoting boundary. If the server is not contactable then
        /// the remoting infrastructure will clear the <see cref="ILogicalThreadAffinative"/>
        /// objects from the <see cref="CallContext"/>. To prevent a logging failure from
        /// having side effects on the calling application the remoting call must be made
        /// from a separate thread to the one used by the application. A <see cref="ThreadPool"/>
        /// thread is used for this. If no <see cref="ThreadPool"/> thread is available then
        /// the events will block in the thread pool manager until a thread is available.
        /// </remarks>
        /// <param name="events">The events to send.</param>
        override protected void SendBuffer(LoggingEvent[] events)
        {
            // Setup for an async send
            BeginAsyncSend();
 
            // Send the events
            if (!ThreadPool.QueueUserWorkItem(new WaitCallback(SendBufferCallback), events))
            {
                // Cancel the async send
                EndAsyncSend();
 
                ErrorHandler.Error("RemotingAppender ["+Name+"] failed to ThreadPool.QueueUserWorkItem logging events in SendBuffer.");
            }
        }
 
        /// <summary>
        /// Override base class close.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method waits while there are queued work items. The events are
        /// sent asynchronously using <see cref="ThreadPool"/> work items. These items
        /// will be sent once a thread pool thread is available to send them, therefore
        /// it is possible to close the appender before all the queued events have been
        /// sent.</para>
        /// <para>
        /// This method attempts to wait until all the queued events have been sent, but this 
        /// method will timeout after 30 seconds regardless.</para>
        /// <para>
        /// If the appender is being closed because the <see cref="AppDomain.ProcessExit"/>
        /// event has fired it may not be possible to send all the queued events. During process
        /// exit the runtime limits the time that a <see cref="AppDomain.ProcessExit"/>
        /// event handler is allowed to run for.</para>
        /// </remarks>
        override protected void OnClose()
        {
            base.OnClose();
 
            // Wait for the work queue to become empty before closing, timeout 30 seconds
            if (!m_workQueueEmptyEvent.WaitOne(30 * 1000, false))
            {
                ErrorHandler.Error("RemotingAppender ["+Name+"] failed to send all queued events before close, in OnClose.");
            }
        }
 
        #endregion
 
        /// <summary>
        /// A work item is being queued into the thread pool
        /// </summary>
        private void BeginAsyncSend()
        {
            // The work queue is not empty
            m_workQueueEmptyEvent.Reset();
 
            // Increment the queued count
            Interlocked.Increment(ref m_queuedCallbackCount);
        }
 
        /// <summary>
        /// A work item from the thread pool has completed
        /// </summary>
        private void EndAsyncSend()
        {
            // Decrement the queued count
            if (Interlocked.Decrement(ref m_queuedCallbackCount) <= 0)
            {
                // If the work queue is empty then set the event
                m_workQueueEmptyEvent.Set();
            }
        }
 
        /// <summary>
        /// Send the contents of the buffer to the remote sink.
        /// </summary>
        /// <remarks>
        /// This method is designed to be used with the <see cref="ThreadPool"/>.
        /// This method expects to be passed an array of <see cref="LoggingEvent"/>
        /// objects in the state param.
        /// </remarks>
        /// <param name="state">the logging events to send</param>
        private void SendBufferCallback(object state)
        {
            try
            {
                LoggingEvent[] events = (LoggingEvent[])state;
 
                // Send the events
                m_sinkObj.LogEvents(events);
            }
            catch(Exception ex)
            {
                ErrorHandler.Error("Failed in SendBufferCallback", ex);
            }
            finally
            {
                EndAsyncSend();
            }
        }
 
        #region Private Instance Fields
 
        /// <summary>
        /// The URL of the remote sink.
        /// </summary>
        private string m_sinkUrl;
 
        /// <summary>
        /// The local proxy (.NET remoting) for the remote logging sink.
        /// </summary>
        private IRemoteLoggingSink m_sinkObj;
 
        /// <summary>
        /// The number of queued callbacks currently waiting or executing
        /// </summary>
        private int m_queuedCallbackCount = 0;
 
        /// <summary>
        /// Event used to signal when there are no queued work items
        /// </summary>
        /// <remarks>
        /// This event is set when there are no queued work items. In this
        /// state it is safe to close the appender.
        /// </remarks>
        private ManualResetEvent m_workQueueEmptyEvent = new ManualResetEvent(true);
 
        #endregion Private Instance Fields
 
        /// <summary>
        /// Interface used to deliver <see cref="LoggingEvent"/> objects to a remote sink.
        /// </summary>
        /// <remarks>
        /// This interface must be implemented by a remoting sink
        /// if the <see cref="RemotingAppender"/> is to be used
        /// to deliver logging events to the sink.
        /// </remarks>
        public interface IRemoteLoggingSink
        {
            /// <summary>
            /// Delivers logging events to the remote sink
            /// </summary>
            /// <param name="events">Array of events to log.</param>
            /// <remarks>
            /// <para>
            /// Delivers logging events to the remote sink
            /// </para>
            /// </remarks>
            void LogEvents(LoggingEvent[] events);
        }
    }
}
 
#endif // !NETCF