pirsch

package module
v2.6.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2021 License: AGPL-3.0 Imports: 34 Imported by: 0

README

Pirsch

Go Reference Go Report Card Chat on Discord

Pirsch is a server side, no-cookie, drop-in and privacy focused tracking solution for Go. Integrated into a Go application it enables you to track HTTP traffic without invading the privacy of your visitors. The visualization of the data (dashboard) is not part of this project.

The name is in German and refers to a special kind of hunt: the hunter carefully and quietly enters the area to be hunted, he stalks against the wind in order to get as close as possible to the prey without being noticed.

If you're looking for a managed solution with an easy-to-use API and JavaScript integration, check out https://pirsch.io/.

How does it work?

Pirsch generates a unique fingerprint for each visitor. The fingerprint is a hash of the visitors IP, User-Agent, the date, and a salt. The date guarantees that the data is separated by day, so visitors can only be tracked for up to one day.

Each time a visitor opens your page, Pirsch will store a hit. The hits are analyzed using the Analyzer to extract meaningful data.

The tracking works without invading the visitor's privacy as no cookies are used nor required. Pirsch can track visitors using ad blockers that block trackers like Google Analytics.

Features

Pirsch tracks the following data:

  • unique visitor count per day, path, and hour
  • session count
  • bounce rate
  • view count
  • growth (unique visitors, sessions, bounces, views, average session duration)
  • average time on page
  • average session duration
  • languages
  • operating system and browser (including versions)
  • referrers
  • countries
  • platform
  • screen size
  • UTM query parameters for campaign tracking
  • entry and exit pages
  • custom event tracking

All timestamps are stored as UTC. Starting with version 2.1, the results can be transformed to the desired timezone. All data points belongs to an (optional) client, which can be used to split data between multiple domains for example. If you just integrate Pirsch into your application, you don't need to care about that field. But if you do, you need to set a client ID for all columns!

Usage

To store hits and statistics, Pirsch uses ClickHouse. Database migrations can be run manually be executing the migrations steps in schema or by using the automatic migration (make sure you set x-multi-statement to true).

Server-side tracking

Here is a quick demo on how to use the library:

// Migrate the database.
pirsch.Migrate("clickhouse://127.0.0.1:9000?x-multi-statement=true")

// Create a new ClickHouse client to save hits.
store, _ := pirsch.NewClient("tcp://127.0.0.1:9000", nil)

// Set up a default tracker with a salt.
// This will buffer and store hits and generate sessions by default.
tracker := pirsch.NewTracker(store, "salt", nil)

// Create a handler to serve traffic.
// We prevent tracking resources by checking the path. So a file on /my-file.txt won't create a new hit
// but all page calls will be tracked.
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path == "/" {
        go tracker.Hit(r, nil)
    }

    w.Write([]byte("<h1>Hello World!</h1>"))
}))

// And finally, start the server.
// We don't flush hits on shutdown but you should add that in a real application by calling Tracker.Flush().
log.Println("Starting server on port 8080...")
http.ListenAndServe(":8080", nil)

The secret salt passed to NewTracker should not be known outside your organization as it can be used to generate fingerprints equal to yours. Note that while you can generate the salt at random, the fingerprints will change too. To get reliable data configure a fixed salt and treat it like a password.

To analyze hits and processed data you can use the Analyzer, which provides convenience functions to extract useful information.

// This also needs access to the store.
analyzer := pirsch.NewAnalyzer(store)

// As an example, lets extract the total number of visitors.
// The filter is used to specify the time frame you're looking at (days) and is optional.
// If you pass nil, the Analyzer returns statistics for all hits (be careful about that!).
visitors, err := analyzer.Visitors(&pirsch.Filter{
    From: yesterday(),
    To: today()
})
Client-side tracking

You can also track visitors on the client side by adding pirsch.js to your website. It will perform a GET request to the configured endpoint.

<!-- add the tracking script to the head area and configure it using attributes -->
<script type="text/javascript" src="js/pirsch.js" id="pirschjs"
        data-endpoint="/count"
        data-client-id="42"
        data-track-localhost
        data-param-optional-param="test"></script>

The parameters are configured through HTML attributes. All of them are optional, except for the id. Here is a list of the possible options.

Option Description Default
data-endpoint The endpoint to call. This can be a local path, like /tracking, or a complete URL, like http://mywebsite.com/tracking. It must not contain any parameters. /pirsch
data-client-id The client ID to use, in case you plan to track multiple websites using the same backend, or you want to split the data. Note that the client ID must be validated in the backend. 0 (no client)
data-track-localhost Enable tracking hits on localhost. This is used for testing purposes only. false
data-param-* Additional parameters to send with the request. The name send is everything after data-param-. (no parameters)

To track the hits you need to call Hit from the endpoint that you configured for pirsch.js. Here is a simple example.

// Create an endpoint to handle client tracking requests.
// HitOptionsFromRequest is a utility function to process the required parameters.
// You might want to additional checks, like for the client ID.
http.Handle("/count", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    tracker.Hit(r, pirsch.HitOptionsFromRequest(r))
}))

HitOptionsFromRequest will read the parameters send by pirsch.js and returns a new HitOptions object that can be passed to Hit. You might want to split these steps into two, to run additional checks for the parameters that were sent by the user.

