Search code examples
javagoannotationsaspect

How to use/implement aspect orientation in Go?


I'd like to monitor arbitrary method calls and I am interested in 3 metrics of the method call:

  1. total time
  2. total number of requests
  3. total number of errors

In Java, I can define an annotation which inject the metrics initialization and calculating before and after the method call. For instance:

@Monitor
public void foo() {
   // do some stuff...
}

And this @Monitor annotation will inject some codes before and after the foo to generate the metrics for it.

Is there a similar way to achieve this in Go?


Solution

  • Check Aspect-Oriented Programming framework for Go and this.

    And see How to write benchmarks in Go - Dave Cheney


    You may use a proxy function to do this:

    total time: 1.002061047s
    total number of requests: 10
    number of errors: 0
    

    Proxy function:

    func (p *metrics) proxy(user func() error) func() error {
        return func() error {
            p.nCalls++
            t0 := time.Now()
            err := user()
            t := time.Since(t0)
            // p.d = append(p.d, t)
            p.total += t
            if err != nil {
                p.nErrs++
            }
            return err
        }
    }
    

    All:

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        m := new(metrics)
        f := m.proxy(user)
        for i := 0; i < 10; i++ {
            f()
        }
        m.show()
    }
    func user() error {
        time.Sleep(100 * time.Millisecond)
        return nil
    }
    
    type metrics struct {
        nCalls, nErrs int
        total         time.Duration
        // d []time.Duration
    }
    
    func (p *metrics) proxy(user func() error) func() error {
        return func() error {
            p.nCalls++
            t0 := time.Now()
            err := user()
            t := time.Since(t0)
            // p.d = append(p.d, t)
            p.total += t
            if err != nil {
                p.nErrs++
            }
            return err
        }
    }
    func (p *metrics) show() {
        fmt.Println("total time:", p.total)
        fmt.Println("total number of requests:", p.nCalls)
        fmt.Println("number of errors:", p.nErrs)
    }