PaperTrail日志系统直接应用实现

PaperTrail是一个强大的网络应用,能够记录发送给它的任何消息。本文将介绍一个基础的UDP和TCP接口,用于在PaperTrail上记录日志消息。编写这个接口的动机是,PaperTrail的使用心态主要是面向客户端工具,这些工具可以连接到系统日志或者使用第三方日志应用程序(例如log4net)。对于Windows系统,PaperTrail建议使用"nxlog"。如果不是开发者,或者特别希望使用PaperTrail来远程查看系统事件,这是一个很好的选择。如果使用的是像log4net这样的日志记录器,可以使用一个附加器来记录到PaperTrail。然而,这不是情况——想直接在应用程序中使用PaperTrail,并且不想使用像log4net这样的第三方组件。

在这里介绍的实现在很大程度上借鉴了Matthew Fittchett的PaperTrailTLS代码。贡献是将其变成了一个可重用的小库,而不是演示代码,并解决了Matthew示例中的一些性能问题。

创建PaperTrail账户

创建PaperTrail账户后,将能够:

  • 在"账户 -> 日志目的地"下找到将要写入的URL和端口号(已经从图片中移除了端口号)。
  • 或者,如果去"系统设置"页面,会在页面顶部看到它以粗体大字显示。

如何将日志条目写入PaperTrail?

如果对远程查询日志感兴趣,将需要一个API令牌。API令牌可以在" -> 个人资料"下找到(同样,已经从截图中移除了API令牌)。

UDP日志记录

UDP日志记录限制为1000个字符的条目,一旦有了URL和端口号,就非常简单了。需要:

using System.Net; using System.Net.Sockets; private static void SendUdpMessage(IPAddress address, int port, string message) { Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint endPoint = new IPEndPoint(address, port); byte[] buffer = Encoding.ASCII.GetBytes(message); socket.SendTo(buffer, endPoint); socket.Close(); } private static void SendUdpMessage(string url, int port, string message) { IPAddress[] addr = Dns.GetHostAddresses(PaperTrailLogUrl); SendUdpMessage(addr[0], port, message); }

一个简单的测试程序:

static string PaperTrailIp = ""; static int PaperTrailPort = ; static string PaperTrailLogUrl = ""; static void Main(string[] args) { IPAddress addr = IPAddress.Parse(PaperTrailIp); SendUdpMessage(addr, PaperTrailPort, "Hello World via IP address (UDP)"); SendUdpMessage(PaperTrailLogUrl, PaperTrailPort, "Hello World via URL (UDP)"); }

TCP日志记录

TCP日志记录似乎不再被支持。至少,无法使其工作。

TLS日志记录

需要:

using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; private static void SendTlsMessage(string url, int port, string message) { const string CRLF = "\r\n"; TcpClient client = new TcpClient(url, port); SslStream sslStream = new SslStream(client.GetStream(), false, ValidateServerCertificate, null); sslStream.AuthenticateAsClient(url); byte[] buffer = Encoding.UTF8.GetBytes(message + CRLF); sslStream.Write(buffer); sslStream.Flush(); sslStream.Close(); client.Close(); } public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return sslPolicyErrors == SslPolicyErrors.None; }

必须以CR-LF结束TCP消息!如果没有这样做,消息将不会出现在PaperTrail上。会注意到不需要对UDP消息这样做。

打开连接是耗时的

真的不想为每条日志消息打开连接、验证证书(如果使用TLS)和关闭连接。这将使应用程序变得非常缓慢。因此,将构建一个类,持久化连接信息,并使用一点继承来分离UDP和TLS行为:

using System; using System.Net; using System.Net.Sockets; using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Text; namespace PaperTrailDemo { public abstract class PaperTrailLogger { protected IPAddress ip; protected int port; protected string url; public PaperTrailLogger(string url, int port) { IPAddress[] addr = Dns.GetHostAddresses(url); ip = addr[0]; this.url = url; this.port = port; } public abstract void Open(); public abstract void Close(); public abstract void Log(string message); } public class UdpPaperTrailLogger : PaperTrailLogger { protected Socket socket; protected IPEndPoint endPoint; public UdpPaperTrailLogger(string url, int port) : base(url, port) { } public override void Open() { socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); endPoint = new IPEndPoint(ip, port); } public override void Close() { socket.Close(); } public override void Log(string message) { byte[] buffer = Encoding.ASCII.GetBytes(message); socket.SendTo(buffer, endPoint); socket.Close(); } } public class TlsPaperTrailLogger : PaperTrailLogger { protected TcpClient client; protected SslStream sslStream; public TlsPaperTrailLogger(string url, int port) : base(url, port) { } public override void Open() { client = new TcpClient(url, port); sslStream = new SslStream(client.GetStream(), false, ValidateServerCertificate, null); sslStream.AuthenticateAsClient(url); } public override void Close() { sslStream.Close(); client.Close(); } public override void Log(string message) { const string CRLF = "\r\n"; byte[] buffer = Encoding.UTF8.GetBytes(message + CRLF); sslStream.Write(buffer); sslStream.Flush(); } private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return sslPolicyErrors == SslPolicyErrors.None; } } }

这让有了更清晰的用法:

UdpPaperTrailLogger logger = new UdpPaperTrailLogger(PaperTrailLogUrl, PaperTrailPort); logger.Open(); logger.Log("UDP - Hello!"); logger.Close(); TlsPaperTrailLogger logger2 = new TlsPaperTrailLogger(PaperTrailLogUrl, PaperTrailPort); logger2.Open(); logger2.Log("TLS - Hello!"); logger2.Close();

深入探索

日志消息符合RFC-5424 "Syslog协议"。如果不想阅读RFC,这里有关于该主题的维基百科页面。例如,可以通过发出这样的日志消息来编码到特定的优先级、系统("程序")和"组件":

logger.Log("<22>" + DateTime.Now.ToString("MMM d H:mm:ss") + "Marc Test: This is a test message");
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485