Custom Event Tracking

Custom events are conceptually the same as hits, except that they have a name and hold additional metadata. To create an event, call the tracker and pass in the additional fields.

http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path == "/" {
    	// The name in the options is required!
		options := pirsch.EventOptions{
            Name: "my-event",
            Duration: 42, // optional field to save a duration, this will be used to calculate an average time when using the analyzer
            Meta: map[string]string{ // optional metadata, the results can be filtered by them
                "http_status": "200",
                "product_id": "123",
            },
        }
        go tracker.Event(r, options, nil)
    }

    w.Write([]byte("<h1>Hello World!</h1>"))
}))

There are two methods to read events using the Analyzer. Analyzer.Events returns a list containing all events and metadata keys. Analyzer.EventBreakdown breaks down a single event by grouping the metadata fields by value. You have to set the Filter.EventName and Filter.EventMetaKey when using this function. All other analyzer methods can be used with an event name to filter for an event.

Mapping IPs to countries

Pirsch uses MaxMind's GeoLite2 database to map IPs to countries. The database is not included, so you need to download it yourself. IP mapping is optional, it must explicitly be enabled by setting the GeoDB attribute of the TrackerConfig or through the HitOptions when calling HitFromRequest.

  1. create an account at MaxMind
  2. generate a new license key
  3. call GetGeoLite2 with the path you would like to extract the tarball to and pass your license key
  4. create a new GeoDB by using NewGeoDB and the file you downloaded and extracted using the step before

The GeoDB should be updated on a regular basis. The Tracker has a method SetGeoDB to update the GeoDB at runtime (thread-safe).

Documentation

Read the full documentation for details, check out demos, or read the article at https://marvinblum.de/blog/server-side-tracking-without-cookies-in-go-OxdzmGZ1Bl.

Build pirsch.js

To minify pirsch.js to pirsch.min.js you need to run npm i and npm run minify inside the js directory.

Changelog

See CHANGELOG.md.

Contribution

Contributions are welcome! Please open a pull requests for your changes and tickets in case you would like to discuss something or have a question.

To run the tests you'll need a ClickHouse database, and a schema called pirschtest. The user is set to default (no password).

Note that we only accept pull requests if you transfer the ownership of your contribution to us. As we also offer a managed commercial solution with this library at its core, we want to make sure we can keep control over the source code.

License

GNU AGPLv3

Documentation

Index

Constants

View Source
const (
	// PlatformDesktop filters for everything on desktops.
	PlatformDesktop = "desktop"

	// PlatformMobile filters for everything on mobile devices.
	PlatformMobile = "mobile"

	// PlatformUnknown filters for everything where the platform is unspecified.
	PlatformUnknown = "unknown"
)
View Source
const (
	// BrowserChrome represents the Chrome and Chromium browser.
	BrowserChrome = "Chrome"

	// BrowserFirefox represents the Firefox browser.
	BrowserFirefox = "Firefox"

	// BrowserSafari  represents the Safari browser.
	BrowserSafari = "Safari"

	// BrowserOpera represents the Opera browser.
	BrowserOpera = "Opera"

	// BrowserEdge represents the Edge browser.
	BrowserEdge = "Edge"

	// BrowserIE represents the Internet Explorer browser.
	BrowserIE = "IE"

	// OSWindows represents the Windows operating system.
	OSWindows = "Windows"

	// OSMac represents the Mac operating system.
	OSMac = "Mac"

	// OSLinux represents a Linux distribution.
	OSLinux = "Linux"

	// OSAndroid represents the Android operating system.
	OSAndroid = "Android"

	// OSiOS represents the iOS operating system.
	OSiOS = "iOS"

	// OSWindowsMobile represents the Windows Mobile operating system.
	OSWindowsMobile = "Windows Mobile"
)
View Source
const (

	// GeoLite2Filename is the default filename of the GeoLite2 database.
	GeoLite2Filename = "GeoLite2-Country.mmdb"
)

Variables

View Source
var (
	// ErrNoPeriodOrDay is returned in case no period or day was specified to calculate the growth rate.
	ErrNoPeriodOrDay = errors.New("no period or day specified")
)
View Source
var NullClient = int64(0)

NullClient is a placeholder for no client (0).

View Source
var ScreenClasses = []screenClass{
	{5120, "UHD 5K"},
	{3840, "UHD 4K"},
	{2560, "WQHD"},
	{1920, "Full HD"},
	{1280, "HD"},
	{1024, "XL"},
	{800, "L"},
	{600, "M"},
	{415, "S"},
}

ScreenClasses is a list of typical screen sizes used to group resolutions. Everything below is considered "XS" (tiny).

Functions

func Fingerprint

func Fingerprint(r *http.Request, salt string) string

Fingerprint returns a hash for given request and salt. The hash is unique for the visitor.

func GetGeoLite2

func GetGeoLite2(path, licenseKey string) error

GetGeoLite2 downloads and unpacks the MaxMind GeoLite2 database. The tarball is downloaded and unpacked at the provided path. The directories will created if required. The license key is used for the download and must be provided for a registered account. Please refer to MaxMinds website on how to do that: https://dev.maxmind.com/geoip/geoip2/geolite2/ The database should be updated on a regular basis.

func GetScreenClass

