using System.Globalization;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
public class ReconnectingTcpAppender : AppenderSkeleton
#region Public Instance Constructors
public ReconnectingTcpAppender()
_remotePort = IPEndPoint.MinPort - 1;
private class AsyncLoggingData
internal TcpClient Client { get; set; }
internal LoggingEvent LoggingEvent { get; set; }
#endregion Public Instance Constructors
#region Public Instance Properties
public string RemoteAddress
get { return _remoteAddress; }
set { _remoteAddress = value; }
TcpClient Client { get; set; }
get { return _remotePort; }
if (value < IPEndPoint.MinPort || value > IPEndPoint.MaxPort)
throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("value", value,
"The value specified is less than " +
IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
get { return _encoding; }
set { _encoding = value; }
readonly ConcurrentQueue<LoggingEvent> mEventBuffer = new ConcurrentQueue<LoggingEvent>();
#endregion Public Instance Properties
#region Implementation of IOptionHandler
public override void ActivateOptions()
if (string.IsNullOrWhiteSpace(RemoteAddress))
throw new ArgumentNullException("RemoteAddress");
if (RemotePort < IPEndPoint.MinPort || RemotePort > IPEndPoint.MaxPort)
throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("RemotePort", RemotePort,
"The RemotePort is less than " +
IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
#region Override implementation of AppenderSkeleton
protected override void Append(LoggingEvent loggingEvent)
if (null != Client && Client.Connected)
WriteEvent(loggingEvent);
Reconnect(loggingEvent, 0);
protected override void Append(LoggingEvent[] loggingEvents)
Array.ForEach(loggingEvents, le => WriteEvent(le));
Array.ForEach(loggingEvents, le => mEventBuffer.Enqueue(le));
[DllImport("dnsapi.dll",EntryPoint="DnsFlushResolverCache")]
private static extern UInt32 DnsFlushResolverCache ();
Task<IPAddress[]> GetAddress()
var currDnsTimeout = ServicePointManager.DnsRefreshTimeout;
ServicePointManager.DnsRefreshTimeout = 0;
return Task.Factory.FromAsync<IPHostEntry>(Dns.BeginGetHostEntry(RemoteAddress, null, null)
ServicePointManager.DnsRefreshTimeout = currDnsTimeout;
ErrorHandler.Error(string.Format("Failed to resolve remote host {0} in dns", RemoteAddress)
return t.Result.AddressList;
ServicePointManager.DnsRefreshTimeout = currDnsTimeout;
return Task.Factory.StartNew(() => new IPAddress[0]);
Task ConnectTask { get; set; }
void Reconnect(LoggingEvent loggingEvent, int waitMilliSecs = 3000)
if (null != loggingEvent)
mEventBuffer.Enqueue(loggingEvent);
if (null != ConnectTask && !ConnectTask.IsCompleted)
var ct = new System.Threading.CancellationToken();
ConnectTask = TaskEx.Delay(waitMilliSecs, ct)
var c = ct.CanBeCanceled;
void Connect(int lastWait=0)
ConnectTask = GetAddress()
var ipAddresses = t.Result;
if (null == ipAddresses || !ipAddresses.Any())
var client = new TcpClient();
var ipAddress = ipAddresses.First(a => a.AddressFamily == AddressFamily.InterNetwork);
ConnectTask = Task.Factory.FromAsync(client.BeginConnect(ipAddress,
ErrorHandler.Error(string.Format("Failed to connect to {0}", RemoteAddress)
while (mEventBuffer.TryDequeue(out e))
void WriteEvent(LoggingEvent loggingEvent)
var rendered = RenderLoggingEvent(loggingEvent);
var ca = rendered.ToCharArray();
var buffer = Encoding.GetBytes(ca);
if (null == client || !client.Connected)
var netStream = client.GetStream();
var writeTask = Task.Factory.FromAsync(netStream.BeginWrite(buffer, 0, buffer.Length, null, Client)
writeTask.ContinueWith(tf =>
"Unable to send logging event to remote host " + RemoteAddress.ToString() + " on port " +
RemotePort + ".", tf.Exception, ErrorCode.WriteFailure);
}, TaskContinuationOptions.NotOnRanToCompletion);
catch (ObjectDisposedException e)
string.Format("Unable to send logging event to remote host {0} on port {1}", RemoteAddress, RemotePort)
, ErrorCode.WriteFailure);
catch (System.IO.IOException e)
string.Format("Unable to send logging event to remote host {0} on port {1}", RemoteAddress, RemotePort)
, ErrorCode.WriteFailure);
override protected bool RequiresLayout
override protected void OnClose()
#endregion Override implementation of AppenderSkeleton
#region Private Instance Fields
private string _remoteAddress;
private Encoding _encoding = Encoding.Default;
#endregion Private Instance Fields