PaperTrail是一个强大的网络应用,能够记录发送给它的任何消息。本文将介绍一个基础的UDP和TCP接口,用于在PaperTrail上记录日志消息。编写这个接口的动机是,PaperTrail的使用心态主要是面向客户端工具,这些工具可以连接到系统日志或者使用第三方日志应用程序(例如log4net)。对于Windows系统,PaperTrail建议使用"nxlog"。如果不是开发者,或者特别希望使用PaperTrail来远程查看系统事件,这是一个很好的选择。如果使用的是像log4net这样的日志记录器,可以使用一个附加器来记录到PaperTrail。然而,这不是情况——想直接在应用程序中使用PaperTrail,并且不想使用像log4net这样的第三方组件。
在这里介绍的实现在很大程度上借鉴了Matthew Fittchett的PaperTrailTLS代码。贡献是将其变成了一个可重用的小库,而不是演示代码,并解决了Matthew示例中的一些性能问题。
创建PaperTrail账户后,将能够:
如果对远程查询日志感兴趣,将需要一个API令牌。API令牌可以在" -> 个人资料"下找到(同样,已经从截图中移除了API令牌)。
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日志记录似乎不再被支持。至少,无法使其工作。
需要:
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");