func GetScreenClass(width int) string

GetScreenClass returns the screen class for given width in pixels.

func IgnoreHit

func IgnoreHit(r *http.Request) bool

IgnoreHit returns true, if a hit should be ignored for given request, or false otherwise. The easiest way to track visitors is to use the Tracker.

func Migrate

func Migrate(connection string) error

Migrate runs the database migration for given connection string. This will use the embedded schema migration scripts. You have to set the x-multi-statement to true, or else it will fail to run the queries.

func RunAtMidnight

func RunAtMidnight(f func()) context.CancelFunc

RunAtMidnight calls given function on each day of month on midnight (UTC), unless it is cancelled by calling the cancel function.

func Today

func Today() time.Time

Today returns the date for today without time at UTC.

Types

type ActiveVisitorStats

type ActiveVisitorStats struct {
	Path     string `json:"path"`
	Title    string `json:"title"`
	Visitors int    `json:"visitors"`
}

ActiveVisitorStats is the result type for active visitor statistics.

type Analyzer

type Analyzer struct {
	// contains filtered or unexported fields
}

Analyzer provides an interface to analyze statistics.

func NewAnalyzer

func NewAnalyzer(store Store) *Analyzer

NewAnalyzer returns a new Analyzer for given Store.

func (*Analyzer) ActiveVisitors

func (analyzer *Analyzer) ActiveVisitors(filter *Filter, duration time.Duration) ([]ActiveVisitorStats, int, error)

ActiveVisitors returns the active visitors per path and (optional) page title and the total number of active visitors for given duration. Use time.Minute*5 for example to get the active visitors for the past 5 minutes.

func (*Analyzer) AvgSessionDuration

func (analyzer *Analyzer) AvgSessionDuration(filter *Filter) ([]TimeSpentStats, error)

AvgSessionDuration returns the average session duration grouped by day.

func (*Analyzer) AvgTimeOnPage

func (analyzer *Analyzer) AvgTimeOnPage(filter *Filter) ([]TimeSpentStats, error)

AvgTimeOnPage returns the average time on page grouped by day.

func (*Analyzer) AvgTimeOnPages

func (analyzer *Analyzer) AvgTimeOnPages(filter *Filter) ([]TimeSpentStats, error)

AvgTimeOnPages returns the average time on page grouped by path and (optional) page title.

func (*Analyzer) Browser

func (analyzer *Analyzer) Browser(filter *Filter) ([]BrowserStats, error)

Browser returns the visitor count grouped by browser.

func (*Analyzer) BrowserVersion added in v2.2.2

func (analyzer *Analyzer) BrowserVersion(filter *Filter) ([]BrowserVersionStats, error)

BrowserVersion returns the visitor count grouped by browser and version.

func (*Analyzer) Countries

func (analyzer *Analyzer) Countries(filter *Filter) ([]CountryStats, error)

Countries returns the visitor count grouped by country.

func (*Analyzer) EntryPages added in v2.2.0

func (analyzer *Analyzer) EntryPages(filter *Filter) ([]EntryStats, error)

EntryPages returns the visitor count and time on page grouped by path and (optional) page title for the first page visited.

func (*Analyzer) EventBreakdown added in v2.4.0

func (analyzer *Analyzer) EventBreakdown(filter *Filter) ([]EventStats, error)

EventBreakdown returns the visitor count, views, and conversion rate for a custom event grouping them by a meta value for given key. The Filter.EventName and Filter.EventMetaKey must be set, or otherwise the result set will be empty.

func (*Analyzer) Events added in v2.4.0

func (analyzer *Analyzer) Events(filter *Filter) ([]EventStats, error)

Events returns the visitor count, views, and conversion rate for custom events.

func (*Analyzer) ExitPages added in v2.2.0

func (analyzer *Analyzer) ExitPages(filter *Filter) ([]ExitStats, error)

ExitPages returns the visitor count and time on page grouped by path and (optional) page title for the last page visited.

func (*Analyzer) Growth

func (analyzer *Analyzer) Growth(filter *Filter) (*Growth, error)

Growth returns the growth rate for visitor count, session count, bounces, views, and average session duration or average time on page (if path is set). The growth rate is relative to the previous time range or day. The period or day for the filter must be set, else an error is returned.

func (*Analyzer) Languages

func (analyzer *Analyzer) Languages(filter *Filter) ([]LanguageStats, error)

Languages returns the visitor count grouped by language.

func (*Analyzer) OS

func (analyzer *Analyzer) OS(filter *Filter) ([]OSStats, error)

OS returns the visitor count grouped by operating system.

func (*Analyzer) OSVersion added in v2.2.2

func (analyzer *Analyzer) OSVersion(filter *Filter) ([]OSVersionStats, error)

OSVersion returns the visitor count grouped by operating systems and version.

func (*Analyzer) PageConversions added in v2.2.5

func (analyzer *Analyzer) PageConversions(filter *Filter) (*PageConversionsStats, error)

PageConversions returns the visitor count, views, and conversion rate for conversion goals. This function is supposed to be used with the Filter.PathPattern, to list page conversions.

func (*Analyzer) Pages

func (analyzer *Analyzer) Pages(filter *Filter) ([]PageStats, error)

