Skip to content

Commit ff89bdd

Browse files
authored
Merge pull request #1929 from onflow/mfbz/emulator-middleware
Added metrics middleware to emulator start
2 parents 3683f33 + 649a6e9 commit ff89bdd

5 files changed

Lines changed: 56 additions & 8 deletions

File tree

.goreleaser.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ release:
5555
owner: onflow
5656
name: flow-cli
5757
prerelease: auto
58-
draft: false
58+
draft: false

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ require (
2121
github.com/onflow/fcl-dev-wallet v0.8.0
2222
github.com/onflow/flixkit-go/v2 v2.3.0
2323
github.com/onflow/flow-core-contracts/lib/go/templates v1.6.1
24-
github.com/onflow/flow-emulator v1.3.0
24+
github.com/onflow/flow-emulator v1.4.0
2525
github.com/onflow/flow-evm-gateway v1.0.3
2626
github.com/onflow/flow-go v0.38.1-0.20250218174738-2181389f9f7d
2727
github.com/onflow/flow-go-sdk v1.4.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -844,8 +844,8 @@ github.com/onflow/flow-core-contracts/lib/go/contracts v1.5.1-preview h1:W+QkNQc
844844
github.com/onflow/flow-core-contracts/lib/go/contracts v1.5.1-preview/go.mod h1:LyCICUK6sK1jtEyb+3GuRw5tYfHT1uxACLwLTLxw/0I=
845845
github.com/onflow/flow-core-contracts/lib/go/templates v1.6.1 h1:Y0bDvS5fTOCrKr7QFl0by3qTq7MFnauVnHoxwW6nQzo=
846846
github.com/onflow/flow-core-contracts/lib/go/templates v1.6.1/go.mod h1:pN768Al/wLRlf3bwugv9TyxniqJxMu4sxnX9eQJam64=
847-
github.com/onflow/flow-emulator v1.3.0 h1:SqKbXZ79GcfnDDaggQccEkp6fBMFQVG4AxRtpT+jYYk=
848-
github.com/onflow/flow-emulator v1.3.0/go.mod h1:+RV5j5TYEE0VYtUAEn/vduecy8yK8P+rB+a9BCc3gQA=
847+
github.com/onflow/flow-emulator v1.4.0 h1:LSNYV4Dd6Aetd2Q/4qc1mes5BIt4JTKMoQfr5QXdBFQ=
848+
github.com/onflow/flow-emulator v1.4.0/go.mod h1:+RV5j5TYEE0VYtUAEn/vduecy8yK8P+rB+a9BCc3gQA=
849849
github.com/onflow/flow-evm-gateway v1.0.3 h1:Mlz3EBVTz7wpUwrDTVSJtd8CG/DtEe920UDUPiTGN7Y=
850850
github.com/onflow/flow-evm-gateway v1.0.3/go.mod h1:JukrDrisLQvup7FwCkA/z7LEKtLpCETPSu25CAuuEN0=
851851
github.com/onflow/flow-ft/lib/go/contracts v1.0.1 h1:Ts5ob+CoCY2EjEd0W6vdLJ7hLL3SsEftzXG2JlmSe24=

internal/command/command.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,14 @@ func initCrashReporting() {
364364
}
365365

366366
// The token is injected at build-time using ldflags
367-
var mixpanelToken = ""
367+
var MixpanelToken = ""
368368

369369
func UsageMetrics(command *cobra.Command, wg *sync.WaitGroup) {
370-
if !settings.MetricsEnabled() || mixpanelToken == "" {
370+
if !settings.MetricsEnabled() || MixpanelToken == "" {
371371
return
372372
}
373373
wg.Add(1)
374-
client := mixpanel.New(mixpanelToken, "")
374+
client := mixpanel.New(MixpanelToken, "")
375375

376376
// calculates a user ID that doesn't leak any personal information
377377
usr, _ := user.Current() // ignore err, just use empty string

internal/emulator/start.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@
1919
package emulator
2020

2121
import (
22+
"crypto/sha256"
23+
"encoding/base64"
2224
"errors"
2325
"fmt"
26+
"net/http"
2427
"os"
28+
"os/user"
29+
"runtime"
2530
"sync"
2631

32+
"github.com/dukex/mixpanel"
2733
"github.com/onflow/flow-emulator/cmd/emulator/start"
2834
"github.com/onflow/flow-go-sdk/crypto"
2935
"github.com/spf13/afero"
@@ -32,12 +38,17 @@ import (
3238
"github.com/onflow/flowkit/v2"
3339
"github.com/onflow/flowkit/v2/config"
3440

41+
"github.com/onflow/flow-cli/build"
3542
"github.com/onflow/flow-cli/internal/command"
43+
"github.com/onflow/flow-cli/internal/settings"
3644
"github.com/onflow/flow-cli/internal/util"
3745
)
3846

3947
var Cmd *cobra.Command
4048

49+
// Mixpanel client to be reused on each http request of the middleware
50+
var mixpanelClient mixpanel.Mixpanel
51+
4152
func configuredServiceKey(
4253
init bool,
4354
_ crypto.SignatureAlgorithm,
@@ -97,8 +108,45 @@ func configuredServiceKey(
97108
return *privateKey, serviceAccount.Key.SigAlgo(), serviceAccount.Key.HashAlgo()
98109
}
99110

111+
func trackRequestMiddleware(next http.Handler) http.Handler {
112+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
113+
// Generate a unique user ID
114+
usr, _ := user.Current() // ignore err, just use empty string
115+
hash := sha256.Sum256([]byte(fmt.Sprintf("%s%s", usr.Username, usr.Uid)))
116+
userID := base64.StdEncoding.EncodeToString(hash[:])
117+
118+
// Track the request in Mixpanel
119+
_ = mixpanelClient.Track(userID, "emulator-request", &mixpanel.Event{
120+
IP: "0", // do not track IPs
121+
Properties: map[string]any{
122+
"method": r.Method,
123+
"url": r.URL.String(),
124+
"version": build.Semver(),
125+
"os": runtime.GOOS,
126+
"ci": os.Getenv("CI") != "", // CI is commonly set by CI providers
127+
},
128+
})
129+
130+
// Call the next handler
131+
next.ServeHTTP(w, r)
132+
})
133+
}
134+
100135
func init() {
101-
Cmd = start.Cmd(configuredServiceKey)
136+
// Initialize mixpanel client only if metrics are enabled and token is not empty
137+
if settings.MetricsEnabled() && command.MixpanelToken != "" {
138+
mixpanelClient = mixpanel.New(command.MixpanelToken, "")
139+
Cmd = start.Cmd(start.StartConfig{
140+
GetServiceKey: configuredServiceKey,
141+
RestMiddlewares: []start.HttpMiddleware{trackRequestMiddleware},
142+
})
143+
} else {
144+
Cmd = start.Cmd(start.StartConfig{
145+
GetServiceKey: configuredServiceKey,
146+
RestMiddlewares: []start.HttpMiddleware{},
147+
})
148+
}
149+
102150
Cmd.Use = "emulator"
103151
Cmd.Short = "Run Flow network for development"
104152
Cmd.GroupID = "tools"

0 commit comments

Comments
 (0)