11

I'm trying to find the best way to measure the duration of a method to log them on Application Insights, I know it's possible if we do something like this:

public void TestMethod()
{    
    var sw = Stopwatch.StartNew();

    //code here

    sw.Stop();
    Console.WriteLine("Time elapsed: {0}", sw.Elapsed);
}

But, as you suppose, I don't want to write it on all the methods, ideally, I want to use a decorator, something similar to this.

[MeasureTime]
public void TestMethod()
{
    //code here
}

Or something similar. So my question is: How can I build something like this? Is there a better way to do it?

Thanks in advance!

Ivan Sangines
  • 440
  • 5
  • 11
  • Have a look at https://stackoverflow.com/questions/12412840/how-do-i-create-a-generic-routine-to-time-methods – MakePeaceGreatAgain Feb 09 '18 at 11:57
  • This question is off-topic because it asks for the "best" way of doing something which is invariably going to be based on opinions, as is already evident by the answers here. – Lasse V. Karlsen Feb 09 '18 at 12:00
  • Personally, I would like to know if a custom attribute would be the ideal for this situation, and how would one implement such. – André B Feb 09 '18 at 12:00
  • 3
    _Best way to guarantee an upvoted question whereby everyone forgets the rules? Easy ask a programming exercise question so that people can demonstrate their cleverness_. OP and upvoters should perhaps review [ask] –  Feb 09 '18 at 12:02
  • 3
    Possible duplicate of [How to inject Application Insights code to monitor timing in all methods](https://stackoverflow.com/questions/46236321/how-to-inject-application-insights-code-to-monitor-timing-in-all-methods) – Peter Bons Feb 09 '18 at 13:00
  • Possible duplicate of [How do I create a generic routine to time methods?](https://stackoverflow.com/questions/12412840/how-do-i-create-a-generic-routine-to-time-methods) – Liam Feb 09 '18 at 14:36

2 Answers2

16

One way to do this would be to use an assembly weaver like 'Fody' with an extension that does exactly what you are looking for. Please see this link for an example extension: https://github.com/Fody/MethodTimer

How Fody works is it injects code into your code-base at compile time, utilising attributes as you have suggested in your question. The provided extension does just as you have described using a stopwatch to log the execution time of your code.

An example of usage:

Once the library is installed, you can add the annotation [Time] to the methods you wish to measure:

[Time]
public void TestMethod()
{
    //code here
}

You can then create a custom interceptor (A static class that will be automatically picked up by the Fody extension) which you can use to add a metric track into application insights:

public static class MethodTimeLogger
{
    public static void Log(MethodBase methodBase, long milliseconds)
    {
        var sample = new MetricTelemetry();
        sample.Name = methodBase.Name;
        sample.Value = milliseconds;
        // Your telemetryClient here
        telemetryClient.TrackMetric(sample);
    }
}
Lex Webb
  • 2,772
  • 2
  • 21
  • 36
11

What I did was create an IDisposable class that would start a stopwatch in the constructor and stop/print the result in the dispose:

public class Superwatch : IDisposable
{
    static Stopwatch Watch = new Stopwatch();
    static Superwatch()
    {
        Watch.Start();
    }

    TimeSpan Start;
    public Superwatch()
    {
        Start = Watch.Elapsed;
    }

    public void Dispose()
    {
        TimeSpan elapsed = Watch.Elapsed - Start;
        Console.WriteLine($"Time elapsed: {elapsed}");
    }
} 

Then just pack the method into a using of an instance of the class you created.

using (var watch = new Superwatch())
{
      //piece of code
}

Not as clean as a decorator, but relatively clean imo and configurable for pieces of code.

NotFound
  • 5,005
  • 2
  • 13
  • 33
  • 1
    The OP wanted to decorate a method to be measured. Btw.: why do you even implement `IDisposable`? There´s nothing to be disposed here. – MakePeaceGreatAgain Feb 09 '18 at 11:51
  • 2
    @HimBromBeere The Op said `ideally` and `Or something similar` -> I think this answer is fine. And the `IDisposable` is so it can be used in the `using` block. – Christoph Fink Feb 09 '18 at 11:52
  • This approach is no different to what the OP is already doing. – Owen Pauling Feb 09 '18 at 11:52
  • @OwenPauling Well it hides away the `StopWatch`, in particular starting and stopping it. – MakePeaceGreatAgain Feb 09 '18 at 11:54
  • 1
    @STDMP You could add `[CallerMemberName] string memberName = ""` to the constructor to get the method name and initialize `Info` with it. – Christoph Fink Feb 09 '18 at 11:54
  • 2
    You misuse the `Dispose`-pattern here to indicate the start and end of a time-measure. – MakePeaceGreatAgain Feb 09 '18 at 11:54
  • To me this approach also looks good until unless anyway see something serious ? – rahulaga-msft Feb 09 '18 at 11:55
  • 3
    @HimBromBeere It's quite similar ye, but that is what the OP asked for. Something that does the same only is 'cleaner'. Imo this (although maybe not the best method) gives you quite a clean and readable result and is easy to implement. – NotFound Feb 09 '18 at 11:55
  • one thing might be not so good here is - "what if any exception occurs in between" ? then also finalize will execute and will give impression method execution completed on so n so time, which is not correct, isn't it ? might be in other answer (foody/methodTimer) this might be not an issue – rahulaga-msft Feb 09 '18 at 11:59
  • 1
    Stopwatch aside, OP wants integration into Azure App Insights. I see no example of usage –  Feb 09 '18 at 12:01