Pages returns the visitor count, session count, bounce rate, views, and average time on page grouped by path and (optional) page title.

func (*Analyzer) Platform

func (analyzer *Analyzer) Platform(filter *Filter) (*PlatformStats, error)

Platform returns the visitor count grouped by platform.

func (*Analyzer) Referrer

func (analyzer *Analyzer) Referrer(filter *Filter) ([]ReferrerStats, error)

Referrer returns the visitor count and bounce rate grouped by referrer.

func (*Analyzer) ScreenClass

func (analyzer *Analyzer) ScreenClass(filter *Filter) ([]ScreenClassStats, error)

ScreenClass returns the visitor count grouped by screen class.

func (*Analyzer) TotalSessionDuration

func (analyzer *Analyzer) TotalSessionDuration(filter *Filter) (int, error)

TotalSessionDuration returns the total session duration in seconds.

func (*Analyzer) TotalTimeOnPage

func (analyzer *Analyzer) TotalTimeOnPage(filter *Filter) (int, error)

TotalTimeOnPage returns the total time on page in seconds.

func (*Analyzer) UTMCampaign

func (analyzer *Analyzer) UTMCampaign(filter *Filter) ([]UTMCampaignStats, error)

UTMCampaign returns the visitor count grouped by utm source.

func (*Analyzer) UTMContent

func (analyzer *Analyzer) UTMContent(filter *Filter) ([]UTMContentStats, error)

UTMContent returns the visitor count grouped by utm source.

func (*Analyzer) UTMMedium

func (analyzer *Analyzer) UTMMedium(filter *Filter) ([]UTMMediumStats, error)

UTMMedium returns the visitor count grouped by utm medium.

func (*Analyzer) UTMSource

func (analyzer *Analyzer) UTMSource(filter *Filter) ([]UTMSourceStats, error)

UTMSource returns the visitor count grouped by utm source.

func (*Analyzer) UTMTerm

func (analyzer *Analyzer) UTMTerm(filter *Filter) ([]UTMTermStats, error)

UTMTerm returns the visitor count grouped by utm source.

func (*Analyzer) VisitorHours

func (analyzer *Analyzer) VisitorHours(filter *Filter) ([]VisitorHourStats, error)

VisitorHours returns the visitor count grouped by time of day.

func (*Analyzer) Visitors

func (analyzer *Analyzer) Visitors(filter *Filter) ([]VisitorStats, error)

Visitors returns the visitor count, session count, bounce rate, views, and average session duration grouped by day.

type BrowserStats

type BrowserStats struct {
	MetaStats
	Browser string `json:"browser"`
}

BrowserStats is the result type for browser statistics.

type BrowserVersionStats added in v2.2.2

type BrowserVersionStats struct {
	MetaStats
	Browser        string `json:"browser"`
	BrowserVersion string `db:"browser_version" json:"browser_version"`
}

BrowserVersionStats is the result type for browser version statistics.

type Client

type Client struct {
	sqlx.DB
	// contains filtered or unexported fields
}

Client is a ClickHouse database client.

func NewClient

func NewClient(connection string, logger *log.Logger) (*Client, error)

NewClient returns a new client for given database connection string. The logger is optional.

func (*Client) Count

func (client *Client) Count(query string, args ...interface{}) (int, error)

Count implements the Store interface.

func (*Client) Get

func (client *Client) Get(result interface{}, query string, args ...interface{}) error

Get implements the Store interface.

func (*Client) SaveEvents added in v2.4.0

func (client *Client) SaveEvents(events []Event) error

SaveEvents implements the Store interface.

func (*Client) SaveHits

func (client *Client) SaveHits(hits []Hit) error

SaveHits implements the Store interface.

func (*Client) Select

func (client *Client) Select(results interface{}, query string, args ...interface{}) error

Select implements the Store interface.

func (*Client) Session

func (client *Client) Session(clientID int64, fingerprint string, maxAge time.Time) (Session, error)

Session implements the Store interface.

type CountryStats

type CountryStats struct {
	MetaStats
	CountryCode string `db:"country_code" json:"country_code"`
}

CountryStats is the result type for country statistics.

type EntryStats added in v2.2.1

type EntryStats struct {
	Path                    string `json:"path"`
	Title                   string `json:"title"`
	Visitors                int    `json:"visitors"`
	Entries                 int    `json:"entries"`
	AverageTimeSpentSeconds int    `db:"average_time_spent_seconds" json:"average_time_spent_seconds"`
}

EntryStats is the result type for entry page statistics.

type Event added in v2.4.0

type Event struct {
	Hit
	Name            string   `db:"event_name" json:"name"`
	DurationSeconds int      `db:"event_duration_seconds" json:"duration_seconds"`
	MetaKeys        []string `db:"event_meta_keys" json:"meta_keys"`
	MetaValues      []string `db:"event_meta_values" json:"meta_values"`
}

Event represents a single data point for custom events. It's basically the same as Hit, but with some additional fields (event name, time, and meta fields).

func (Event) String added in v2.4.0

func (event Event) String() string

String implements the Stringer interface.

type EventOptions added in v2.4.0

type EventOptions struct {
	// Name is the name of the event (required).
	Name string

	// Duration is an optional duration that is used to calculate an average time on the dashboard.
	Duration int

	// Meta are optional fields used to break down the events that were send for a name.
	Meta map[string]string
}

