If you're interested, contact me through the Logverse site for more details.
Showing posts with label Logging. Show all posts
Showing posts with label Logging. Show all posts
Friday, November 5, 2010
Logverse Available for Purchase!
Because some people have expressed interest in purchasing the source code for Logverse rather than paying a monthly subscription fee, I've decided to add that option.
Friday, September 3, 2010
Tweeting Logger (Twitter Logger Updated -- Using OAuth)
Twitter now requires OAuth in order to use their API. I thought it might be nice to provide a new Logger that tweets using Twitter's OAuth.
Unfortunately, compared to the original TwitterLogger, the code has ballooned quite a bit. However, if you want to use one of the available Twitter API libraries, then most of the code here would go away and you'd be back to a tiny Logger subclass that Tweets.
But if you want to avoid adding yet another DLL to your application and you have limited needs to integrate with Twitter--such as just tweeting, then this should get you started.
Check out the Twitter help for obtaining a Consumer Key, a Consumer Secret, an Access Token, and an Access Token Secret.
Notice that there is only one method that is overridden. The rest of the code is there to support OAuth. Remember, this is intended to be neither a complete OAuth implementation, nor a complete Twitter API integration. It's just a simple TweetingLogger. And frankly, it could be improved quite a bit. (e.g. Separating out the tweeting specifics into another class, parameterizing the constants, etc.) But it's good enough to get you started.
Unfortunately, compared to the original TwitterLogger, the code has ballooned quite a bit. However, if you want to use one of the available Twitter API libraries, then most of the code here would go away and you'd be back to a tiny Logger subclass that Tweets.
But if you want to avoid adding yet another DLL to your application and you have limited needs to integrate with Twitter--such as just tweeting, then this should get you started.
Check out the Twitter help for obtaining a Consumer Key, a Consumer Secret, an Access Token, and an Access Token Secret.
Notice that there is only one method that is overridden. The rest of the code is there to support OAuth. Remember, this is intended to be neither a complete OAuth implementation, nor a complete Twitter API integration. It's just a simple TweetingLogger. And frankly, it could be improved quite a bit. (e.g. Separating out the tweeting specifics into another class, parameterizing the constants, etc.) But it's good enough to get you started.
public class TweetingLogger : Logger
{
const string TweetUrl = "http://api.twitter.com/1/statuses/update.json";
const string ConsumerKey = "[Your Consumer Key]";
const string ConsumerSecret = "[Your Consumer Secret]";
const string AccessToken = "[Your Access Token]";
const string AccessTokenSecret = "[Your Access Token Secret]";
protected string GetOAuthUrlEncode(string aValue)
{
// Thanks to Stephen Denton for this url encode
var newValue = System.Web.HttpUtility.UrlEncode(aValue).Replace("+", "%20");
// UrlEncode escapes with lowercase characters (e.g. %2f) but oAuth needs %2F
newValue = System.Text.RegularExpressions.Regex.Replace(newValue, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
// these characters are not escaped by UrlEncode() but needed to be escaped
newValue = newValue.Replace("(", "%28").Replace(")", "%29").Replace("$", "%24").Replace("!", "%21").Replace("*", "%2A").Replace("'", "%27");
// these characters are escaped by UrlEncode() but will fail if unescaped!
newValue = newValue.Replace("%7E", "~");
return newValue;
}
protected string GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
protected string GetNonce()
{
return Guid.NewGuid().ToString();
}
protected string GetOAuthBaseString(string anHttpMethod, string aBaseUri, SortedDictionary<string, string> someParameters)
{
var paramStrings = new List<String>();
foreach (string key in someParameters.Keys)
paramStrings.Add(key + "=" + someParameters[key]);
return anHttpMethod.ToUpper() + "&" + GetOAuthUrlEncode(aBaseUri) + "&" + GetOAuthUrlEncode(String.Join("&", paramStrings.ToArray()));
}
protected string GetAuthSignature(SortedDictionary<string,string> someParameters)
{
var hmacsha1 = new System.Security.Cryptography.HMACSHA1(Encoding.ASCII.GetBytes(ConsumerSecret + "&" + AccessTokenSecret));
var bytes = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(GetOAuthBaseString("POST", TweetUrl, someParameters)));
return Convert.ToBase64String(bytes);
}
protected SortedDictionary<string, string> GetOAuthParameters()
{
return new SortedDictionary<string,string>()
{
{ "oauth_nonce", GetNonce() },
{ "oauth_consumer_key", ConsumerKey },
{ "oauth_signature_method", "HMAC-SHA1" },
{ "oauth_timestamp", GetTimeStamp() },
{ "oauth_token", AccessToken },
{ "oauth_version", "1.0" },
//{ "oauth_signature", GetAuthSignature() }, // add later in process
};
}
protected string GetOAuthString(SortedDictionary<string, string> baseParameters, SortedDictionary<string, string> additionalParameters)
{
var sb = new StringBuilder();
sb.Append("OAuth ");
var paramList = new List<string>();
foreach (string key in baseParameters.Keys)
paramList.Add(key + "=" + "\"" + baseParameters[key] + "\"");
sb.Append(string.Join(",", paramList.ToArray()));
var allParameters = new SortedDictionary<string, string>(baseParameters);
foreach (KeyValuePair<string, string> kv in additionalParameters)
allParameters.Add(kv.Key, kv.Value);
sb.Append(",oauth_signature=\"" + GetOAuthUrlEncode(GetAuthSignature(allParameters)) + "\"");
return sb.ToString();
}
protected override bool DoLog(LogEntry aLogEntry)
{
// without this, you may receive a '417 Expectation Failed' error
ServicePointManager.Expect100Continue = false;
using (WebClient wClient = new WebClient())
{
var parameters = GetOAuthParameters();
var additionalParameters = new SortedDictionary<string, string>() { { "status", GetOAuthUrlEncode(aLogEntry.Message) } };
var theString = GetOAuthString(parameters, additionalParameters);
wClient.Headers.Add("Authorization", theString);
var nvc = new NameValueCollection();
nvc["status"] = aLogEntry.Message;
wClient.UploadValues(TweetUrl, nvc);
}
return true;
}
}
Wednesday, March 24, 2010
Logging to Logverse
If you haven't heard of Logverse, it's a service that allows applications to log to it. And just about anything can be logged to it--errors, user actions, debug information... whatever you want.
It's perfect for applications out in the field, like desktop and mobile applications. But even web or other server applications can take advantage of it.
You don't need to use a logging framework to use Logverse. But if you use The Object Guy's Logging Framework, it is extremely easy to create a logger that logs to Logverse.
I have two very small classes here. The first is just a utility class that will write to Logverse.
The second class I have is the LogverseLogger.
The Logverse logger can't currently be configured using the web.config (or app.config), so at the beginning of the application I call the Create method. Here's an example for a web application. This code would go into global.asax.cs.
WOW!
Now that was easy. And now I can log to Logverse. Pretty cool, huh?
One might ask, "What about exception handling?" Good question. Actually, it isn't required in the Logger, because that's handled automatically by the framework. (Cool!) However, if you were to use the LogverseWriter class independently, then you'd probably would want to put some exception handling in it. The only reason I didn't was so that I'd have an excuse to talk about it in this paragraph. :)
It's perfect for applications out in the field, like desktop and mobile applications. But even web or other server applications can take advantage of it.
You don't need to use a logging framework to use Logverse. But if you use The Object Guy's Logging Framework, it is extremely easy to create a logger that logs to Logverse.
I have two very small classes here. The first is just a utility class that will write to Logverse.
public class LogverseWriter
{
private readonly Guid LogId;
private readonly string SubscriptionKey;
public LogverseWriter(string aLogId, string aSubscriptionKey)
{
LogId = new Guid(aLogId);
SubscriptionKey = aSubscriptionKey;
}
public bool Write(Dictionary<string, string> values)
{
var logEntryInfo = new Logverse.LogEntryInfo()
{
LogId = LogId,
SubscriptionKey = SubscriptionKey,
ClientTimeStampUtc = DateTime.UtcNow
};
logEntryInfo.Fields = values;
var service = new Logverse.LogServiceClient();
var result = service.Log(logEntryInfo);
service.Close();
return result.Result == Logverse.LogResult.EResult.Logged;
}
}
The second class I have is the LogverseLogger.
public class LogverseLogger : Logger
{
private readonly LogverseWriter LogverseWriter;
public LogverseLogger(string aLogId, string aSubscriptionKey)
{
LogverseWriter = new LogverseWriter(aLogId, aSubscriptionKey);
}
protected override bool DoLog(LogEntry aLogEntry)
{
return LogverseWriter.Write(
new Dictionary<string, string>()
{
{ "Category", aLogEntry.Category != null ? aLogEntry.Category.ToString() : "" },
{ "Severity", aLogEntry.SeverityString },
{ "Message", aLogEntry.Message }
});
}
public static void Create(string aLogId, string aSubscriptionKey)
{
ConfigLogger.Instance.AddLogger("logverse_" + aLogId, new LogverseLogger(aLogId, aSubscriptionKey));
}
}
The Logverse logger can't currently be configured using the web.config (or app.config), so at the beginning of the application I call the Create method. Here's an example for a web application. This code would go into global.asax.cs.
protected void Application_Start(object sender, EventArgs e)
{
LogverseLogger.Create("<mylogid>", "<mysubscriptionkey>");
}
WOW!
Now that was easy. And now I can log to Logverse. Pretty cool, huh?
One might ask, "What about exception handling?" Good question. Actually, it isn't required in the Logger, because that's handled automatically by the framework. (Cool!) However, if you were to use the LogverseWriter class independently, then you'd probably would want to put some exception handling in it. The only reason I didn't was so that I'd have an excuse to talk about it in this paragraph. :)
Sunday, March 14, 2010
Friday, January 1, 2010
Multi-threaded Logging
In light of my previous post, I thought it would be a good idea for me to demonstrate logging in a multi-threaded application using my framework.
Watch this very short video. It's just a little over 3 minutes long.
Watch this very short video. It's just a little over 3 minutes long.
Labels:
Logging
Thursday, December 31, 2009
Scary Framework
I don't really want to bag on another logging framework, but I've come across many posts that are downright scary. Here's an example of a situation with one of the most popular logging frameworks. Read down a bit and you'll hear about deadlocks that happened every day--until they removed that logging framework.
I've also read about other serious deadlock issues with that framework. But posting more links would be just piling on.
Try this one for yourself (if you're masochistic enough to be using that other framework). Delete the log file while your application is running. Oops. You can't do that, can you? Because the file is locked. I sure hope you don't have another application writing to the same file--or another logger in the same application.
For me, I think I'll stick with a framework that doesn't require a Ph.D. to configure--and one that is thread-safe, and yet I can be sure won't put my application into any deadlock situations. Like this one.
I've also read about other serious deadlock issues with that framework. But posting more links would be just piling on.
Try this one for yourself (if you're masochistic enough to be using that other framework). Delete the log file while your application is running. Oops. You can't do that, can you? Because the file is locked. I sure hope you don't have another application writing to the same file--or another logger in the same application.
For me, I think I'll stick with a framework that doesn't require a Ph.D. to configure--and one that is thread-safe, and yet I can be sure won't put my application into any deadlock situations. Like this one.
Labels:
Logging
Exceptional Gotchas!
Yes, the pun was intended.
Many ASP.NET developers think that having code in the Application_Error method in Global.asax.cs is adequate for dealing with any unhandled exceptions within their web application. While it usually is adequate--for most basic web applications, there are particular cases where this simply won't catch all errors.
Specifically, if you have code running on the server that is in a thread pool, or you've spawned a separate thread, or you have callback code for a timer, if an unhandled exception occurs, you just might be out of luck.
You can easily test this for yourself by creating a web page with two buttons, one that throws an exception when you click it, and the other that uses a thread from the thread pool and throws an exception within it. Something like this:
In addition to the HttpUnhandledException that gets sent to the Application_Error method, you need to handle the UnhandledException event on the AppDomain.
You can set up a handler in the Application_Start method in Global.asax.cs. But if you go a step further, you can create something a little more reusable. Here's an HttpModule that will demonstrate handling both kinds of web exeptions--those occurring during a normal web request, and those occurring on separate threads. Perhaps it should be part of the Logging Framework.
Modify the above to suit your needs. In addition, the following would go into your web.config file.
You might consider putting something like this into a separate utility assembly that you can reuse in all your web projects.
Many ASP.NET developers think that having code in the Application_Error method in Global.asax.cs is adequate for dealing with any unhandled exceptions within their web application. While it usually is adequate--for most basic web applications, there are particular cases where this simply won't catch all errors.
Specifically, if you have code running on the server that is in a thread pool, or you've spawned a separate thread, or you have callback code for a timer, if an unhandled exception occurs, you just might be out of luck.
You can easily test this for yourself by creating a web page with two buttons, one that throws an exception when you click it, and the other that uses a thread from the thread pool and throws an exception within it. Something like this:
public partial class _Default : System.Web.UI.Page
{
private void ThrowException()
{
throw new ApplicationException("Ouch!");
}
protected void Button1_Click(object sender, EventArgs e)
{
ThrowException();
}
protected void Button2_Click(object sender, EventArgs e)
{
System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback(DoWork));
}
private void DoWork(object dummy)
{
ThrowException();
}
}
In addition to the HttpUnhandledException that gets sent to the Application_Error method, you need to handle the UnhandledException event on the AppDomain.
You can set up a handler in the Application_Start method in Global.asax.cs. But if you go a step further, you can create something a little more reusable. Here's an HttpModule that will demonstrate handling both kinds of web exeptions--those occurring during a normal web request, and those occurring on separate threads. Perhaps it should be part of the Logging Framework.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using BitFactory.Logging;
namespace WebAppA
{
public class MyExceptionModule : IHttpModule
{
private HttpServerUtility Server { get; set; }
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
// deal with any non-main thread exceptions
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
// deal with any HttpUnhandledExceptions
context.Error += new EventHandler(context_Error);
Server = context.Server;
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// If we are here, the process is about to die
ConfigLogger.Instance.LogFatal(((Exception)e.ExceptionObject).Message);
}
void context_Error(object sender, EventArgs e)
{
// The outer exception is usually an HttpUnhandledException, so use the InnerException if it exists.
// Even more detail can be retrieved by iterateing over each InnerException, if desired.
Exception ex = Server.GetLastError();
ex = ex.InnerException ?? ex;
ConfigLogger.Instance.LogError(ex.Message);
}
#endregion
}
}
Modify the above to suit your needs. In addition, the following would go into your web.config file.
<httpModules> <add name="MyExceptionModule" type="WebAppA.MyExceptionModule, WebAppA" /> </httpModules>
You might consider putting something like this into a separate utility assembly that you can reuse in all your web projects.
Tuesday, December 29, 2009
The Overabundance of Logging Frameworks
An interesting question in a tweet caught my eye, and I thought I would attempt to provide an answer. To paraphrase, why are there so many logging frameworks for .NET, especially when there are perfectly capable facilities built-in?
Ill start off by saying that I think there are probably more logging frameworks than are necessary, simply because some have come along more recently which add very little value to what already exists. However, the simple fact is that not every logging solution is going to feel right for everyone. People have very different needs--and different values.
Different values?
Yes, such as: flexibility vs. simplicity, or configurability vs. maintainability, etc.
With any tool you choose, typically you'll try to pick one that maximizes the benefits (that are important to you) and minimize the disadvantages.
I happen to think my logging framework maximizes the benefits most people look for in a logging framework, with very little downside. Incidentally, a lot of people agree. Here is just one example.
Watch this short video that demos my logging framework and tell me what you think.
Ill start off by saying that I think there are probably more logging frameworks than are necessary, simply because some have come along more recently which add very little value to what already exists. However, the simple fact is that not every logging solution is going to feel right for everyone. People have very different needs--and different values.
Different values?
Yes, such as: flexibility vs. simplicity, or configurability vs. maintainability, etc.
With any tool you choose, typically you'll try to pick one that maximizes the benefits (that are important to you) and minimize the disadvantages.
I happen to think my logging framework maximizes the benefits most people look for in a logging framework, with very little downside. Incidentally, a lot of people agree. Here is just one example.
Watch this short video that demos my logging framework and tell me what you think.
Friday, December 25, 2009
A Simple Twitter Logger
[UPDATE! Look here for the OAuth version.]
Yes, using The Object Guy's Logging Framework it is incredibly easy to create a logger that will write to Twitter.
Here's a sample:
Remember that Twitter may limit your status updates to 150 per hour and a thousand or so per day.
Yes, using The Object Guy's Logging Framework it is incredibly easy to create a logger that will write to Twitter.
Here's a sample:
public class TwitterLogger : Logger
{
protected override bool DoLog(LogEntry aLogEntry)
{
// without this, you may receive a '417 Expectation Failed' error
ServicePointManager.Expect100Continue = false;
using (WebClient webClient = new WebClient())
{
webClient.Credentials = new NetworkCredential("[YOUR USER NAME]", "[YOUR PASSWORD]");
var nvc = new NameValueCollection();
nvc["status"] = aLogEntry.Message;
webClient.UploadValues("http://twitter.com/statuses/update.xml", nvc);
}
return true;
}
}
Remember that Twitter may limit your status updates to 150 per hour and a thousand or so per day.
Subscribe to:
Posts (Atom)