Open-source project
Fody/MethodTimer avatar
Fody/MethodTimer

MethodTimer.Fody: weaving method timing into a .NET assembly at compile time

Injects some very basic method timing code.

781 stars78 forksC#MIT

At a glance

What is it?
MethodTimer.Fody is a Fody add-in that injects timing code into methods marked with a [Time] attribute, so the instrumentation is baked into the assembly rather than left to the developer. What it gives you is a stopwatch around the method body and a Trace.WriteLine call, or a call into your own interceptor, with a real limitation in how parameter values can be captured.
Who is it for?
Adopt MethodTimer.Fody if you want a quick, low-ceremony way to see how long a method takes in a .NET build you are already profiling, and you are comfortable accepting an interceptor shape that must match one of the documented Log signatures exactly. Do not adopt it if you need argument values beyond plain parameter names, if the woven assembly has to be reproducible byte for byte across machines, or if a build-time weaver is not acceptable in your pipeline.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

MethodTimer is a weaver, not a runtime profiler

MethodTimer.Fody is described in one line as injecting some very basic method timing code, and that is a fair summary of the scope. It is an add-in for Fody, which means it runs as part of the build and rewrites your compiled assembly rather than observing it at runtime.

The difference matters for what you can expect. A sampling profiler gives you a statistical picture of a whole application under load. This gives you a stopwatch around a specific method body, in the assembly you ship, with no agent and no sampling overhead to argue about. The implementation uses an allocation-free method to determine the duration of a method, which is the design decision that makes it usable inside a hot path rather than only in a lab.

The audience is a .NET developer who wants to answer a narrow question, which of these two calls is slow, and wants the answer visible in trace output rather than in a separate profiling session. It suits diagnostics and experiments, not continuous production monitoring.

The [Time] attribute and what the weaver does to your method

You mark a method with the Time attribute and the weaver rewrites the method body at compile time. The input looks like this:

csharp
public class MyClass
{
    [Time]
    public void MyMethod()
    {
        //Some code u are curious how long it takes
        Console.WriteLine("Hello");
    }
}

Without an interceptor, the compiled result captures a start timestamp, wraps the original body in a try block, and in the finally block captures an end timestamp, computes the elapsed span and writes a trace line:

csharp
        var start = Stopwatch.GetTimestamp();
        try
        {
            //Some code u are curious how long it takes
            Console.WriteLine("Hello");
        }
        finally
        {
            var end = Stopwatch.GetTimestamp();
            var elapsed = end - start;
            var elapsedTimeSpan = new TimeSpan(...);
            Trace.WriteLine("MyClass.MyMethod " + elapsedTimeSpan.TotalMilliseconds + "ms");
        }

Two details are worth reading twice. The measurement uses Stopwatch.GetTimestamp rather than a DateTime, and it is wrapped in try/finally, so a method that throws still reports the time it consumed before unwinding. The output goes to Trace.WriteLine, which means it only appears if a listener is attached to the Trace source at runtime.

Taking the logging over with an interceptor

If Trace output is not what you want, you can take over the logging by defining a static class with a Log method, and the weaver will call it instead. Two interceptor shapes are supported, one taking the elapsed duration as a long count of milliseconds and one taking it as a TimeSpan:

csharp
public static class MethodTimeLogger
{
    public static void Log(MethodBase methodBase, long milliseconds, string message)
    {
        //Do some logging here
    }
}

The compiled call passes the MethodBase of the timed method, so the interceptor knows which method was measured, along with the elapsed value. The documentation states that when both methods are available, the interceptor will prefer the TimeSpan overload, which is a useful thing to know if you define both and expect the millisecond one to be called.

The method must be static and its name must be Log. The repository layout shows how many shapes are covered by tests, with separate assemblies for an interceptor, an interceptor with formatting, a TimeSpan interceptor with formatting, a nop interceptor, an interceptor in a referenced assembly, and even cases with wrong parameters and with a missing overload, plus a VBNet assembly with an interceptor.

Parameter values in the log message, and where the feature stops

To include argument values in the log, you put a string format in the attribute definition, and the weaver formats the message at compile time:

csharp
public class MyClass
{
    [Time("File name: '{fileName}'")]
    public void MyMethod(string fileName)
    {
        //Some code u are curious how long it takes
        Console.WriteLine("Hello");
    }
}

The allowed values are any parameter name, written as {fileName}, and {this}, which calls ToString() on the instance itself. Sub-properties are not supported, and the project notes that this might be implemented later. {this} is not available on static methods, and the weaver throws an error if it is used in one, which the repository reflects in a test assembly named for a static member with formatting.

There is a further constraint on the interceptor itself. The message parameter feature requires an updated Log method call with a specific definition, and if that method with the message parameter is not found, the weaver will raise an error:

csharp
public static void Log(MethodBase methodBase, long milliseconds, string message)

In other words, adding a format string to the attribute changes the shape of the call the weaver emits, so an existing interceptor with only two parameters will stop the build rather than silently drop the message.