EventOptions are the options to save a new event. The name is required. All other fields are optional.

type EventStats added in v2.4.0

type EventStats struct {
	Name                   string   `db:"event_name" json:"name"`
	Visitors               int      `json:"visitors"`
	Views                  int      `json:"views"`
	CR                     float64  `json:"cr"`
	AverageDurationSeconds int      `db:"average_duration_seconds" json:"average_duration_seconds"`
	MetaKeys               []string `db:"meta_keys" json:"meta_keys"`
	MetaValue              string   `db:"meta_value" json:"meta_value"`
}

EventStats is the result type for custom events.

type ExitStats added in v2.2.1

type ExitStats struct {
	Path     string  `json:"path"`
	Title    string  `json:"title"`
	Visitors int     `json:"visitors"`
	Exits    int     `json:"exits"`
	ExitRate float64 `db:"exit_rate" json:"exit_rate"`
}

ExitStats is the result type for exit page statistics.

type Filter

type Filter struct {
	// ClientID is the optional.
	ClientID int64

	// Timezone sets the timezone used to interpret dates and times.
	// It will be set to UTC by default.
	Timezone *time.Location

	// From is the start date of the selected period.
	From time.Time

	// To is the end date of the selected period.
	To time.Time

	// Day is an exact match for the result set ("on this day").
	Day time.Time

	// Start is the start date and time of the selected period.
	Start time.Time

	// Path filters for the path.
	// Note that if this and PathPattern are both set, Path will be preferred.
	Path string

	// PathPattern filters for the path using a (ClickHouse supported) regex pattern.
	// Note that if this and Path are both set, Path will be preferred.
	// Examples for useful patterns (all case-insensitive, * is used for every character but slashes, ** is used for all characters including slashes):
	//  (?i)^/path/[^/]+$ // matches /path/*
	//  (?i)^/path/[^/]+/.* // matches /path/*/**
	//  (?i)^/path/[^/]+/slashes$ // matches /path/*/slashes
	//  (?i)^/path/.+/slashes$ // matches /path/**/slashes
	PathPattern string

	// Language filters for the ISO language code.
	Language string

	// Country filters for the ISO country code.
	Country string

	// Referrer filters for the referrer.
	Referrer string

	// OS filters for the operating system.
	OS string

	// OSVersion filters for the operating system version.
	OSVersion string

	// Browser filters for the browser.
	Browser string

	// BrowserVersion filters for the browser version.
	BrowserVersion string

	// Platform filters for the platform (desktop, mobile, unknown).
	Platform string

	// ScreenClass filters for the screen class.
	ScreenClass string

	// UTMSource filters for the utm_source query parameter.
	UTMSource string

	// UTMMedium filters for the utm_medium query parameter.
	UTMMedium string

	// UTMCampaign filters for the utm_campaign query parameter.
	UTMCampaign string

	// UTMContent filters for the utm_content query parameter.
	UTMContent string

	// UTMTerm filters for the utm_term query parameter.
	UTMTerm string

	// EventName filters for an event by its name.
	EventName string

	// EventMetaKey filters for an event meta key.
	// This must be used together with an EventName.
	EventMetaKey string

	// Limit limits the number of results. Less or equal to zero means no limit.
	Limit int

	// IncludeTitle indicates whether the Analyzer.Pages, Analyzer.EntryPages, and Analyzer.ExitPages should contain the page title or not.
	IncludeTitle bool

	// IncludeAvgTimeOnPage indicates whether Analyzer.Pages and Analyzer.EntryPages should contain the average time on page or not.
	IncludeAvgTimeOnPage bool

	// MaxTimeOnPageSeconds is an optional maximum for the time spent on page.
	// Visitors who are idle artificially increase the average time spent on a page, this option can be used to limit the effect.
	// Set to 0 to disable this option (default).
	MaxTimeOnPageSeconds int
}

Filter are all fields that can be used to filter the result sets. Fields can be inverted by adding a "!" in front of the string.

func NewFilter

func NewFilter(clientID int64) *Filter

NewFilter creates a new filter for given client ID.

type GeoDB

type GeoDB struct {
	// contains filtered or unexported fields
}

GeoDB maps IPs to their geo location based on MaxMinds GeoLite2 or GeoIP2 database.

func NewGeoDB

func NewGeoDB(config GeoDBConfig) (*GeoDB, error)

NewGeoDB creates a new GeoDB for given database file. The file is loaded into memory, therefore it's not necessary to close the reader (see oschwald/maxminddb-golang documentatio). The database should be updated on a regular basis.

func (*GeoDB) CountryCode

func (db *GeoDB) CountryCode(ip string) string

CountryCode looks up the country code for given IP. If the IP is invalid it will return an empty string. The country code is returned in lowercase.

type GeoDBConfig

type GeoDBConfig struct {
	// File is the path (including the filename) to the GeoLite2 country database file.
	// See GeoLite2Filename for the required filename.
	File string

	// Logger is the log.Logger used for logging.
	// Note that this will log the IP address and should therefore only be used for debugging.
	// Set it to nil to disable logging for GeoDB.
	Logger *log.Logger
}

GeoDBConfig is the configuration for the GeoDB.

type Growth

