using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using log4net.Appender;
using log4net.Core;
namespace TwitterAppender
{
public class TwitterAppender : AppenderSkeleton
{
#region Private Members
private const string REQUEST_CONTENT_TYPE = "application/x-www-form-urlencoded";
private const string REQUEST_METHOD = "POST";
// The source attribute has been removed from the Twitter API,
// unless you're using OAuth.
// Even if you are using OAuth, there's still an approval process.
// Not worth it; "API" will work for now!
// private const string TWITTER_SOURCE_NAME = "Log4Net";
private const string TWITTER_UPDATE_URL_FORMAT = "http://twitter.com/statuses/update.xml?status={0}";
private string _twitterUserName;
private string _twitterPassword;
///
/// This is where we're posting the actual event as a tweet.
/// I was originally thinking of using REST-ful WCF, but, to keep things simple
/// and to reduce dependencies on other technologies, I decided to just use a traditional web request.
///
/// <param name="eventToPost_" />The event to post.
private void PostLoggingEvent(LoggingEvent eventToPost_)
{
// The base RenderLoggingEvent method converts the event to a string
// based on the layout specified in the configuration file.
// It's important to encode the tweet so that the request URL remains valid.
var updateRequest = HttpWebRequest.Create(String.Format(TWITTER_UPDATE_URL_FORMAT,
HttpUtility.UrlEncode(RenderLoggingEvent(eventToPost_).ToTweet()))) as HttpWebRequest;
updateRequest.ContentLength = 0;
updateRequest.ContentType = REQUEST_CONTENT_TYPE;
updateRequest.Credentials = new NetworkCredential(TwitterUserName, TwitterPassword);
updateRequest.Method = REQUEST_METHOD;
// This is in idiosyncracy with the Twitter API.
updateRequest.ServicePoint.Expect100Continue = true;
var updateResponse = updateRequest.GetResponse() as HttpWebResponse;
if (updateResponse.StatusCode != HttpStatusCode.OK && updateResponse.StatusCode != HttpStatusCode.Continue)
throw new ApplicationException(String.Format("An error occured while invoking the Twitter REST API [Response Code: {0}]; unable to proceed.", updateResponse.StatusCode));
}
#endregion
#region Public Members
///
/// This is the password for the Twitter account that is to be used.
/// Fortunately, the Log4Net framework automatically populates
/// this property for us based on the configuration.
///
public string TwitterPassword
{
get
{
// We need a password.
if (String.IsNullOrEmpty(_twitterPassword))
throw new ApplicationException("Twitter account password not specified; unable to proceed.");
return _twitterPassword;
}
set
{
_twitterPassword = value;
}
}
///
/// This is the user name for the Twitter account that is to be used.
/// Fortunately, the Log4Net framework automatically populates
/// this property for us based on the configuration.
///
public string TwitterUserName
{
get
{
// We need a user name.
if (String.IsNullOrEmpty(_twitterUserName))
throw new ApplicationException("Twitter account user name not specified; unable to proceed.");
return _twitterUserName;
}
set
{
_twitterUserName = value;
}
}
#endregion
#region Protected Members
///
/// This is the main entry point for the Log4Net framework.
///
/// <param name="loggingEvent_" />The event to append.
protected override void Append(LoggingEvent loggingEvent_)
{
try
{
if (loggingEvent_ == null)
throw new ArgumentNullException("loggingEvent_");
PostLoggingEvent(loggingEvent_);
}
catch (Exception ex_)
{
// Since we're already inside the event logging framework,
// let Log4Net handle this one.
// We don't want to get stuck in a loop!
ErrorHandler.Error("An error occured while posting the provided event; unable to proceed.", ex_);
}
}
#endregion
}
}