diff --git a/plugin/metrics/collector.go b/plugin/metrics/collector.go index 01bde1c8..559dc5fa 100644 --- a/plugin/metrics/collector.go +++ b/plugin/metrics/collector.go @@ -1,6 +1,14 @@ package metric +// Metric is the API message for metric. +type Metric struct { + ID string + Name string + Labels map[string]string +} + // Collector is the interface definition for metric collector. type Collector interface { + Identify(id string) error Collect(metric *Metric) error } diff --git a/plugin/metrics/metric.go b/plugin/metrics/metric.go deleted file mode 100644 index 60487016..00000000 --- a/plugin/metrics/metric.go +++ /dev/null @@ -1,7 +0,0 @@ -package metric - -// Metric is the API message for metric. -type Metric struct { - Name string - Labels map[string]string -} diff --git a/plugin/metrics/segment/collector.go b/plugin/metrics/segment/collector.go index 56d8bb03..54aa9853 100644 --- a/plugin/metrics/segment/collector.go +++ b/plugin/metrics/segment/collector.go @@ -3,15 +3,10 @@ package segment import ( "time" - "github.com/google/uuid" "github.com/segmentio/analytics-go" metric "github.com/usememos/memos/plugin/metrics" ) -var ( - sessionUUID = uuid.NewString() -) - // collector is the metrics collector https://segment.com/. type collector struct { client analytics.Client @@ -26,6 +21,14 @@ func NewCollector(key string) metric.Collector { } } +// Identify will identify the server caller. +func (c *collector) Identify(id string) error { + return c.client.Enqueue(analytics.Identify{ + UserId: id, + Timestamp: time.Now().UTC(), + }) +} + // Collect will exec all the segment collector. func (c *collector) Collect(metric *metric.Metric) error { properties := analytics.NewProperties() @@ -34,9 +37,9 @@ func (c *collector) Collect(metric *metric.Metric) error { } return c.client.Enqueue(analytics.Track{ - Event: string(metric.Name), - AnonymousId: sessionUUID, - Properties: properties, - Timestamp: time.Now().UTC(), + UserId: metric.ID, + Timestamp: time.Now().UTC(), + Event: metric.Name, + Properties: properties, }) } diff --git a/server/auth.go b/server/auth.go index 33aec950..88a8937f 100644 --- a/server/auth.go +++ b/server/auth.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/usememos/memos/api" "github.com/usememos/memos/common" + metric "github.com/usememos/memos/plugin/metrics" "github.com/labstack/echo/v4" "golang.org/x/crypto/bcrypt" @@ -150,12 +151,15 @@ func (s *Server) createUserAuthSignInActivity(c echo.Context, user *api.User) er if err != nil { return errors.Wrap(err, "failed to marshal activity payload") } - _, err = s.Store.CreateActivity(ctx, &api.ActivityCreate{ + activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{ CreatorID: user.ID, Type: api.ActivityUserAuthSignIn, Level: api.ActivityInfo, Payload: string(payloadStr), }) + s.Collector.Collect(ctx, &metric.Metric{ + Name: string(activity.Type), + }) return err } @@ -169,11 +173,14 @@ func (s *Server) createUserAuthSignUpActivity(c echo.Context, user *api.User) er if err != nil { return errors.Wrap(err, "failed to marshal activity payload") } - _, err = s.Store.CreateActivity(ctx, &api.ActivityCreate{ + activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{ CreatorID: user.ID, Type: api.ActivityUserAuthSignUp, Level: api.ActivityInfo, Payload: string(payloadStr), }) + s.Collector.Collect(ctx, &metric.Metric{ + Name: string(activity.Type), + }) return err } diff --git a/server/memo.go b/server/memo.go index 3cf3e9ab..63538992 100644 --- a/server/memo.go +++ b/server/memo.go @@ -12,6 +12,7 @@ import ( "github.com/pkg/errors" "github.com/usememos/memos/api" "github.com/usememos/memos/common" + metric "github.com/usememos/memos/plugin/metrics" "github.com/labstack/echo/v4" ) @@ -576,11 +577,14 @@ func (s *Server) createMemoCreateActivity(c echo.Context, memo *api.Memo) error if err != nil { return errors.Wrap(err, "failed to marshal activity payload") } - _, err = s.Store.CreateActivity(ctx, &api.ActivityCreate{ + activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{ CreatorID: memo.CreatorID, Type: api.ActivityMemoCreate, Level: api.ActivityInfo, Payload: string(payloadStr), }) + s.Collector.Collect(ctx, &metric.Metric{ + Name: string(activity.Type), + }) return err } diff --git a/server/metric_collector.go b/server/metric_collector.go index 660625d4..9c755de6 100644 --- a/server/metric_collector.go +++ b/server/metric_collector.go @@ -8,47 +8,54 @@ import ( "github.com/usememos/memos/plugin/metrics/segment" "github.com/usememos/memos/server/profile" "github.com/usememos/memos/server/version" - "github.com/usememos/memos/store" ) // MetricCollector is the metric collector. type MetricCollector struct { - Collector metric.Collector + collector metric.Collector + ID string Enabled bool Profile *profile.Profile - Store *store.Store } const ( - segmentMetricWriteKey = "fTn5BumOkj352n3TGw9tu0ARH2dOkcoQ" + segmentMetricWriteKey = "NbPruMMmfqfD2AMCw3pkxZTsszVS3hKq" ) -func NewMetricCollector(profile *profile.Profile, store *store.Store) MetricCollector { +func (s *Server) registerMetricCollector() { c := segment.NewCollector(segmentMetricWriteKey) - - return MetricCollector{ - Collector: c, + mc := &MetricCollector{ + collector: c, + ID: s.ID, Enabled: true, - Profile: profile, - Store: store, + Profile: s.Profile, } + s.Collector = mc } -func (mc *MetricCollector) Collect(_ context.Context, metric *metric.Metric) { +func (mc *MetricCollector) Identify(_ context.Context) { if !mc.Enabled { return } - if mc.Profile.Mode == "dev" { + err := mc.collector.Identify(mc.ID) + if err != nil { + fmt.Printf("Failed to request segment, error: %+v\n", err) + } +} + +func (mc *MetricCollector) Collect(_ context.Context, metric *metric.Metric) { + if !mc.Enabled { return } if metric.Labels == nil { metric.Labels = map[string]string{} } + metric.Labels["mode"] = mc.Profile.Mode metric.Labels["version"] = version.GetCurrentVersion(mc.Profile.Mode) - - err := mc.Collector.Collect(metric) + metric.ID = mc.ID + err := mc.collector.Collect(metric) if err != nil { fmt.Printf("Failed to request segment, error: %+v\n", err) } diff --git a/server/resource.go b/server/resource.go index 684ae17d..07ca84a4 100644 --- a/server/resource.go +++ b/server/resource.go @@ -12,6 +12,7 @@ import ( "github.com/pkg/errors" "github.com/usememos/memos/api" "github.com/usememos/memos/common" + metric "github.com/usememos/memos/plugin/metrics" "github.com/labstack/echo/v4" ) @@ -287,11 +288,14 @@ func (s *Server) createResourceCreateActivity(c echo.Context, resource *api.Reso if err != nil { return errors.Wrap(err, "failed to marshal activity payload") } - _, err = s.Store.CreateActivity(ctx, &api.ActivityCreate{ + activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{ CreatorID: resource.CreatorID, Type: api.ActivityResourceCreate, Level: api.ActivityInfo, Payload: string(payloadStr), }) + s.Collector.Collect(ctx, &metric.Metric{ + Name: string(activity.Type), + }) return err } diff --git a/server/server.go b/server/server.go index 0028f521..af299820 100644 --- a/server/server.go +++ b/server/server.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/usememos/memos/api" + metric "github.com/usememos/memos/plugin/metrics" "github.com/usememos/memos/server/profile" "github.com/usememos/memos/store" "github.com/usememos/memos/store/db" @@ -46,11 +47,6 @@ func NewServer(ctx context.Context, profile *profile.Profile) (*Server, error) { storeInstance := store.New(db.DBInstance, profile) s.Store = storeInstance - metricCollector := NewMetricCollector(profile, storeInstance) - // Disable metrics collector. - metricCollector.Enabled = false - s.Collector = &metricCollector - e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ Format: `{"time":"${time_rfc3339}",` + `"method":"${method}","uri":"${uri}",` + @@ -93,6 +89,9 @@ func NewServer(ctx context.Context, profile *profile.Profile) (*Server, error) { embedFrontend(e) + // Register MetricCollector to server. + s.registerMetricCollector() + rootGroup := e.Group("") s.registerRSSRoutes(rootGroup) @@ -122,7 +121,7 @@ func (s *Server) Run(ctx context.Context) error { if err := s.createServerStartActivity(ctx); err != nil { return errors.Wrap(err, "failed to create activity") } - + s.Collector.Identify(ctx) return s.e.Start(fmt.Sprintf(":%d", s.Profile.Port)) } @@ -135,11 +134,14 @@ func (s *Server) createServerStartActivity(ctx context.Context) error { if err != nil { return errors.Wrap(err, "failed to marshal activity payload") } - _, err = s.Store.CreateActivity(ctx, &api.ActivityCreate{ + activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{ CreatorID: api.UnknownID, Type: api.ActivityServerStart, Level: api.ActivityInfo, Payload: string(payloadStr), }) + s.Collector.Collect(ctx, &metric.Metric{ + Name: string(activity.Type), + }) return err } diff --git a/server/user.go b/server/user.go index 625703ee..a1afd769 100644 --- a/server/user.go +++ b/server/user.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/usememos/memos/api" "github.com/usememos/memos/common" + metric "github.com/usememos/memos/plugin/metrics" "github.com/labstack/echo/v4" "golang.org/x/crypto/bcrypt" @@ -289,11 +290,14 @@ func (s *Server) createUserCreateActivity(c echo.Context, user *api.User) error if err != nil { return errors.Wrap(err, "failed to marshal activity payload") } - _, err = s.Store.CreateActivity(ctx, &api.ActivityCreate{ + activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{ CreatorID: user.ID, Type: api.ActivityUserCreate, Level: api.ActivityInfo, Payload: string(payloadStr), }) + s.Collector.Collect(ctx, &metric.Metric{ + Name: string(activity.Type), + }) return err } diff --git a/store/activity.go b/store/activity.go index fb266d16..2ebd94a7 100644 --- a/store/activity.go +++ b/store/activity.go @@ -57,7 +57,8 @@ func (s *Store) CreateActivity(ctx context.Context, create *api.ActivityCreate) return nil, FormatError(err) } - return activityRaw.toActivity(), nil + activity := activityRaw.toActivity() + return activity, nil } // createActivity creates a new activity.