Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stats collection. #2447

Merged
merged 2 commits into from
Nov 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions cmd/traefik/traefik.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/containous/flaeg"
"github.com/containous/staert"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/collector"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
Expand Down Expand Up @@ -149,6 +150,8 @@ func run(globalConfiguration *configuration.GlobalConfiguration, configFile stri
checkNewVersion()
}

stats(globalConfiguration)

log.Debugf("Global configuration loaded %s", string(jsonConf))
svr := server.NewServer(*globalConfiguration)
svr.Start()
Expand Down Expand Up @@ -244,14 +247,38 @@ func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
}

func checkNewVersion() {
ticker := time.NewTicker(24 * time.Hour)
ticker := time.Tick(24 * time.Hour)
safe.Go(func() {
for time.Sleep(10 * time.Minute); ; <-ticker {
version.CheckNewVersion()
}
})
}

func stats(globalConfiguration *configuration.GlobalConfiguration) {
if globalConfiguration.SendAnonymousUsage {
log.Info(`
Stats collection is enabled.
Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.
Help us improve Traefik by leaving this feature on :)
More details on: https://docs.traefik.io/basic/#collected-data
`)
collect(globalConfiguration)
} else {
log.Info(`
Stats collection is disabled.
Help us improve Traefik by turning this feature on :)
More details on: https://docs.traefik.io/basic/#collected-data
`)
}
}

func collect(globalConfiguration *configuration.GlobalConfiguration) {
ticker := time.Tick(24 * time.Hour)
safe.Go(func() {
time.Sleep(10 * time.Minute)
version.CheckNewVersion()
for {
select {
case <-ticker.C:
version.CheckNewVersion()
for time.Sleep(10 * time.Minute); ; <-ticker {
if err := collector.Collect(globalConfiguration); err != nil {
log.Debug(err)
}
}
})
Expand Down
79 changes: 79 additions & 0 deletions collector/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package collector

import (
"bytes"
"encoding/base64"
"encoding/json"
"net"
"net/http"
"strconv"
"time"

"github.com/containous/traefik/cmd/traefik/anonymize"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 why anonymize is under cmd/traefik package ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or why collector is in a different parent package that anonimize ?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because before this PR it was use only by traefik bug command.

I can move the package anonymize at the root of the project.

"github.com/containous/traefik/configuration"
"github.com/containous/traefik/log"
"github.com/containous/traefik/version"
"github.com/mitchellh/hashstructure"
)

// collectorURL URL where the stats are send
const collectorURL = "https://collect.traefik.io/619df80498b60f985d766ce62f912b7c"

// Collected data
type data struct {
Version string
Codename string
BuildDate string
Configuration string
Hash string
}

// Collect anonymous data.
func Collect(globalConfiguration *configuration.GlobalConfiguration) error {
anonConfig, err := anonymize.Do(globalConfiguration, false)
if err != nil {
return err
}

log.Infof("Anonymous stats sent to %s: %s", collectorURL, anonConfig)

hashConf, err := hashstructure.Hash(globalConfiguration, nil)
if err != nil {
return err
}

data := &data{
Version: version.Version,
Codename: version.Codename,
BuildDate: version.BuildDate,
Hash: strconv.FormatUint(hashConf, 10),
Configuration: base64.StdEncoding.EncodeToString([]byte(anonConfig)),
}

buf := new(bytes.Buffer)
err = json.NewEncoder(buf).Encode(data)
if err != nil {
return err
}

_, err = makeHTTPClient().Post(collectorURL, "application/json; charset=utf-8", buf)
return err
}

func makeHTTPClient() *http.Client {
dialer := &net.Dialer{
Timeout: configuration.DefaultDialTimeout,
KeepAlive: 30 * time.Second,
DualStack: true,
}

transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialer.DialContext,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}

return &http.Client{Transport: transport}
}
1 change: 1 addition & 0 deletions configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type GlobalConfiguration struct {
GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"`
AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated
Expand Down
119 changes: 119 additions & 0 deletions docs/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -628,3 +628,122 @@ traefik healthcheck
```bash
OK: http://:8082/ping
```


## Collected Data

**This feature is disabled by default.**

You can read the public proposal on this topic [here](https://github.com/containous/traefik/issues/2369).

### Why ?

In order to help us learn more about how Træfik is being used and improve it, we collect anonymous usage statistics from running instances.
Those data help us prioritize our developments and focus on what's more important (for example, which configuration backend is used and which is not used).

### What ?

Once a day (the first call begins 10 minutes after the start of Træfik), we collect:
- the Træfik version
- a hash of the configuration
- an **anonymous version** of the static configuration:
- token, user name, password, URL, IP, domain, email, etc, are removed

!!! note
We do not collect the dynamic configuration (frontends & backends).

!!! note
We do not collect data behind the scenes to run advertising programs or to sell such data to third-party.

#### Here is an example

- Source configuration:

```toml
[entryPoints]
[entryPoints.http]
address = ":80"

[web]
address = ":8080"

[Docker]
endpoint = "tcp://10.10.10.10:2375"
domain = "foo.bir"
exposedByDefault = true
swarmMode = true

[Docker.TLS]
CA = "dockerCA"
Cert = "dockerCert"
Key = "dockerKey"
InsecureSkipVerify = true

[ECS]
Domain = "foo.bar"
ExposedByDefault = true
Clusters = ["foo-bar"]
Region = "us-west-2"
AccessKeyID = "AccessKeyID"
SecretAccessKey = "SecretAccessKey"
```

- Obfuscated and anonymous configuration:

```toml
[entryPoints]
[entryPoints.http]
address = ":80"

[web]
address = ":8080"

[Docker]
Endpoint = "xxxx"
Domain = "xxxx"
ExposedByDefault = true
SwarmMode = true

[Docker.TLS]
CA = "xxxx"
Cert = "xxxx"
Key = "xxxx"
InsecureSkipVerify = false

[ECS]
Domain = "xxxx"
ExposedByDefault = true
Clusters = []
Region = "us-west-2"
AccessKeyID = "xxxx"
SecretAccessKey = "xxxx"
```

### Show me the code !

If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/containous/traefik/blob/master/collector/collector.go)

By default we anonymize all configuration fields, except fields tagged with `export=true`.

You can check all fields in the [godoc](https://godoc.org/github.com/containous/traefik/configuration#GlobalConfiguration).

### How to enable this ?

You can enable the collecting system by:

- adding this line in the configuration TOML file:

```toml
# Send anonymous usage data
#
# Optional
# Default: false
#
sendAnonymousUsage = true
```

- adding this flag in the CLI:

```bash
./traefik --sendAnonymousUsage=true
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to note that in the future, that this will be enabled by default.

6 changes: 4 additions & 2 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ import:
- package: github.com/armon/go-proxyproto
version: 48572f11356f1843b694f21a290d4f1006bc5e47
- package: github.com/mitchellh/copystructure
- package: github.com/mitchellh/hashstructure
testImport:
- package: github.com/stvp/go-udp-testing
- package: github.com/docker/libcompose
Expand Down
21 changes: 21 additions & 0 deletions vendor/github.com/mitchellh/hashstructure/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading