65.9K
CodeProject 正在变化。 阅读更多。
Home

在您的应用程序中直接使用 PaperTrail 进行日志记录

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2014年11月18日

CPOL

4分钟阅读

viewsIcon

30594

downloadIcon

152

一个精简版系列文章。

(您多久能看到一个以字节为单位计量的下载?不是 KB,不是 MB,而是纯粹的“B”?“B”代表“极简”!)

引言

PaperTrail 是一个巧妙的 Web 应用程序,可以记录您发送给它的任何消息。我在这里介绍的是一个极简的 UDP 和 TCP 接口,用于在 PaperTrail 上记录消息。编写此代码的初衷是 PaperTrail 的理念侧重于能够连接到系统日志的客户端工具和/或使用第三方日志应用程序(例如 log4net)。对于 Windows,PaperTrail 建议使用“nxlog”。如果您不是开发人员和/或特别有兴趣使用 PaperTrail 远程查看您的系统事件,这一切都没问题。另外,如果您使用 log4net 等日志记录器,您可以使用 appender 来记录到 PaperTrail。然而,我的情况并非如此——我想学习如何在我的应用程序中直接使用 PaperTrail,并且我不想使用 log4net 这样的第三方组件。

我在这里介绍的实现大量借鉴了 Matthew Fittchett 的 PaperTrailTLS 代码。我的贡献是将其制作成一个真正小巧的可重用库,而不是演示代码,并解决 Matthew 示例中的一些性能问题。

创建 PaperTrail 帐户

创建 PaperTrail 帐户后...

如何向 PaperTrail 写入日志条目?

您将写入的 URL 和端口位于您的“帐户 -> 日志目标”下(我已从图片中删除了我的端口号)

或者,如果您转到“系统设置”页面,您会在页面顶部看到它以粗体大字显示

我的 API 令牌在哪里?

如果您有兴趣远程查询您的日志,您将需要一个 API 令牌。API 令牌位于“我 -> 个人资料”下(同样,我已从屏幕截图中删除了我的 API 令牌)

也许未来的文章将讨论如何使用 REST/JSON 查询 PaperTrail。

UDP 日志记录

UDP 日志记录限制为 1000 个字符条目,一旦您有了 URL 和端口号,它就非常直接。我们需要

using System.Net;
using System.Net.Sockets;

然后是实现,允许我们向日志目标的已知 IP 地址或日志目标的 URL 发送日志消息(返回的任何 IP 地址都可以使用)

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 = "<your log destination IP>";
static int PaperTrailPort = <your port number>;
static string PaperTrailLogUrl = "<your log destination URL";

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;
}

我们必须在 TCP 消息的末尾添加 CR-LF!如果您未能这样做,消息将不会出现在 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");

请注意,日期格式必须完全符合此格式。在 PaperTrail 端,结果是

请注意,PaperTrail 自动创建了系统“Marc”。我们可以使用这种更精细的消息粒度来进行警报和查询,我在这里没有涵盖。

客户支持

关于 PaperTrail 的客户支持,简单说一下——我前段时间加入了帮助聊天室,与 Leon 进行了一次非常有帮助的谈话,他为我指明了如何将我的日志“系统”命名为除我的路由器 IP 地址之外的其他名称!我对此印象深刻。

这里有一个 PHP 示例链接,说明了 BSD Syslog 协议。

结论

如果您正在寻找更集成的日志记录,那么请务必使用 nxlog 或 log4net appender。正如我提到的,这里的代码大量借鉴了 Matthew Fittchett 的 PaperTrailTLS 代码。我在这里增加的价值是删除了所有不必要的东西,这样您就可以在一个小的 C# 文件中获得核心功能,并将方法包装在一个持久化连接的类中,而不是为每条日志消息不断地打开、验证、关闭连接。

希望如果您一直在寻找如何在应用程序中直接实现 PaperTrail 日志记录,这篇短文对您有所帮助。

© . All rights reserved.