From f6d5ebf5136a291a89424e87e8f1d90d1416b0af Mon Sep 17 00:00:00 2001 From: Tom Wilkie <tom.wilkie@gmail.com> Date: Sun, 25 Nov 2018 12:06:28 +0000 Subject: [PATCH] Document how to get promtail running locally. Signed-off-by: Tom Wilkie <tom.wilkie@gmail.com> --- .gitignore | 1 + README.md | 88 ++++---------------- cmd/tempo/main.go | 2 +- docs/api.md | 70 ++++++++++++++++ docs/promtail-local-config.yaml | 9 ++ docs/{local.yaml => tempo-local-config.yaml} | 0 pkg/promtail/config.go | 40 +-------- pkg/promtail/position.go | 2 +- pkg/promtail/target.go | 9 +- pkg/promtail/targetmanager.go | 5 +- 10 files changed, 110 insertions(+), 116 deletions(-) create mode 100644 docs/api.md create mode 100644 docs/promtail-local-config.yaml rename docs/{local.yaml => tempo-local-config.yaml} (100%) diff --git a/.gitignore b/.gitignore index a3a41d54..b5c50e79 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ mixin/vendor/ cmd/tempo/tempo cmd/promtail/promtail /tempo +/promtail diff --git a/README.md b/README.md index 3b9fd8e6..eb22b059 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,25 @@ not index the contents of the logs, but rather a set of labels for each log stea ## Run it locally -Tempo can be run in a single host, no-dependencies mode using the following commands: +Tempo can be run in a single host, no-dependencies mode using the following commands. + +Tempo consists of 3 components; `tempo` is the main server, responsible for storing +logs and processing queries. `promtail` is the agent, responsible for gather logs +and sending them to tempo and `grafana` as the UI. + +To run tempo, use the following commands: ``` $ go build ./cmd/tempo -$ ./tempo -config.file=./docs/local.yaml +$ ./tempo -config.file=./docs/tempo-local-config.yaml +... +``` + +To run promtail, use the following commands: + +``` +$ go build ./cmd/promtail +$ ./promtail -config.file=./docs/promtail-local-config.yaml -positions.file=./positions.yaml -client.url=http://localhost/api/prom/push ... ``` @@ -89,73 +103,3 @@ Args: <query> eg '{foo="bar",baz="blip"}' [<regex>] ``` - -## API - -*nb* Authentication is out of scope for this project. You are expected to run an -authenticating reverse proxy in front of our services, such as an Nginx with basic -auth or a OAuth2 proxy. - -There are 4 API endpoints: - -- `POST /api/prom/push` - - For sending log entries, expects a snappy compresses proto in the HTTP Body. - -- `GET /api/prom/query` - - For doing queries, accepts the following paramters in the query-string: - - `query`: a logQL query - - `limit`: max number of entries to return - - `start`: the start time for the query, as a nanosecond Unix epoch (nanoseconds since 1970) - - `end`: the end time for the query, as a nanosecond Unix epoch (nanoseconds since 1970) - - `direction`: `forward` or `backward`, useful when specifying a limit - - `regexp`: a regex to filter the returned results, will eventually be rolled into the query language - - Responses looks like this: - ``` - { - "streams": [ - { - "labels": "{instance=\"...\", job=\"...\", namespace=\"...\"}", - "entries": [ - { - "timestamp": "2018-06-27T05:20:28.699492635Z", - "line": "..." - }, - ... - ] - }, - ... - ] - } - ``` - -- `GET /api/prom/label` - - For retrieving the names of the labels one can query on. - - Responses looks like this: - ``` - { - "values": [ - "instance", - "job", - ... - ] - } - ``` - -- `GET /api/prom/label/<name>/values` - For retrieving the label values one can query on. - - Responses looks like this: - ``` - { - "values": [ - "default", - "cortex-ops", - ... - ] - } - ``` diff --git a/cmd/tempo/main.go b/cmd/tempo/main.go index f980184b..8566f244 100644 --- a/cmd/tempo/main.go +++ b/cmd/tempo/main.go @@ -62,7 +62,7 @@ func readConfig(filename string, cfg *tempo.Config) error { return errors.Wrap(err, "Error reading config file: %v") } - if err := yaml.Unmarshal(buf, &cfg); err != nil { + if err := yaml.UnmarshalStrict(buf, &cfg); err != nil { return errors.Wrap(err, "Error reading config file: %v") } return nil diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 00000000..439af9f2 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,70 @@ + +## API + +*nb* Authentication is out of scope for this project. You are expected to run an +authenticating reverse proxy in front of our services, such as an Nginx with basic +auth or a OAuth2 proxy. + +There are 4 API endpoints: + +- `POST /api/prom/push` + + For sending log entries, expects a snappy compresses proto in the HTTP Body. + +- `GET /api/prom/query` + + For doing queries, accepts the following paramters in the query-string: + - `query`: a logQL query + - `limit`: max number of entries to return + - `start`: the start time for the query, as a nanosecond Unix epoch (nanoseconds since 1970) + - `end`: the end time for the query, as a nanosecond Unix epoch (nanoseconds since 1970) + - `direction`: `forward` or `backward`, useful when specifying a limit + - `regexp`: a regex to filter the returned results, will eventually be rolled into the query language + + Responses looks like this: + ``` + { + "streams": [ + { + "labels": "{instance=\"...\", job=\"...\", namespace=\"...\"}", + "entries": [ + { + "timestamp": "2018-06-27T05:20:28.699492635Z", + "line": "..." + }, + ... + ] + }, + ... + ] + } + ``` + +- `GET /api/prom/label` + + For retrieving the names of the labels one can query on. + + Responses looks like this: + ``` + { + "values": [ + "instance", + "job", + ... + ] + } + ``` + +- `GET /api/prom/label/<name>/values` + For retrieving the label values one can query on. + + Responses looks like this: + ``` + { + "values": [ + "default", + "cortex-ops", + ... + ] + } + ``` diff --git a/docs/promtail-local-config.yaml b/docs/promtail-local-config.yaml new file mode 100644 index 00000000..a083d0ca --- /dev/null +++ b/docs/promtail-local-config.yaml @@ -0,0 +1,9 @@ +scrape_configs: +- job_name: system + + static_configs: + - targets: + - localhost + labels: + job: system + __path__: /var/log/system.log diff --git a/docs/local.yaml b/docs/tempo-local-config.yaml similarity index 100% rename from docs/local.yaml rename to docs/tempo-local-config.yaml diff --git a/pkg/promtail/config.go b/pkg/promtail/config.go index 515afbb2..9c3f41c0 100644 --- a/pkg/promtail/config.go +++ b/pkg/promtail/config.go @@ -4,20 +4,17 @@ import ( "fmt" "io/ioutil" "path/filepath" - "strings" yaml "gopkg.in/yaml.v2" "github.com/prometheus/prometheus/config" sd_config "github.com/prometheus/prometheus/discovery/config" + "github.com/prometheus/prometheus/discovery/targetgroup" ) // Config for promtail, describing what files to watch. type Config struct { ScrapeConfig []ScrapeConfig `yaml:"scrape_configs,omitempty"` - - // Catches all undefined fields and must be empty after parsing. - XXX map[string]interface{} `yaml:",inline"` } // LoadConfig loads config from a file. @@ -28,34 +25,19 @@ func LoadConfig(filename string) (*Config, error) { } var cfg Config - if err := yaml.Unmarshal(buf, &cfg); err != nil { + if err := yaml.UnmarshalStrict(buf, &cfg); err != nil { return nil, err } return &cfg, nil } -// UnmarshalYAML implements the yaml.Unmarshaler interface. -func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { - type plain Config - err := unmarshal((*plain)(c)) - if err != nil { - return err - } - if err = checkOverflow(c.XXX, "config"); err != nil { - return err - } - return nil -} - // ScrapeConfig describes a job to scrape. type ScrapeConfig struct { JobName string `yaml:"job_name,omitempty"` - ServiceDiscoveryConfig sd_config.ServiceDiscoveryConfig `yaml:",inline"` RelabelConfigs []*config.RelabelConfig `yaml:"relabel_configs,omitempty"` - - // Catches all undefined fields and must be empty after parsing. - XXX map[string]interface{} `yaml:",inline"` + ServiceDiscoveryConfig sd_config.ServiceDiscoveryConfig `yaml:",inline"` + StaticConfig targetgroup.Group `yaml:"static_config"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. @@ -65,22 +47,8 @@ func (c *ScrapeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err != nil { return err } - if err = checkOverflow(c.XXX, "scrape_config"); err != nil { - return err - } if len(c.JobName) == 0 { return fmt.Errorf("job_name is empty") } return nil } - -func checkOverflow(m map[string]interface{}, ctx string) error { - if len(m) > 0 { - var keys []string - for k := range m { - keys = append(keys, k) - } - return fmt.Errorf("unknown fields in %s: %s", ctx, strings.Join(keys, ", ")) - } - return nil -} diff --git a/pkg/promtail/position.go b/pkg/promtail/position.go index 3c911927..225de730 100644 --- a/pkg/promtail/position.go +++ b/pkg/promtail/position.go @@ -111,7 +111,7 @@ func readPositionsFile(filename string) (map[string]int64, error) { } var p positionsFile - if err := yaml.Unmarshal(buf, &p); err != nil { + if err := yaml.UnmarshalStrict(buf, &p); err != nil { return nil, err } diff --git a/pkg/promtail/target.go b/pkg/promtail/target.go index 9f2cf5df..2ef0ce0e 100644 --- a/pkg/promtail/target.go +++ b/pkg/promtail/target.go @@ -8,6 +8,7 @@ import ( "github.com/grafana/tempo/pkg/helpers" "github.com/hpcloud/tail" + "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" log "github.com/sirupsen/logrus" @@ -41,16 +42,14 @@ type Target struct { // NewTarget create a new Target. func NewTarget(c *Client, positions *Positions, path string, labels model.LabelSet) (*Target, error) { - log.Info("newTarget", labels) - watcher, err := fsnotify.NewWatcher() if err != nil { - return nil, err + return nil, errors.Wrap(err, "fsnotify.NewWatcher") } if err := watcher.Add(path); err != nil { helpers.LogError("closing watcher", watcher.Close) - return nil, err + return nil, errors.Wrap(err, "watcher.Add") } t := &Target{ @@ -66,7 +65,7 @@ func NewTarget(c *Client, positions *Positions, path string, labels model.LabelS // Fist, we're going to add all the existing files fis, err := ioutil.ReadDir(t.path) if err != nil { - return nil, err + return nil, errors.Wrap(err, "ioutil.ReadDir") } for _, fi := range fis { if fi.IsDir() { diff --git a/pkg/promtail/targetmanager.go b/pkg/promtail/targetmanager.go index a261b3b4..c5b39bc2 100644 --- a/pkg/promtail/targetmanager.go +++ b/pkg/promtail/targetmanager.go @@ -61,7 +61,6 @@ func NewTargetManager( } config := map[string]sd_config.ServiceDiscoveryConfig{} - for _, cfg := range scrapeConfig { s := &syncer{ log: logger, @@ -75,6 +74,7 @@ func NewTargetManager( } go tm.run() + go tm.manager.Run() return tm, tm.manager.ApplyConfig(config) } @@ -109,10 +109,13 @@ func (s *syncer) Sync(groups []*targetgroup.Group) { for _, group := range groups { for _, t := range group.Targets { + level.Debug(s.log).Log("msg", "new target", "labels", t) + labels := group.Labels.Merge(t) labels = relabel.Process(labels, s.relabelConfig...) // Drop empty targets (drop in relabeling). if labels == nil { + level.Debug(s.log).Log("msg", "dropping target, no labels") continue } -- GitLab