type Growth struct {
	VisitorsGrowth  float64 `json:"visitors_growth"`
	ViewsGrowth     float64 `json:"views_growth"`
	SessionsGrowth  float64 `json:"sessions_growth"`
	BouncesGrowth   float64 `json:"bounces_growth"`
	TimeSpentGrowth float64 `json:"time_spent_growth"`
}

Growth represents the visitors, views, sessions, bounces, and average session duration growth between two time periods.

type Hit

type Hit struct {
	ClientID                  int64 `db:"client_id"`
	Fingerprint               string
	Time                      time.Time
	Session                   time.Time
	PreviousTimeOnPageSeconds int    `db:"previous_time_on_page_seconds"`
	UserAgent                 string `db:"user_agent"`
	Path                      string
	URL                       string
	Title                     string
	Language                  string
	CountryCode               string `db:"country_code"`
	Referrer                  string
	ReferrerName              string `db:"referrer_name"`
	ReferrerIcon              string `db:"referrer_icon"`
	OS                        string
	OSVersion                 string `db:"os_version"`
	Browser                   string
	BrowserVersion            string `db:"browser_version"`
	Desktop                   bool
	Mobile                    bool
	ScreenWidth               int    `db:"screen_width"`
	ScreenHeight              int    `db:"screen_height"`
	ScreenClass               string `db:"screen_class"`
	UTMSource                 string `db:"utm_source"`
	UTMMedium                 string `db:"utm_medium"`
	UTMCampaign               string `db:"utm_campaign"`
	UTMContent                string `db:"utm_content"`
	UTMTerm                   string `db:"utm_term"`
}

Hit represents a single data point/page visit and is the central entity of Pirsch.

func HitFromRequest

func HitFromRequest(r *http.Request, salt string, options *HitOptions) Hit

HitFromRequest returns a new Hit for given request, salt and HitOptions. The salt must stay consistent to track visitors across multiple calls. The easiest way to track visitors is to use the Tracker.

func (Hit) String

func (hit Hit) String() string

String implements the Stringer interface.

type HitOptions

type HitOptions struct {

	// SessionCache is the cache to look up sessions.
	SessionCache *SessionCache

	// ClientID is optionally saved with a hit to split the data between multiple clients.
	ClientID int64

	// SessionMaxAge defines the maximum time a session stays active.
	// A session is kept active if requests are made within the time frame.
	// Set to 15 minutes by default.
	SessionMaxAge time.Duration

	// URL can be set to manually overwrite the URL stored for this request.
	// This will also affect the Path, except it is set too.
	URL string

	// Path can be set to manually overwrite the path stored for the request.
	// This will also affect the URL.
	Path string

	// Title is the page title.
	Title string

	// Referrer can be set to manually overwrite the referrer from the request.
	Referrer string

	// ReferrerDomainBlacklist is used to filter out unwanted referrers from the Referrer header.
	// This can be used to filter out traffic from your own site or subdomains.
	// To filter your own domain and subdomains, add your domain to the list and set ReferrerDomainBlacklistIncludesSubdomains to true.
	// This way the referrer for blog.mypage.com -> mypage.com won't be saved.
	ReferrerDomainBlacklist []string

	// ReferrerDomainBlacklistIncludesSubdomains set to true to include all subdomains in the ReferrerDomainBlacklist,
	// or else subdomains must explicitly be included in the blacklist.
	// If the blacklist contains domain.com, sub.domain.com and domain.com will be treated as equals.
	ReferrerDomainBlacklistIncludesSubdomains bool

	// ScreenWidth sets the screen width to be stored with the hit.
	ScreenWidth int

	// ScreenHeight sets the screen height to be stored with the hit.
	ScreenHeight int
	// contains filtered or unexported fields
}

HitOptions is used to manipulate the data saved on a hit.

func HitOptionsFromRequest

func HitOptionsFromRequest(r *http.Request) *HitOptions

HitOptionsFromRequest returns the HitOptions for given client request. This function can be used to accept hits from pirsch.js. Invalid parameters are ignored and left empty. You might want to add additional checks before calling HitFromRequest afterwards (like for the HitOptions.ClientID).

type LanguageStats

type LanguageStats struct {
	MetaStats
	Language string `json:"language"`
}

LanguageStats is the result type for language statistics.

type MetaStats

type MetaStats struct {
	Visitors         int     `json:"visitors"`
	RelativeVisitors float64 `db:"relative_visitors" json:"relative_visitors"`
}

MetaStats is the base for meta result types (languages, countries, ...).

type MockClient

type MockClient struct {
	Hits          []Hit
	Events        []Event
	ReturnSession *Session
	// contains filtered or unexported fields
}

MockClient is a mock Store implementation.

func NewMockClient

func NewMockClient() *MockClient

NewMockClient returns a new mock client.

func (*MockClient) Count

func (client *MockClient) Count(query string, args ...interface{}) (int, error)

Count implements the Store interface.

func (*MockClient) Get

func (client *MockClient) Get(result interface{}, query string, args ...interface{}) error

Get implements the Store interface.

func (*MockClient) SaveEvents added in v2.4.0

func (client *MockClient) SaveEvents(events []Event) error

SaveEvents implements the Store interface.

func (*MockClient) SaveHits

