I believe MasterPages actually inherit from UserControl.
Can pages use more than one masterpage? If not then what is the difference in just adding all of the code to a base page class and using that one, seems like it would be the same thing?
The difference is that with the MasterPage, you encapsulate logic into a container that can be plugged into Pages and swapped out at will. In OO, this is known as "loose coupling".
To elaborate on my previous example, let's say you have 20 page with an inheritance setup like this:
[tt]
Page Count Base Class
----------------------------------
5 System.Web.UI.Page
10 MyCustomPage
5 AnotherCustomPage
[/tt]
Let's say that on load, you want all pages to perform some initialization logic, security checks, whatever. Let's also say that you put this logic into MyCustomPage.OnLoad() and that some logic contained in MyCustomPage should only apply to pages of type MyCustomPage. If you want the other pages of the system to share some of the functionality of MyCustomPage, then you might be able to do it with the pages that inherit from System.Web.UI.Page (by chaning their superclass), but that would introduce uneeded functionality that only applies to MyCustomPage, and what about the pages inheriting from AnotherCustomPage?
Let's further assume that AnotherCustomPage also has important logic, but it's logic that should only apply to pages of type AnotherCustomPage and that AnotherCustomPage is a class you bought as part of a software package from a third-party vendor. So how do you share all the functionality of MyCustomPage?
With inheritance, it's not possible. You don't have access to the source code, so you can't change the superclass, and even if you did, you'd lose the functionality of AnotherCustomPage.
So how do you solve the problem of sharing only the needed functionality, and sharing it across pages? You do it with composition! If you have a component existing outside of the page class that does all of the processing, then you can easily include that component in all twenty pages.
In this case, we're speaking of a MasterPage as the component, because you can easily configure each page to use the MasterPage, and it would perform the common logic without obtruding into the inheritance structure.
So you get a sense that it's a step in the right direction, but your question is still valid, "What if we have more than one MasterPage introducing important functionality?".
In that case, you'd further abstract the functionality into its own class so that several MasterPages can use it which would actually be more "ideal" from an OO perspective. The introduction of composition of MasterPages is superior to an inheritance chain in terms of OO, but it's not a perfect solution.
In the last project I did I created a class "ErrorLogger" and called that from a function in my base page. Then all of my pages inherited from the base page and I only needed to call my base page function (which called the ErrorLogger" class). Is this not good practice?
The ErrorLogger is a good idea because it encapsulates logging functionality (though you can also consider TraceListeners), but if it works with your design, the Application_Error of Global.asax would be a "looser coupled" location to handle and log errors (because, again, you drop the dependency on a page type which could prove problematic to maintainability).
Alternately, you can consider exposing the logging functinoality as a static method so the worst you'd have to do is call ErrorLogger.LogException( myException ).