I'd go with the first option. The creation of a Stopwatch is very cheap. And with a good wrapper the required code in each API method can be as simple as:
public int MyApiMethod()
{
using (new ExecutionTimeLogger())
{
// All API functionality goes inside this using block.
var theResultValue = 23;
return theResultValue;
}
}
The class ExecutionTimeLogger would look something like this:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using log4net;
public class ExecutionTimeLogger : IDisposable
{
private readonly ILog log = LogManager.GetLogger("ExecutionTimes");
private readonly string methodName;
private readonly Stopwatch stopwatch;
public ExecutionTimeLogger([CallerMemberName] string methodName = "")
{
this.methodName = methodName;
stopwatch = Stopwatch.StartNew();
}
public void Dispose()
{
log.Debug(methodName + "() took " + stopwatch.ElapsedMilliseconds + " ms.");
GC.SuppressFinalize(this);
}
}
Depending on your logger implementation the output could look something like this:
15:04:23.4477 | DEBUG | ExecutionTimes | MyApiMethod() took 42 ms.
Note that the log output will also be generated when the API method threw an exception inside the using, because the ExecutionTimeLogger instance will be disposed nethertheless.
The methodName parameter will be automatically filled by the compiler because it has the [CallerMemberName] attribute. You don't need to pass it every time you create an ExecutionTimeLogger.
The line GC.SuppressFinalize(this) tells the garbage collector that a call to the finalizer of the ExecutionTimeLogger instance doesn't have to be scheduled, because we know it never created unmanaged resources.
If you are using Unity as the DI framework you could also write a UnityContainerExtension that wraps every method having a certain custom attribute (say LogExecutionTimeAttribute for example) with the required measurement and logging code. That's much more complicated though.