func (client *MockClient) SaveHits(hits []Hit) error

SaveHits implements the Store interface.

func (*MockClient) Select

func (client *MockClient) Select(results interface{}, query string, args ...interface{}) error

Select implements the Store interface.

func (*MockClient) Session

func (client *MockClient) Session(clientID int64, fingerprint string, maxAge time.Time) (Session, error)

Session implements the Store interface.

type OSStats

type OSStats struct {
	MetaStats
	OS string `json:"os"`
}

OSStats is the result type for operating system statistics.

type OSVersionStats added in v2.2.2

type OSVersionStats struct {
	MetaStats
	OS        string `json:"os"`
	OSVersion string `db:"os_version" json:"os_version"`
}

OSVersionStats is the result type for operating system version statistics.

type PageConversionsStats added in v2.2.5

type PageConversionsStats struct {
	Visitors int     `json:"visitors"`
	Views    int     `json:"views"`
	CR       float64 `json:"cr"`
}

PageConversionsStats is the result type for page conversions.

type PageStats

type PageStats struct {
	Path                    string  `json:"path"`
	Title                   string  `json:"title"`
	Visitors                int     `json:"visitors"`
	Views                   int     `json:"views"`
	Sessions                int     `json:"sessions"`
	Bounces                 int     `json:"bounces"`
	RelativeVisitors        float64 `db:"relative_visitors" json:"relative_visitors"`
	RelativeViews           float64 `db:"relative_views" json:"relative_views"`
	BounceRate              float64 `db:"bounce_rate" json:"bounce_rate"`
	AverageTimeSpentSeconds int     `db:"average_time_spent_seconds" json:"average_time_spent_seconds"`
}

PageStats is the result type for page statistics.

type PlatformStats

type PlatformStats struct {
	PlatformDesktop         int     `db:"platform_desktop" json:"platform_desktop"`
	PlatformMobile          int     `db:"platform_mobile" json:"platform_mobile"`
	PlatformUnknown         int     `db:"platform_unknown" json:"platform_unknown"`
	RelativePlatformDesktop float64 `db:"relative_platform_desktop" json:"relative_platform_desktop"`
	RelativePlatformMobile  float64 `db:"relative_platform_mobile" json:"relative_platform_mobile"`
	RelativePlatformUnknown float64 `db:"relative_platform_unknown" json:"relative_platform_unknown"`
}

PlatformStats is the result type for platform statistics.

type ReferrerStats

type ReferrerStats struct {
	Referrer         string  `json:"referrer"`
	ReferrerName     string  `db:"referrer_name" json:"referrer_name"`
	ReferrerIcon     string  `db:"referrer_icon" json:"referrer_icon"`
	Visitors         int     `json:"visitors"`
	RelativeVisitors float64 `db:"relative_visitors" json:"relative_visitors"`
	Bounces          int     `json:"bounces"`
	BounceRate       float64 `db:"bounce_rate" json:"bounce_rate"`
}

ReferrerStats is the result type for referrer statistics.

type ScreenClassStats

type ScreenClassStats struct {
	MetaStats
	ScreenClass string `db:"screen_class" json:"screen_class"`
}

ScreenClassStats is the result type for screen class statistics.

type Session added in v2.6.0

type Session struct {
	Path    string
	Time    time.Time
	Session time.Time
}

Session represents a visitor session as it is returned by the Client from the database. This is not used for any actual statistic.

type SessionCache added in v2.6.0

type SessionCache struct {
	// contains filtered or unexported fields
}

SessionCache caches sessions.

func NewSessionCache added in v2.6.0

func NewSessionCache(client Store, maxSessions int) *SessionCache

NewSessionCache creates a new cache for given client and maximum size.

type Store

type Store interface {
	// SaveHits saves given hits.
	SaveHits([]Hit) error

	// SaveEvents saves given events.
	SaveEvents([]Event) error

	// Session returns the last path, time, and session timestamp for given client, fingerprint, and maximum age.
	Session(int64, string, time.Time) (Session, error)

	// Count returns the number of results for given query.
	Count(string, ...interface{}) (int, error)

	// Get returns a single result for given query.
	// The result must be a pointer.
	Get(interface{}, string, ...interface{}) error

	// Select returns the results for given query.
	// The results must be a pointer to a slice.
	Select(interface{}, string, ...interface{}) error
}

Store is the database storage interface.

type TimeSpentStats

type TimeSpentStats struct {
	Day                     time.Time `json:"day"`
	Path                    string    `json:"path"`
	Title                   string    `json:"title"`
	AverageTimeSpentSeconds int       `db:"average_time_spent_seconds" json:"average_time_spent_seconds"`
}

TimeSpentStats is the result type for average time spent statistics (sessions, time on page).

type Tracker

type Tracker struct {
	// contains filtered or unexported fields
}

Tracker provides methods to track requests (hits and events). Make sure you call Stop to make sure the hits get stored before shutting down the server.

func NewTracker

func NewTracker(client Store, salt string, config *TrackerConfig) *Tracker

NewTracker creates a new tracker for given client, salt and config. Pass nil for the config to use the defaults. The salt is mandatory. It creates the same amount of workers for both, hits and events.

func (*Tracker) Event added in v2.4.0