The TimeAttribute class is injected and then removed

There is a packaging detail that affects how you organise a solution. In addition to the weaving assembly, the NuGet package adds a file named TimeAttribute.cs to the target project, containing the attribute definition itself:

csharp
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor,AllowMultiple = false)]
class TimeAttribute : Attribute
{
}

The attribute targets assembly, module, class, method and constructor, and it does not allow multiple uses, which means the weaver is expected to handle a class-level or assembly-level application as well as the method-level one shown earlier.

At compile time this attribute and all usages of it are removed from the target assembly. That is the point of the whole design, since a leftover attribute would be dead weight in a shipped binary. The documented workaround is the reverse case: if you want to reuse the class in a common assembly, change the class from internal to public, which results in the class not being removed at compile time.

So a shared helper library needs a slightly different shape from a single-project use, and the difference is one keyword.

Installing through NuGet and wiring up FodyWeavers.xml

The installation path is the standard Fody add-in route. Both packages come from NuGet, and the order matters in a way the project explains:

powershell
PM> Install-Package Fody
PM> Install-Package MethodTimer.Fody

The Fody package has to be installed explicitly because NuGet always defaults to the oldest, and the project calls it the most buggy, version of any dependency. If you only add MethodTimer.Fody and let NuGet resolve Fody transitively, you may end up compiling with a Fody that predates your add-in.

Next you add the weaver to FodyWeavers.xml, the file that lists which add-ins run over the assembly:

xml
<Weavers>
  <MethodTimer/>
</Weavers>

Because the weaver runs during the build, this is a build-time change, and it means anyone who checks out the repository needs the same package versions or the compiled output will differ. That is the operational cost of the tool, and it is worth keeping in mind when a build artefact is supposed to be reproducible.

Licence, release trail and who pays for it

The repository is MIT licensed, with the text in license.txt at the root of the tree. The project also carries a patron model, and the readme states that it is expected that all developers using Fody become a Patron on OpenCollective, with a link to a licensing and patron FAQ for the details.

That arrangement is worth reading rather than skimming, because it attaches a funding expectation to a package that is otherwise permissively licensed, and the terms that actually govern your use are in the Fody home repository pages the readme points to, not in this repository alone.

On release cadence, the tags are sparse relative to the commit history. The most recent releases are 3.2.2 from 2023-05-23, 3.2.1 from 2023-01-06 and 3.1.2 from 2020-01-26, and the readme points to closed milestones rather than a changelog for release notes. The repository is not archived and the last push was on 2026-10-14, so work continues on master without new tags.

Practically, that means you should pin the package version you test with, since upgrading means picking up whatever has landed on master rather than reading a curated release note.

Editorial conclusion

Adopt MethodTimer.Fody if you want a quick, low-ceremony way to see how long a method takes in a .NET build you are already profiling, and you are comfortable accepting an interceptor shape that must match one of the documented Log signatures exactly. Do not adopt it if you need argument values beyond plain parameter names, if the woven assembly has to be reproducible byte for byte across machines, or if a build-time weaver is not acceptable in your pipeline. Before you add it, check that your Fody version is not the one NuGet picks by default, because the project states that the Fody package must be installed explicitly to avoid the oldest, most buggy dependency version, and confirm the interceptor Log method has the message parameter if you use the parameter formatting feature.

Frequently asked questions

How do I install MethodTimer.Fody in a .NET project?

Install both packages from NuGet with PM> Install-Package Fody and PM> Install-Package MethodTimer.Fody, then add <MethodTimer/> to FodyWeavers.xml. Installing Fody explicitly is required because NuGet otherwise defaults to the oldest, and most buggy, version of a dependency.

How does MethodTimer.Fody measure a method?

The weaver rewrites the method body so it captures a start timestamp with Stopwatch.GetTimestamp, wraps the original code in a try block, and captures the end timestamp in a finally block. The duration is reported through Trace.WriteLine or through your own interceptor.

Can I log method parameters with MethodTimer.Fody?

Yes, by putting a format string in the attribute, for example [Time("File name: '{fileName}'")]. Any parameter name and {this} are allowed, sub-properties are not supported, and {this} cannot be used on static methods. Using a message requires your interceptor to have a Log method taking a message parameter, otherwise the weaver raises an error.

What happens to the Time attribute after the build?

The attribute class and all usages of it are removed from the target assembly at compile time. If you want to share the class from a common assembly, change it from internal to public, which stops it being removed.

Is MethodTimer.Fody still being worked on?

The repository is not archived and the last push was on 2026-10-14, but the tagged releases are older, with the most recent being 3.2.2 from 2023-05-23, and release notes live in closed milestones rather than a changelog. Pin the package version you test with.

Official sources

  1. Fody/MethodTimer on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fody-methodtimer.svg)](https://hysenlabs.com/projects/fody-methodtimer)

Was this useful?