Skip to content

Commit 7a14292

Browse files
updated comments + readme + refactoring (#20)
* updated comments * updated readme * added tests for validate function * added tests for validate function * added tests for validate function * added tests for validate function * added tests for validate function * added tests for validate function * added tests for validate function * refactoring * refactoring * refactoring * refactoring
1 parent 5f07090 commit 7a14292

File tree

5 files changed

+311
-34
lines changed

5 files changed

+311
-34
lines changed

README.md

Lines changed: 175 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,182 @@
22

33
[![codecov](https://codecov.io/gh/prashantgupta24/activity-tracker/branch/master/graph/badge.svg)](https://codecov.io/gh/prashantgupta24/activity-tracker) [![Go Report Card](https://goreportcard.com/badge/github.com/prashantgupta24/activity-tracker)](https://goreportcard.com/report/github.com/prashantgupta24/activity-tracker) [![version][version-badge]][RELEASES]
44

5-
It is a libary that lets you monitor certain activities on your machine, and sends a heartbeat at a periodic (configurable) time detailing all the activity changes during that time. The activities that you want to monitor are pluggable handlers and can be added or removed according to your needs.
5+
It is a libary that lets you monitor certain activities on your machine, and sends a heartbeat at a periodic (configurable) time detailing all the activity changes during that time. The activities that you want to monitor are **pluggable** handlers for those activities and can be added or removed according to your needs.
6+
7+
## Installation
8+
9+
` go get -u github.com/prashantgupta24/activity-tracker`
10+
11+
## Usage
12+
13+
14+
heartbeatFrequency := 60 //value always in seconds
15+
workerFrequency := 5 //seconds
16+
17+
activityTracker := &tracker.Instance{
18+
HeartbeatFrequency: heartbeatFrequency,
19+
WorkerFrequency: workerFrequency,
20+
LogLevel: logging.Debug,
21+
}
22+
23+
//This starts the tracker for all handlers. It gives you a channel
24+
//which you can listen to for heartbeat objects
25+
heartbeatCh := activityTracker.Start()
26+
27+
//if you only want to track certain handlers, you can use StartWithhandlers
28+
//heartbeatCh := activityTracker.StartWithHanders(handler.MouseClickHandler(), handler.MouseCursorHandler())
29+
30+
31+
select {
32+
case heartbeat := <-heartbeatCh:
33+
34+
if !heartbeat.WasAnyActivity {
35+
36+
logger.Infof("no activity detected in the last %v seconds", int(heartbeatFrequency))
37+
38+
} else {
39+
40+
logger.Infof("activity detected in the last %v seconds.", int(heartbeatFrequency))
41+
42+
logger.Infof("Activity type:\n")
43+
44+
for activityType, times := range heartbeat.ActivityMap {
45+
logger.Infof("activityType : %v times: %v\n", activityType, len(times))
46+
}
47+
}
48+
}
49+
50+
## Output
51+
52+
The above code created a tracker with all (`Mouse-click`, `Mouse-movement` and `Screen-Change`) handlers activated. The `heartbeat frequency` is set to 60 seconds, i.e. every 60 seconds I received a `heartbeat` which mentioned all activities that were captured.
53+
54+
```
55+
INFO[2019-03-30T15:52:01-07:00] starting activity tracker with 60s heartbeat and 5s worker frequency...
56+
57+
INFO[2019-03-30T15:53:01-07:00] activity detected in the last 60 seconds.
58+
59+
INFO[2019-03-30T15:53:01-07:00] Activity type:
60+
INFO[2019-03-30T15:53:01-07:00] activityType : screen-change times: 7
61+
INFO[2019-03-30T15:53:01-07:00] activityType : mouse-click times: 10
62+
INFO[2019-03-30T15:53:01-07:00] activityType : cursor-move times: 12
63+
```
64+
65+
## How it works
66+
67+
There are 2 primary configs required for the tracker to work:
68+
69+
- `HeartbeatFrequency `
70+
71+
> The frequency at which you want the heartbeat (in seconds, default 60s)
72+
73+
74+
- `WorkerFrequency`
75+
76+
> The frequency at which you want the checks to happen within a heartbeat (default 60s).
77+
78+
79+
The activity tracker gives you a `heartbeat` object every 60 seconds, that is based on the `HeartbeatFrequency`. But there is something else to understand here. In order for the tracker to know how many times an activity occured, or how many times you moved the cursor for example, it needs to query the mouse movement library `n` number of times. That's where the `WorkerFrequency` comes into play.
80+
81+
The `WorkerFrequency` tells the tracker how many times to query each of the handlers in the tracker within a heartbeat. Let's say you want to know how many times the mouse cursor was moved. What you want to do is to start the tracker with the usual 60s `HeartbeatFrequency `, configured with a `Mouse-cursor` handler. In this case, you set the `WorkerFrequency` to 5 seconds. It will then keep checking the mouse coordinates every 5 seconds to see if there was a movement, and track each time there was a change. At the end of `HeartbeatFrequency`, it will track all the changes and send it in the `heatbeat` object.
82+
83+
> If you are just concerned whether any activity happened within a heartbeat or not, you can set `WorkerFrequency` the same as `HeartbeatFrequency`.
84+
85+
>If you want to know how many times an activity occured within a heartbeat, you might want to set the `WorkerFrequency` to a low value, so that it keeps quering the handlers.
86+
87+
88+
##### Note: If the `WorkerFrequency` and the `HeartbeatFrequency` are set the same, then the `WorkerFrequency` always is started a fraction of a second before the `HeartbeatFrequency` kicks in. This is done so that when the `heartbeat` is going to be generated at the end of `HeartbeatFrequency`, the worker should have done its job of querying each of the handlers before that.
89+
90+
## Usecase
91+
92+
Suppose you want to track Activities A, B and C on your machine, and you want a heartbeat every 5 minutes. What you want is for the tracker to send you heartbeats every 5 minutes, and each heartbeat would contain whether any of A, B or C occured within those 5 minutes, and if so, at what times.
93+
94+
As another example, let's say you want to monitor whether there was any mouse click on your machine and you want to be monitor every 5 minutes. What you do is start the `Activity Tracker` with just the `mouse click` handler and `heartbeat` frequency set to 5 minutes. The `Start` function of the library gives you a channel which receives a `heartbeat` every 5 minutes, and it has details on whether there was a `click` in those 5 minutes, and if yes, the times the click happened.
95+
96+
97+
98+
# Components
99+
100+
### Heartbeat struct
101+
102+
It is the data packet sent from the tracker library to the user.
103+
104+
type Heartbeat struct {
105+
WasAnyActivity bool //whether any activity was detected
106+
ActivityMap map[activity.Type][]time.Time //activity type with its times
107+
Time time.Time //heartbeat time
108+
}
109+
110+
`WasAnyActivity` tells if there was any activity within that time frame
111+
If there was, then the `ActivityMap` will tell you what type of activity it was and what all times it occured.
112+
113+
The `Time` field is the time of the Heartbeat sent (not to be confused with
114+
the activity time, which is the time the activity occured within the time frame)
115+
116+
### Tracker
117+
118+
The tracker is the main struct for the library.
119+
120+
//Instance is an instance of the tracker
121+
HeartbeatFrequency int //the frequency at which you want the heartbeat (in seconds, default 60s)
122+
WorkerFrequency int //therequency at which you want the checks to happen within a heartbeat (in seconds, default 5s)
123+
LogLevel string
124+
LogFormat string
125+
126+
127+
#### - `HeartbeatFrequency `
128+
129+
The frequency at which you want the heartbeat (in seconds, default 60s)
130+
131+
##### Note: The `HeartbeatFrequency ` value can be set anywhere between 60 seconds - 300 seconds. Not setting it or setting it to anything other than the allowed range will revert it to default of 60s.
132+
133+
#### - `WorkerFrequency`
134+
135+
The frequency at which you want the checks to happen within a heartbeat (default 60s).
136+
137+
##### Note: The `WorkerFrequency ` value can be set anywhere between 4 seconds - 60 seconds. It CANNOT be more than `HeartbeatFrequency` for obvious reasons. Not setting it or setting it to anything other than the allowed range will revert it to default of 60s.
138+
139+
140+
## Relationship between Activity and Handler
141+
142+
Activity and Handler have a 1-1 mapping, i.e. each handler can only handle one type of activity, and vice-versa, each activity should be handled by one handler only.
143+
144+
The `Type` in the `Handler` interface determines the type of activity the particular handler handles.
145+
146+
## New pluggable handlers for activities
147+
148+
149+
//Instance is the main interface for a Handler for the tracker
150+
type Instance interface {
151+
Start(*log.Logger, chan *activity.Instance)
152+
Type() activity.Type
153+
Trigger()
154+
Close()
155+
}
156+
157+
Any new type of handler for an activity can be easily added, it just needs to implement the above `Handler` interface and define what type of activity it is going to track, that's it! It can be plugged in with the tracker and then the tracker will include those activity checks in its heartbeat.
158+
159+
160+
## Currently supported list of activities/handlers
161+
162+
163+
### Activities
164+
165+
MouseCursorMovement Type = "cursor-move"
166+
MouseClick Type = "mouse-click"
167+
ScreenChange Type = "screen-change"
168+
169+
### Corresponding handlers
170+
171+
mouseCursorHandler
172+
mouseClickHandler
173+
screenChangeHandler
174+
175+
176+
- Mouse click (whether any mouse click happened during the time frame)
177+
- Mouse cursor movement (whether the mouse cursor was moved during the time frame)
178+
- Screen change (whether the screen was changed anytime within that time frame)
179+
6180

7-
For example, let's say you want to monitor whether there was any mouse click on your machine and you want to be notified every 5 minutes if there was one or not. What you do is start the `Activity Tracker` with just the `mouse click` handler and `heartbeat` frequency set to 5 minutes. The `Start` function of the library gives you a channel which receives a `heartbeat` every 5 minutes, and it has details on whether there was a `click` in those 5 minutes.
8181

9182
[version-badge]: https://img.shields.io/github/release/prashantgupta24/activity-tracker.svg
10183
[RELEASES]: https://github.com/prashantgupta24/activity-tracker/releases

example/example.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,33 @@ func main() {
1212

1313
logger := logging.New()
1414

15-
logger.Infof("starting activity tracker")
16-
17-
frequency := 12 //value always in seconds
15+
heartbeatFrequency := 60 //value always in seconds
16+
workerFrequency := 5 //seconds
1817

1918
activityTracker := &tracker.Instance{
20-
Frequency: frequency,
21-
LogLevel: logging.Info,
19+
HeartbeatFrequency: heartbeatFrequency,
20+
WorkerFrequency: workerFrequency,
21+
LogLevel: logging.Info,
2222
}
2323

24-
//This starts the tracker for all handlers
24+
//This starts the tracker for all handlers. It gives you a channel
25+
//which you can listen to for heartbeat objects
2526
heartbeatCh := activityTracker.Start()
2627

2728
//if you only want to track certain handlers, you can use StartWithhandlers
2829
//heartbeatCh := activityTracker.StartWithHanders(handler.MouseClickHandler(), handler.MouseCursorHandler())
2930

30-
timeToKill := time.NewTicker(time.Second * 60)
31+
timeToKill := time.NewTicker(time.Second * 120)
32+
33+
logger.Infof("starting activity tracker with %vs heartbeat and %vs worker frequency...", heartbeatFrequency, workerFrequency)
3134

3235
for {
3336
select {
3437
case heartbeat := <-heartbeatCh:
3538
if !heartbeat.WasAnyActivity {
36-
logger.Infof("no activity detected in the last %v seconds\n\n\n", int(frequency))
39+
logger.Infof("no activity detected in the last %v seconds\n\n\n", int(heartbeatFrequency))
3740
} else {
38-
logger.Infof("activity detected in the last %v seconds.", int(frequency))
41+
logger.Infof("activity detected in the last %v seconds.", int(heartbeatFrequency))
3942
logger.Infof("Activity type:\n")
4043
for activityType, times := range heartbeat.ActivityMap {
4144
logger.Infof("activityType : %v times: %v\n", activityType, len(times))

pkg/tracker/tracker.go

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ import (
1212

1313
const (
1414
preHeartbeatTime = time.Millisecond * 100
15+
//heartbeat (seconds)
16+
minHFrequency = 60
17+
maxHFrequency = 300
18+
defaultHFrequency = 60
19+
20+
//worker (seconds)
21+
minWFrequency = 4
22+
maxWFrequency = minHFrequency
23+
defaultWFrequency = minHFrequency
24+
25+
numWorkerFrequencyDivisions = 5
1526
)
1627

1728
//StartWithHandlers starts the tracker with a set of handlers
@@ -31,22 +42,18 @@ func (tracker *Instance) StartWithHandlers(handlers ...handler.Instance) (heartb
3142
"method": "activity-tracker",
3243
})
3344

34-
timeToCheck := time.Duration(tracker.Frequency)
35-
//tickers
36-
tickerHeartbeat := time.NewTicker(timeToCheck * time.Second)
37-
var tickerWorker *time.Ticker
38-
if timeToCheck >= 10 {
39-
tickerWorker = time.NewTicker((timeToCheck / 5 * time.Second) - preHeartbeatTime)
40-
} else {
41-
tickerWorker = time.NewTicker(timeToCheck*time.Second - preHeartbeatTime)
42-
}
45+
//instantiating ticker frequencies
46+
heartbeatFrequency, workerFrequency := tracker.validateFrequencies()
47+
48+
tickerHeartbeat := time.NewTicker(heartbeatFrequency * time.Second)
49+
tickerWorker := time.NewTicker(workerFrequency*time.Second - preHeartbeatTime)
4350

4451
activityMap := makeActivityMap()
4552

4653
for {
4754
select {
4855
case <-tickerWorker.C:
49-
trackerLog.Infof("tracker worker working")
56+
trackerLog.Debugln("tracker worker working")
5057
//time to trigger all registered handlers
5158
for _, handler := range tracker.handlers {
5259
handler.Trigger()
@@ -55,14 +62,14 @@ func (tracker *Instance) StartWithHandlers(handlers ...handler.Instance) (heartb
5562
trackerLog.Debugln("tracker heartbeat checking")
5663
var heartbeat *Heartbeat
5764
if len(activityMap) == 0 {
58-
logger.Debugf("no activity detected in the last %v seconds ...\n", int(timeToCheck))
65+
logger.Debugf("no activity detected in the last %v seconds ...\n", int(heartbeatFrequency))
5966
heartbeat = &Heartbeat{
6067
WasAnyActivity: false,
6168
ActivityMap: nil,
6269
Time: time.Now(),
6370
}
6471
} else {
65-
trackerLog.Debugf("activity detected in the last %v seconds ...\n", int(timeToCheck))
72+
trackerLog.Debugf("activity detected in the last %v seconds ...\n", int(heartbeatFrequency))
6673
heartbeat = &Heartbeat{
6774
WasAnyActivity: true,
6875
ActivityMap: activityMap,
@@ -113,6 +120,32 @@ func makeActivityMap() (activityMap map[activity.Type][]time.Time) {
113120
return activityMap
114121
}
115122

123+
func (tracker *Instance) validateFrequencies() (heartbeatFreqReturn, workerFreqReturn time.Duration) {
124+
heartbeatFreq := tracker.HeartbeatFrequency
125+
workerFreq := tracker.WorkerFrequency
126+
127+
if tracker.isTest {
128+
heartbeatFreqReturn = time.Duration(heartbeatFreq)
129+
workerFreqReturn = time.Duration(heartbeatFreq)
130+
return
131+
}
132+
133+
//heartbeat check
134+
if heartbeatFreq >= minHFrequency && heartbeatFreq <= maxHFrequency {
135+
heartbeatFreqReturn = time.Duration(heartbeatFreq) //within range
136+
} else {
137+
heartbeatFreqReturn = time.Duration(defaultHFrequency)
138+
}
139+
140+
//worker check
141+
if workerFreq >= minWFrequency && workerFreq <= maxWFrequency {
142+
workerFreqReturn = time.Duration(workerFreq) //within range
143+
} else {
144+
workerFreqReturn = time.Duration(defaultWFrequency)
145+
}
146+
return
147+
}
148+
116149
func (tracker *Instance) registerHandlers(logger *log.Logger, handlers ...handler.Instance) {
117150

118151
tracker.handlers = make(map[activity.Type]handler.Instance)

0 commit comments

Comments
 (0)