func (tracker *Tracker) Event(r *http.Request, eventOptions EventOptions, options *HitOptions)

Event stores the given request as a new event. The event name in the options must be set, or otherwise the request will be ignored. The request might be ignored if it meets certain conditions. The HitOptions, if passed, will overwrite the Tracker configuration. It's save (and recommended!) to call this function in its own goroutine.

func (*Tracker) Flush

func (tracker *Tracker) Flush()

Flush flushes all hits to client that are currently buffered by the workers. Call Tracker.Stop to also save hits that are in the queue.

func (*Tracker) Hit

func (tracker *Tracker) Hit(r *http.Request, options *HitOptions)

Hit stores the given request. The request might be ignored if it meets certain conditions. The HitOptions, if passed, will overwrite the Tracker configuration. It's save (and recommended!) to call this function in its own goroutine.

func (*Tracker) SetGeoDB

func (tracker *Tracker) SetGeoDB(geoDB *GeoDB)

SetGeoDB sets the GeoDB for the Tracker. The call to this function is thread safe to enable live updates of the database. Pass nil to disable the feature.

func (*Tracker) Stop

func (tracker *Tracker) Stop()

Stop flushes and stops all workers.

type TrackerConfig

type TrackerConfig struct {
	// Worker sets the number of workers that are used to client hits.
	// Must be greater or equal to 1.
	Worker int

	// WorkerBufferSize is the size of the buffer used to client hits.
	// Must be greater than 0. The hits are stored in batch when the buffer is full.
	WorkerBufferSize int

	// WorkerTimeout sets the timeout used to client hits.
	// This is used to allow the workers to client hits even if the buffer is not full yet.
	// It's recommended to set this to a few seconds.
	// If you leave it 0, the default timeout is used, else it is limited to 60 seconds.
	WorkerTimeout time.Duration

	// ReferrerDomainBlacklist see HitOptions.ReferrerDomainBlacklist.
	ReferrerDomainBlacklist []string

	// ReferrerDomainBlacklistIncludesSubdomains see HitOptions.ReferrerDomainBlacklistIncludesSubdomains.
	ReferrerDomainBlacklistIncludesSubdomains bool

	// MaxSessions sets the maximum size for the session cache.
	// If you leave it 0, the default will be used.
	MaxSessions int

	// SessionMaxAge see HitOptions.SessionMaxAge.
	SessionMaxAge time.Duration

	// GeoDB enables/disabled mapping IPs to country codes.
	// Can be set/updated at runtime by calling Tracker.SetGeoDB.
	GeoDB *GeoDB

	// Logger is the log.Logger used for logging.
	// The default log will be used printing to os.Stdout with "pirsch" in its prefix in case it is not set.
	Logger *log.Logger
}

TrackerConfig is the optional configuration for the Tracker.

type UTMCampaignStats

type UTMCampaignStats struct {
	MetaStats
	UTMCampaign string `db:"utm_campaign" json:"utm_campaign"`
}

UTMCampaignStats is the result type for utm campaign statistics.

type UTMContentStats

type UTMContentStats struct {
	MetaStats
	UTMContent string `db:"utm_content" json:"utm_content"`
}

UTMContentStats is the result type for utm content statistics.

type UTMMediumStats

type UTMMediumStats struct {
	MetaStats
	UTMMedium string `db:"utm_medium" json:"utm_medium"`
}

UTMMediumStats is the result type for utm medium statistics.

type UTMSourceStats

type UTMSourceStats struct {
	MetaStats
	UTMSource string `db:"utm_source" json:"utm_source"`
}

UTMSourceStats is the result type for utm source statistics.

type UTMTermStats

type UTMTermStats struct {
	MetaStats
	UTMTerm string `db:"utm_term" json:"utm_term"`
}

UTMTermStats is the result type for utm term statistics.

type UserAgent

type UserAgent struct {
	// Browser is the browser name.
	Browser string

	// BrowserVersion is the browser (non technical) version number.
	BrowserVersion string

	// OS is the operating system.
	OS string

	// OSVersion is the operating system version number.
	OSVersion string
}

UserAgent contains information extracted from the User-Agent header.

func ParseUserAgent

func ParseUserAgent(ua string) UserAgent

ParseUserAgent parses given User-Agent header and returns the extracted information. This just supports major browsers and operating systems, we don't care about browsers and OSes that have no market share, unless you prove us wrong.

func (*UserAgent) IsDesktop

func (ua *UserAgent) IsDesktop() bool

IsDesktop returns true if the user agent is a desktop device.

func (*UserAgent) IsMobile

func (ua *UserAgent) IsMobile() bool

IsMobile returns true if the user agent is a mobile device.

type VisitorHourStats

type VisitorHourStats struct {
	Hour     int `json:"hour"`
	Visitors int `json:"visitors"`
}

VisitorHourStats is the result type for visitor statistics grouped by time of day.

type VisitorStats

type VisitorStats struct {
	Day        time.Time `json:"day"`
	Visitors   int       `json:"visitors"`
	Views      int       `json:"views"`
	Sessions   int       `json:"sessions"`
	Bounces    int       `json:"bounces"`
	BounceRate float64   `db:"bounce_rate" json:"bounce_rate"`
}

VisitorStats is the result type for visitor statistics.

Directories

Path Synopsis
demos

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL