RemotingAppender.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. #region Apache License
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to you under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. // .NET Compact Framework 1.0 has no support for System.Runtime.Remoting
  20. #if !NETCF
  21. using System;
  22. using System.Collections;
  23. using System.Threading;
  24. using System.Runtime.Remoting.Messaging;
  25. using log4net.Layout;
  26. using log4net.Core;
  27. using log4net.Util;
  28. namespace log4net.Appender
  29. {
  30. /// <summary>
  31. /// Delivers logging events to a remote logging sink.
  32. /// </summary>
  33. /// <remarks>
  34. /// <para>
  35. /// This Appender is designed to deliver events to a remote sink.
  36. /// That is any object that implements the <see cref="IRemoteLoggingSink"/>
  37. /// interface. It delivers the events using .NET remoting. The
  38. /// object to deliver events to is specified by setting the
  39. /// appenders <see cref="RemotingAppender.Sink"/> property.</para>
  40. /// <para>
  41. /// The RemotingAppender buffers events before sending them. This allows it to
  42. /// make more efficient use of the remoting infrastructure.</para>
  43. /// <para>
  44. /// Once the buffer is full the events are still not sent immediately.
  45. /// They are scheduled to be sent using a pool thread. The effect is that
  46. /// the send occurs asynchronously. This is very important for a
  47. /// number of non obvious reasons. The remoting infrastructure will
  48. /// flow thread local variables (stored in the <see cref="CallContext"/>),
  49. /// if they are marked as <see cref="ILogicalThreadAffinative"/>, across the
  50. /// remoting boundary. If the server is not contactable then
  51. /// the remoting infrastructure will clear the <see cref="ILogicalThreadAffinative"/>
  52. /// objects from the <see cref="CallContext"/>. To prevent a logging failure from
  53. /// having side effects on the calling application the remoting call must be made
  54. /// from a separate thread to the one used by the application. A <see cref="ThreadPool"/>
  55. /// thread is used for this. If no <see cref="ThreadPool"/> thread is available then
  56. /// the events will block in the thread pool manager until a thread is available.</para>
  57. /// <para>
  58. /// Because the events are sent asynchronously using pool threads it is possible to close
  59. /// this appender before all the queued events have been sent.
  60. /// When closing the appender attempts to wait until all the queued events have been sent, but
  61. /// this will timeout after 30 seconds regardless.</para>
  62. /// <para>
  63. /// If this appender is being closed because the <see cref="AppDomain.ProcessExit"/>
  64. /// event has fired it may not be possible to send all the queued events. During process
  65. /// exit the runtime limits the time that a <see cref="AppDomain.ProcessExit"/>
  66. /// event handler is allowed to run for. If the runtime terminates the threads before
  67. /// the queued events have been sent then they will be lost. To ensure that all events
  68. /// are sent the appender must be closed before the application exits. See
  69. /// <see cref="log4net.Core.LoggerManager.Shutdown"/> for details on how to shutdown
  70. /// log4net programmatically.</para>
  71. /// </remarks>
  72. /// <seealso cref="IRemoteLoggingSink" />
  73. /// <author>Nicko Cadell</author>
  74. /// <author>Gert Driesen</author>
  75. /// <author>Daniel Cazzulino</author>
  76. public class RemotingAppender : BufferingAppenderSkeleton
  77. {
  78. #region Public Instance Constructors
  79. /// <summary>
  80. /// Initializes a new instance of the <see cref="RemotingAppender" /> class.
  81. /// </summary>
  82. /// <remarks>
  83. /// <para>
  84. /// Default constructor.
  85. /// </para>
  86. /// </remarks>
  87. public RemotingAppender()
  88. {
  89. }
  90. #endregion Public Instance Constructors
  91. #region Public Instance Properties
  92. /// <summary>
  93. /// Gets or sets the URL of the well-known object that will accept
  94. /// the logging events.
  95. /// </summary>
  96. /// <value>
  97. /// The well-known URL of the remote sink.
  98. /// </value>
  99. /// <remarks>
  100. /// <para>
  101. /// The URL of the remoting sink that will accept logging events.
  102. /// The sink must implement the <see cref="IRemoteLoggingSink"/>
  103. /// interface.
  104. /// </para>
  105. /// </remarks>
  106. public string Sink
  107. {
  108. get { return m_sinkUrl; }
  109. set { m_sinkUrl = value; }
  110. }
  111. #endregion Public Instance Properties
  112. #region Implementation of IOptionHandler
  113. /// <summary>
  114. /// Initialize the appender based on the options set
  115. /// </summary>
  116. /// <remarks>
  117. /// <para>
  118. /// This is part of the <see cref="IOptionHandler"/> delayed object
  119. /// activation scheme. The <see cref="ActivateOptions"/> method must
  120. /// be called on this object after the configuration properties have
  121. /// been set. Until <see cref="ActivateOptions"/> is called this
  122. /// object is in an undefined state and must not be used.
  123. /// </para>
  124. /// <para>
  125. /// If any of the configuration properties are modified then
  126. /// <see cref="ActivateOptions"/> must be called again.
  127. /// </para>
  128. /// </remarks>
  129. #if NET_4_0 || MONO_4_0
  130. [System.Security.SecuritySafeCritical]
  131. #endif
  132. override public void ActivateOptions()
  133. {
  134. base.ActivateOptions();
  135. IDictionary channelProperties = new Hashtable();
  136. channelProperties["typeFilterLevel"] = "Full";
  137. m_sinkObj = (IRemoteLoggingSink)Activator.GetObject(typeof(IRemoteLoggingSink), m_sinkUrl, channelProperties);
  138. }
  139. #endregion
  140. #region Override implementation of BufferingAppenderSkeleton
  141. /// <summary>
  142. /// Send the contents of the buffer to the remote sink.
  143. /// </summary>
  144. /// <remarks>
  145. /// The events are not sent immediately. They are scheduled to be sent
  146. /// using a pool thread. The effect is that the send occurs asynchronously.
  147. /// This is very important for a number of non obvious reasons. The remoting
  148. /// infrastructure will flow thread local variables (stored in the <see cref="CallContext"/>),
  149. /// if they are marked as <see cref="ILogicalThreadAffinative"/>, across the
  150. /// remoting boundary. If the server is not contactable then
  151. /// the remoting infrastructure will clear the <see cref="ILogicalThreadAffinative"/>
  152. /// objects from the <see cref="CallContext"/>. To prevent a logging failure from
  153. /// having side effects on the calling application the remoting call must be made
  154. /// from a separate thread to the one used by the application. A <see cref="ThreadPool"/>
  155. /// thread is used for this. If no <see cref="ThreadPool"/> thread is available then
  156. /// the events will block in the thread pool manager until a thread is available.
  157. /// </remarks>
  158. /// <param name="events">The events to send.</param>
  159. override protected void SendBuffer(LoggingEvent[] events)
  160. {
  161. // Setup for an async send
  162. BeginAsyncSend();
  163. // Send the events
  164. if (!ThreadPool.QueueUserWorkItem(new WaitCallback(SendBufferCallback), events))
  165. {
  166. // Cancel the async send
  167. EndAsyncSend();
  168. ErrorHandler.Error("RemotingAppender ["+Name+"] failed to ThreadPool.QueueUserWorkItem logging events in SendBuffer.");
  169. }
  170. }
  171. /// <summary>
  172. /// Override base class close.
  173. /// </summary>
  174. /// <remarks>
  175. /// <para>
  176. /// This method waits while there are queued work items. The events are
  177. /// sent asynchronously using <see cref="ThreadPool"/> work items. These items
  178. /// will be sent once a thread pool thread is available to send them, therefore
  179. /// it is possible to close the appender before all the queued events have been
  180. /// sent.</para>
  181. /// <para>
  182. /// This method attempts to wait until all the queued events have been sent, but this
  183. /// method will timeout after 30 seconds regardless.</para>
  184. /// <para>
  185. /// If the appender is being closed because the <see cref="AppDomain.ProcessExit"/>
  186. /// event has fired it may not be possible to send all the queued events. During process
  187. /// exit the runtime limits the time that a <see cref="AppDomain.ProcessExit"/>
  188. /// event handler is allowed to run for.</para>
  189. /// </remarks>
  190. override protected void OnClose()
  191. {
  192. base.OnClose();
  193. // Wait for the work queue to become empty before closing, timeout 30 seconds
  194. if (!m_workQueueEmptyEvent.WaitOne(30 * 1000, false))
  195. {
  196. ErrorHandler.Error("RemotingAppender ["+Name+"] failed to send all queued events before close, in OnClose.");
  197. }
  198. }
  199. /// <summary>
  200. /// Flushes any buffered log data.
  201. /// </summary>
  202. /// <param name="millisecondsTimeout">The maximum time to wait for logging events to be flushed.</param>
  203. /// <returns><c>True</c> if all logging events were flushed successfully, else <c>false</c>.</returns>
  204. public override bool Flush(int millisecondsTimeout)
  205. {
  206. base.Flush();
  207. return m_workQueueEmptyEvent.WaitOne(millisecondsTimeout, false);
  208. }
  209. #endregion
  210. /// <summary>
  211. /// A work item is being queued into the thread pool
  212. /// </summary>
  213. private void BeginAsyncSend()
  214. {
  215. // The work queue is not empty
  216. m_workQueueEmptyEvent.Reset();
  217. // Increment the queued count
  218. Interlocked.Increment(ref m_queuedCallbackCount);
  219. }
  220. /// <summary>
  221. /// A work item from the thread pool has completed
  222. /// </summary>
  223. private void EndAsyncSend()
  224. {
  225. // Decrement the queued count
  226. if (Interlocked.Decrement(ref m_queuedCallbackCount) <= 0)
  227. {
  228. // If the work queue is empty then set the event
  229. m_workQueueEmptyEvent.Set();
  230. }
  231. }
  232. /// <summary>
  233. /// Send the contents of the buffer to the remote sink.
  234. /// </summary>
  235. /// <remarks>
  236. /// This method is designed to be used with the <see cref="ThreadPool"/>.
  237. /// This method expects to be passed an array of <see cref="LoggingEvent"/>
  238. /// objects in the state param.
  239. /// </remarks>
  240. /// <param name="state">the logging events to send</param>
  241. private void SendBufferCallback(object state)
  242. {
  243. try
  244. {
  245. LoggingEvent[] events = (LoggingEvent[])state;
  246. // Send the events
  247. m_sinkObj.LogEvents(events);
  248. }
  249. catch(Exception ex)
  250. {
  251. ErrorHandler.Error("Failed in SendBufferCallback", ex);
  252. }
  253. finally
  254. {
  255. EndAsyncSend();
  256. }
  257. }
  258. #region Private Instance Fields
  259. /// <summary>
  260. /// The URL of the remote sink.
  261. /// </summary>
  262. private string m_sinkUrl;
  263. /// <summary>
  264. /// The local proxy (.NET remoting) for the remote logging sink.
  265. /// </summary>
  266. private IRemoteLoggingSink m_sinkObj;
  267. /// <summary>
  268. /// The number of queued callbacks currently waiting or executing
  269. /// </summary>
  270. private int m_queuedCallbackCount = 0;
  271. /// <summary>
  272. /// Event used to signal when there are no queued work items
  273. /// </summary>
  274. /// <remarks>
  275. /// This event is set when there are no queued work items. In this
  276. /// state it is safe to close the appender.
  277. /// </remarks>
  278. private ManualResetEvent m_workQueueEmptyEvent = new ManualResetEvent(true);
  279. #endregion Private Instance Fields
  280. /// <summary>
  281. /// Interface used to deliver <see cref="LoggingEvent"/> objects to a remote sink.
  282. /// </summary>
  283. /// <remarks>
  284. /// This interface must be implemented by a remoting sink
  285. /// if the <see cref="RemotingAppender"/> is to be used
  286. /// to deliver logging events to the sink.
  287. /// </remarks>
  288. public interface IRemoteLoggingSink
  289. {
  290. /// <summary>
  291. /// Delivers logging events to the remote sink
  292. /// </summary>
  293. /// <param name="events">Array of events to log.</param>
  294. /// <remarks>
  295. /// <para>
  296. /// Delivers logging events to the remote sink
  297. /// </para>
  298. /// </remarks>
  299. void LogEvents(LoggingEvent[] events);
  300. }
  301. }
  302. }
  303. #endif // !NETCF