Click here to Skip to main content
14,973,243 members
Articles / Programming Languages / C#
Article
Posted 18 Nov 2014

Tagged as

Stats

22.7K views
116 downloads
9 bookmarked

Logging With PaperTrail Directly In Your Application

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
18 Nov 2014CPOL4 min read
A Bare-Bones Series Article

(How often do you see a download that is measured in bytes?  Not KB, not MB, but just "B"?  "B" is for "Bare Bones!")

Introduction

PaperTrail is a nifty web application that will log any messages that you send to it.  What I present here is a bare-bones UDP and TCP interface to logging messages on PaperTrail.  The impetus for writing this is that the mindset of PaperTrail is oriented around client-side tools that hook into system logs and/or use third party logging applications (for example, log4net.)  For Windows, PaperTrail suggests using "nxlog."  This is all fine and dandy if you're not a developer and/or are interested specifically in using PaperTrail to remotely view your system events. Also, if you're using a logger like log4net, you can use an appender to log to PaperTrail.  However, this was not my case -- I wanted to learn how to use PaperTrail directly in my application and I didn't want to use a third party component like log4net.

The implementation I present here is heavily borrowed from Matthew Fittchett's PaperTrailTLS code.  What I've contributed is making this an actual small re-usable library rather than demo code and addressing some performance issues with the Matthew's examples.

Creating a PaperTrail Account

After you've created a PaperTrail account...

How do I Write a Log Entry to PaperTrail?

The URL and port that you will be writing to is located under your Account -> Log Destinations (I've removed my port # from the image):

Image 1

Or, if you go to the System Setup page, you'll see it in big bold print at the top of the page:

Image 2

Where's my API Token?

If you're interested in querying your logs remotely, you will need and API token.  The API token is under Me -> Profile (again I've removed my API token from the screenshot):

Image 3

Perhaps a future article will discuss querying PaperTrail with REST/JSON.

UDP Logging

UDP Logging is limited to 1000 character entries and, once you have the URL a port number, is very straight forward.  We need:

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

and then the implementation, allowing us to send a log message to either the known IP address of the log destination or the log destination's URL (any of the returned IP addresses will work):

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

A simple test program:

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

Et voila:

Image 4

TCP Logging

TCP logging does not seem to be supported anymore.  At least, I couldn't get it to work.

TLS Logging

We need:

using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

and then the implementation:

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

Image 5 We must end our TCP message with a CR-LF!  If you fail to do this, the message will not appear on PaperTrail.  You will note that we did not need to do this with UDP messages.

Opening Connections is Time Consuming

We really don't want to be opening a connection, validating the certificate (if we're using TLS) and closing the connection for every log message.  This will bring your application to a crawl.  So instead, we'll put together a class that persists the connection information and use a wee bit of inheritance to separate out the UDP and TLS behavior:

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

This leaves us with a cleaner usage:

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();

Et voila (again):

Image 6

Image 7  Exceptions should be handled by the caller.

Diving Deeper

The log message complies with RFC-5424 "The Syslog Protocol".  If you don't want to read the RFC, here's a wikipedia page on the the topic.

 For example, we can code to a specific priority, system (the "program") and "component" by emitting a log message like this:

logger.Log("<22>" + DateTime.Now.ToString("MMM d H:mm:ss") + " Marc Test: This is a test message");

Note that the date format must be exactly in this format.  This result, on PaperTrail's end, is:

Image 8

Note that PaperTrail automatically created the system "Marc".  We can use this finer granularity of messaging in alerts and queries, which I'm not covering here.

Customer Support

A quick word about PaperTrail's customer support - I hopped onto the help chat room a little while ago and had a very helpful conversation with Leon, who pointed me in the right direction for how to name my logging "system" with something other than my router's IP address!  I was duly impressed.

Here's a link to a PHP examplee, illustrating the BSD Syslog Protocol.

Conclusion

If you're looking for more integrated logging, then by all means use nxlog or the log4net appender.  As I mentioned, the code here is heavily borrowed from  Matthew Fittchett's PaperTrailTLS code.  The value that I've added here is to remove all the unnecessary stuff so that you have the core functionality in a single small C# file and to wrap the methods into a class that persists the connection rather than constantly opening, validation, closing the connection for each log message.

Hopefully, if you've been looking for how to implement PaperTrail logging directly in your app, this short article will have been of use.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Marc Clifton
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Thomas Maierhofer (Tom)24-Nov-14 9:08
MemberThomas Maierhofer (Tom)24-Nov-14 9:08 
Questiontest Pin
Karthik Keyan19-Nov-14 4:34
MemberKarthik Keyan19-Nov-14 4:34 
AnswerRe: test Pin
Karthik Keyan19-Nov-14 4:34
MemberKarthik Keyan19-Nov-14 4:34 
GeneralRe: test Pin
Marc Clifton19-Nov-14 4:36
mvaMarc Clifton19-Nov-14 4:36 
GeneralNice work Marc Pin
Garth J Lancaster18-Nov-14 16:35
mveGarth J Lancaster18-Nov-14 16:35 
GeneralRe: Nice work Marc Pin
Marc Clifton19-Nov-14 1:10
mvaMarc Clifton19-Nov-14 1:10 
GeneralRe: Nice work Marc Pin
Garth J Lancaster19-Nov-14 11:58
mveGarth J Lancaster19-Nov-14 11:58 
GeneralRe: Nice work Marc Pin
Marc Clifton19-Nov-14 12:01
mvaMarc Clifton19-Nov-14 12:01 
GeneralRe: Nice work Marc Pin
Garth J Lancaster20-Nov-14 15:56
mveGarth J Lancaster20-Nov-14 15:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.