ELK集成Log4net 重写一个TcpAppender
最近搞了下ELK,三个工具部署完毕,想再集成上log4net。没想到.net core版Log4net竟然没有直接Tcp发送消息的appender。醉了。log4net
1.RemotingAppender:core已经不支持。
2.RemoteSysLogAppender:只监听 udp的514端口。【原文:The BSD syslog protocol is used to remotely log to a syslog daemon. The syslogd listens for for messages on UDP port 514.】
3.UdpAppender:udp协议传输数据
4.TelnetAppender:一开始以为肯定是这个可以用了。官网没有examples。。科学上网翻了config。里面竟然只有port配置没有remotingAddress等(黑人问号)???。官网解释这个东西怎么玩的【原文:The TelnetAppender accepts socket connections and streams logging messages back to the client. The output is provided in a telnet-friendly way so that a log can be monitored over a TCP/IP socket. This allows simple remote monitoring of application logging.】
也就是它是开了一个监听。监听别人给他发的日志数据,然后记录数据然后再发回去。。。What???。我要的是你发出去。混蛋。下面去反编译看看他的源码。。
namespace log4net.Appender { public class TelnetAppender : AppenderSkeleton { private static readonly Type declaringType = typeof (TelnetAppender); private int m_listeningPort = 23; private TelnetAppender.SocketHandler m_handler; public int Port { get { return this.m_listeningPort; } set { if (value < 0 || value > (int) ushort.MaxValue) throw SystemInfo.CreateArgumentOutOfRangeException("value", (object) value, "The value specified for Port is less than " + 0.ToString((IFormatProvider) NumberFormatInfo.InvariantInfo) + " or greater than " + ((int) ushort.MaxValue).ToString((IFormatProvider) NumberFormatInfo.InvariantInfo) + "."); this.m_listeningPort = value; } } protected override bool RequiresLayout { get { return true; } } protected override void OnClose() { base.OnClose(); if (this.m_handler == null) return; this.m_handler.Dispose(); this.m_handler = (TelnetAppender.SocketHandler) null; } public override void ActivateOptions() { base.ActivateOptions(); try { LogLog.Debug(TelnetAppender.declaringType, "Creating SocketHandler to listen on port [" + (object) this.m_listeningPort + "]"); this.m_handler = new TelnetAppender.SocketHandler(this.m_listeningPort); } catch (Exception ex) { LogLog.Error(TelnetAppender.declaringType, "Failed to create SocketHandler", ex); throw; } } protected override void Append(LoggingEvent loggingEvent) { if (this.m_handler == null || !this.m_handler.HasConnections) return; this.m_handler.Send(this.RenderLoggingEvent(loggingEvent)); } protected class SocketHandler : IDisposable { private ArrayList m_clients = new ArrayList(); private const int MAX_CONNECTIONS = 20; private Socket m_serverSocket; public bool HasConnections { get { ArrayList clients = this.m_clients; if (clients != null) return clients.Count > 0; return false; } } public SocketHandler(int port) { this.m_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); this.m_serverSocket.Bind((EndPoint) new IPEndPoint(IPAddress.Any, port)); this.m_serverSocket.Listen(5); this.AcceptConnection(); } private void AcceptConnection() { this.m_serverSocket.AcceptAsync().ContinueWith(new Action<Task<Socket>>(this.OnConnect), TaskScheduler.Default); } public void Send(string message) { foreach (TelnetAppender.SocketHandler.SocketClient client in this.m_clients) { try { client.Send(message); } catch (Exception ex) { client.Dispose(); this.RemoveClient(client); } } } private void AddClient(TelnetAppender.SocketHandler.SocketClient client) { TelnetAppender.SocketHandler socketHandler = this; bool lockTaken = false; try { Monitor.Enter((object) socketHandler, ref lockTaken); ArrayList arrayList = (ArrayList) this.m_clients.Clone(); arrayList.Add((object) client); this.m_clients = arrayList; } finally { if (lockTaken) Monitor.Exit((object) socketHandler); } } private void RemoveClient(TelnetAppender.SocketHandler.SocketClient client) { TelnetAppender.SocketHandler socketHandler = this; bool lockTaken = false; try { Monitor.Enter((object) socketHandler, ref lockTaken); ArrayList arrayList = (ArrayList) this.m_clients.Clone(); arrayList.Remove((object) client); this.m_clients = arrayList; } finally { if (lockTaken) Monitor.Exit((object) socketHandler); } } private void OnConnect(Task<Socket> acceptTask) { try { Socket result = acceptTask.GetAwaiter().GetResult(); LogLog.Debug(TelnetAppender.declaringType, "Accepting connection from [" + result.RemoteEndPoint.ToString() + "]"); TelnetAppender.SocketHandler.SocketClient client = new TelnetAppender.SocketHandler.SocketClient(result); int count = this.m_clients.Count; if (count < 20) { try { client.Send("TelnetAppender v1.0 (" + (object) (count + 1) + " active connections)\r\n\r\n"); this.AddClient(client); } catch { client.Dispose(); } } else { client.Send("Sorry - Too many connections.\r\n"); client.Dispose(); } } catch { } finally { if (this.m_serverSocket != null) this.AcceptConnection(); } } public void Dispose() { foreach (TelnetAppender.SocketHandler.SocketClient client in this.m_clients) client.Dispose(); this.m_clients.Clear(); Socket serverSocket = this.m_serverSocket; this.m_serverSocket = (Socket) null; try { serverSocket.Shutdown(SocketShutdown.Both); } catch { } try { CompatibilityExtensions.Close(serverSocket); } catch { } } protected class SocketClient : IDisposable { private Socket m_socket; private StreamWriter m_writer; public SocketClient(Socket socket) { this.m_socket = socket; try { this.m_writer = new StreamWriter((Stream) new NetworkStream(socket)); } catch { this.Dispose(); throw; } } public void Send(string message) { this.m_writer.Write(message); this.m_writer.Flush(); } public void Dispose() { try { if (this.m_writer != null) { CompatibilityExtensions.Close(this.m_writer); this.m_writer = (StreamWriter) null; } } catch { } if (this.m_socket == null) return; try { this.m_socket.Shutdown(SocketShutdown.Both); } catch { } try { CompatibilityExtensions.Close(this.m_socket); } catch { } this.m_socket = (Socket) null; } } } } }
那就没办法。。我自己再重写一个直接发送的TcpAppender吧。。写法是完全看udpAppender源码改的。暴露出来的配置和udp基本一致。把local的去除了
internal class TcpAppender : AppenderSkeleton { private Encoding _encoding = Encoding.Unicode; private IPAddress _remoteAddress; private int _remotePort; private IPEndPoint _remoteEndPoint; private TcpClient _client; public IPAddress RemoteAddress { get { return this._remoteAddress; } set { this._remoteAddress = value; } } public int RemotePort { get { return this._remotePort; } set { if (value < 0 || value > (int)ushort.MaxValue) throw SystemInfo.CreateArgumentOutOfRangeException("value", (object)value, "The value specified is less than " + 0.ToString((IFormatProvider)NumberFormatInfo.InvariantInfo) + " or greater than " + ((int)ushort.MaxValue).ToString((IFormatProvider)NumberFormatInfo.InvariantInfo) + "."); this._remotePort = value; } } public Encoding Encoding { get { return this._encoding; } set { this._encoding = value; } } protected TcpClient Client { get { return this._client; } set { this._client = value; } } protected IPEndPoint RemoteEndPoint { get { return this._remoteEndPoint; } set { this._remoteEndPoint = value; } } protected override bool RequiresLayout { get { return true; } } public override void ActivateOptions() { base.ActivateOptions(); if (this.RemoteAddress == null) throw new ArgumentNullException("The required property 'Address' was not specified."); if (this.RemotePort < 0 || this.RemotePort > (int)ushort.MaxValue) throw SystemInfo.CreateArgumentOutOfRangeException("this.RemotePort", (object)this.RemotePort, "The RemotePort is less than " + 0.ToString((IFormatProvider)NumberFormatInfo.InvariantInfo) + " or greater than " + ((int)ushort.MaxValue).ToString((IFormatProvider)NumberFormatInfo.InvariantInfo) + "."); this.RemoteEndPoint = new IPEndPoint(this.RemoteAddress, this.RemotePort); this.InitializeClientConnection(); } protected override void Append(LoggingEvent loggingEvent) { try { NetworkStream ntwStream = this.Client.GetStream(); var aa = this.RenderLoggingEvent(loggingEvent).ToCharArray(); byte[] bytes = this._encoding.GetBytes(this.RenderLoggingEvent(loggingEvent).ToCharArray()); ntwStream.Write(bytes, 0, bytes.Length); } catch (Exception ex) { this.ErrorHandler.Error("Unable to send logging event to remote host " + this.RemoteAddress.ToString() + " on port " + (object)this.RemotePort + ".", ex, ErrorCode.WriteFailure); } } protected override void OnClose() { base.OnClose(); if (this.Client == null) return; Client.Dispose(); this.Client = null; } protected virtual void InitializeClientConnection() { try { this.Client = new TcpClient(this.RemoteAddress.ToString(), this.RemotePort); } catch (Exception ex) { this.ErrorHandler.Error("Could not initialize the UdpClient connection on port " + this.RemotePort.ToString((IFormatProvider)NumberFormatInfo.InvariantInfo) + ".", ex, ErrorCode.GenericFailure); this.Client = null; } } }
用法代码。里面额外加入了一个consoleAppender 方便查看调试
1 static void Main(string[] args) 2 { 3 Test(); 4 Console.ReadKey(); 5 } 6 7 private static void Test() 8 { 9 var repo = LogManager.CreateRepository("R"); 10 11 var layout = new PatternLayout() 12 { 13 ConversionPattern = "#%date #%level #%message#", 14 }; 15 var tcpAppender = new TcpAppender 16 { 17 Encoding = Encoding.UTF8, 18 RemoteAddress = new IPAddress(new byte[] { 192,168,132,128 }), 19 RemotePort = 8001, 20 Layout = layout 21 }; 22 var consoleAppender = new ConsoleAppender 23 { 24 Layout = layout 25 }; 26 layout.ActivateOptions(); 27 tcpAppender.ActivateOptions(); 28 BasicConfigurator.Configure(repo, consoleAppender,tcpAppender); 29 30 ILog log = LogManager.GetLogger(repo.Name, "Debug"); 31 log.Info("666666666666666666666"); 32 Console.ReadKey(); 33 }
下面是成功发送到虚拟机上的kibana的后台显示。部分数据和字段没匹配好是logstash的规则和log4net规则没对应好