How do I avoid editing Drupal core? I was building an exchange with a partners XML service, and I couldn't get the XML right, but as with all things Drupal, the xmlrpc error and action logging is less than robust.

So I did this in includes/xmlrpc.inc.

function xmlrpc_request($method, $args) {
  $xmlrpc_request = new stdClass();
  $xmlrpc_request-&gt;method = $method;
  $xmlrpc_request-&gt;args = $args;
  $xmlrpc_request-&gt;xml = &lt;&lt;&lt;EOD
&lt;?xml version="1.0"?&gt;
&lt;methodCall&gt;
&lt;methodName&gt;{$xmlrpc_request-&gt;method}&lt;/methodName&gt;
&lt;params&gt;
EOD;
  foreach ($xmlrpc_request-&gt;args as $arg) {
    $xmlrpc_request-&gt;xml .= '&lt;param&gt;&lt;value&gt;';
    $v = xmlrpc_value($arg);
    $xmlrpc_request-&gt;xml .= xmlrpc_value_get_xml($v);
    $xmlrpc_request-&gt;xml .= "&lt;/value&gt;&lt;/param&gt;\n";
  }
  $xmlrpc_request-&gt;xml .= '&lt;/params&gt;&lt;/methodCall&gt;';

  /* This part here */
  watchdog('xmlrpc',$xmlrpc_request-&gt;xml);
  /* End ridiculously tiny hack */

  return $xmlrpc_request;
}


I got the data I needed and in 10 minutes had the partner interface responding appropriately to my request because (shocking I know) logs are good.

I like the extra logging, and I want to keep it. What is the clean, straightforward, and most importantly, Drupal-approved way of doing that?
Hacking core is strongly discouraged for the uninitiated because it effectively reduces the support community of thousands to a support community of one (or whatever your team size is). Without this best practice, helping those new to Drupal would be nigh impossible. It also hinders modularity and, in some cases, security.

This having been said, hacking core is not always as evil as we like to make it out to be. Without modifying core, we wouldn't have distributions like Pressflow and the like that augment core in interesting ways. It's just vitally important that you know exactly what you're doing, that you're distributing your patches with your distribution (preferably in a way that allows you to apply them again automatically post-upgrade), and that you're keeping detailed documentation of what you've changed and why you've changed it.

Depending on how you have things structured, you could certainly make the above change to xmlrpc_request(), create a patch, and then use something like Drush Make to automate applying it (note that Drush Make is moving  into the Drush project itself for the 5.x release), while supplying additional documentation in the makefile and elsewhere as to what the change does and why it's necessary/desired.

Another common pattern to enhance core functions is to create a wrapper that adds a tiny bit of functionality to a core function, and call the wrapper in lieu of core's implementation. When feasible, this makes things much more modular. Consider the following:

/**
 * Wrapper function for xmlrpc_request() to provide logging.
 */
function mymodule_xmlrpc_request($method, $args) {
  $xrr = xmlrpc_request($method, $args);
  watchdog('xmlrpc', $xrr-&gt;xml);
  return $xrr;
}


Again, depending on what you're doing this may or may not be feasible, but when it is you've saved yourself a few headaches in trying to make sure that core stays patched and documented. Though in this case, a one-off function like this seems like a perfect candidate for such a wrapper. If your implementation is captured in a module, you could even expand on it to control the log level of your entire solution, disabling this functionality on production sites:

/**
 * Wrapper function for xmlrpc_request() to provide logging (if enabled).
 */
function mymodule_xmlrpc_request($method, $args) {
  $xrr = xmlrpc_request($method, $args);
  if (variable_get('mymodule_log_level', 0) &gt; 0) {
    watchdog('xmlrpc', $xrr-&gt;xml);
  }
}


In short, you want to maximize what you can do with modules (and you can do a lot), but there are legitimate reasons for altering core. It should be done with care, that's all.