diff --git a/cmd/config_job_prometheus.go b/cmd/config_job_prometheus.go index 662a924..512d01b 100644 --- a/cmd/config_job_prometheus.go +++ b/cmd/config_job_prometheus.go @@ -12,6 +12,7 @@ import ( ) type PrometheusJob struct { + Name string Listen string } @@ -55,12 +56,10 @@ func parsePrometheusJob(c JobParsingContext, name string, i map[string]interface if s.Listen == "" { return nil, errors.New("must specify 'listen' attribute") } - return &PrometheusJob{s.Listen}, nil + return &PrometheusJob{name, s.Listen}, nil } -func (*PrometheusJob) JobName() string { - return "prometheus" -} +func (j *PrometheusJob) JobName() string { return j.Name } func (j *PrometheusJob) JobType() JobType { return JobTypePrometheus } diff --git a/cmd/config_parse.go b/cmd/config_parse.go index ddbbd60..3b913d4 100644 --- a/cmd/config_parse.go +++ b/cmd/config_parse.go @@ -93,9 +93,30 @@ func parseConfig(i interface{}) (c *Config, err error) { cpc := ConfigParsingContext{&c.Global} jpc := JobParsingContext{cpc} - - // Jobs c.Jobs = make(map[string]Job, len(asMap.Jobs)) + + // FIXME internal jobs should not be mixed with user jobs + // Monitoring Jobs + var monJobs []map[string]interface{} + if err := mapstructure.Decode(asMap.Global["monitoring"], &monJobs); err != nil { + return nil, errors.Wrap(err, "cannot parse monitoring section") + } + for i, jc := range monJobs { + if jc["name"] == "" || jc["name"] == nil { + // FIXME internal jobs should not require a name... + jc["name"] = fmt.Sprintf("prometheus-%d", i) + } + job, err := parseJob(jpc, jc) + if err != nil { + return nil, errors.Wrapf(err, "cannot parse monitoring job #%d", i) + } + if job.JobType() != JobTypePrometheus { + return nil, errors.Errorf("monitoring job #%d has invalid job type", i) + } + c.Jobs[job.JobName()] = job + } + + // Regular Jobs for i := range asMap.Jobs { job, err := parseJob(jpc, asMap.Jobs[i]) if err != nil { @@ -109,7 +130,7 @@ func parseConfig(i interface{}) (c *Config, err error) { } jn := job.JobName() if _, ok := c.Jobs[jn]; ok { - err = errors.Errorf("duplicate job name: %s", jn) + err = errors.Errorf("duplicate or invalid job name: %s", jn) return nil, err } c.Jobs[job.JobName()] = job diff --git a/cmd/config_test.go b/cmd/config_test.go index dbbf439..5f666a1 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -17,7 +17,7 @@ func TestSampleConfigsAreParsedWithoutErrors(t *testing.T) { "./sampleconf/pullbackup/backuphost.yml", "./sampleconf/pullbackup/productionhost.yml", "./sampleconf/random/debugging.yml", - "./sampleconf/random/logging.yml", + "./sampleconf/random/logging_and_monitoring.yml", } for _, p := range paths { diff --git a/cmd/sampleconf/random/logging.yml b/cmd/sampleconf/random/logging_and_monitoring.yml similarity index 88% rename from cmd/sampleconf/random/logging.yml rename to cmd/sampleconf/random/logging_and_monitoring.yml index 7981258..d739f99 100644 --- a/cmd/sampleconf/random/logging.yml +++ b/cmd/sampleconf/random/logging_and_monitoring.yml @@ -20,5 +20,9 @@ global: level: debug format: logfmt + monitoring: + + - type: prometheus + listen: ':9090' jobs: [] diff --git a/docs/changelog.rst b/docs/changelog.rst index ff30e74..744ae2e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,6 +10,11 @@ Changelog The changelog summarized bugfixes that are deemed relevant for users. Developers should consult the git commit log or GitHub issue tracker. +0.0.4 (unreleased) +------------------ + +* |feature| :issue:`67`: Expose `Prometheus `_ metrics via HTTP (:ref:`config docs `) + 0.0.3 ----- diff --git a/docs/configuration.rst b/docs/configuration.rst index 4e84184..98b7174 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -13,4 +13,5 @@ Configuration configuration/map_filter_syntax configuration/prune configuration/logging + configuration/monitoring configuration/misc diff --git a/docs/configuration/logging.rst b/docs/configuration/logging.rst index 22c2e3f..4be4991 100644 --- a/docs/configuration/logging.rst +++ b/docs/configuration/logging.rst @@ -8,7 +8,7 @@ Logging zrepl uses structured logging to provide users with easily processable log messages. Logging outlets are configured in the ``global`` section of the |mainconfig|. -Check out :sampleconf:`random/logging.yml` for an example on how to configure multiple outlets: +Check out :sampleconf:`random/logging_and_monitoring.yml` for an example on how to configure multiple outlets: :: diff --git a/docs/configuration/monitoring.rst b/docs/configuration/monitoring.rst new file mode 100644 index 0000000..ac213e8 --- /dev/null +++ b/docs/configuration/monitoring.rst @@ -0,0 +1,30 @@ +.. include:: ../global.rst.inc + +.. _monitoring: + +Monitoring +========== + +Monitoring endpoints are configured in the ``global.monitoring`` section of the |mainconfig|. +Check out :sampleconf:`random/logging_and_monitoring.yml` for examples. + +.. _monitoring-prometheus: + +Prometheus +---------- + +zrepl can expose `Prometheus metrics `_ via HTTP. +The ``listen`` attribute is a `net.Listen `_ string for tcp, e.g. ``:9091`` or ``127.0.0.1:9091``. + +The Prometheues monitoring job appears in the ``zrepl control`` job list and may be specified **at most once**. +There is no stability guarantee on the exported metrics. + +:: + + global: + monitoring: + - type: prometheus + listen: ':9091' + + + diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 49180c7..40192d8 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -193,6 +193,7 @@ Summary Congratulations, you have a working pull backup. Where to go next? * Read more about :ref:`configuration format, options & job types ` +* Configure :ref:`logging ` \& :ref:`monitoring `. * Learn about :ref:`implementation details ` of zrepl. diff --git a/docs/usage.rst b/docs/usage.rst index dba9729..3beb7ba 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -37,7 +37,7 @@ zrepl daemon ============ All actual work zrepl does is performed by a daemon process. -Logging is configurable via the config file. Please refer to the :ref:`logging documention `. +The daemon supports structured :ref:`logging ` and provides :ref:`monitoring endpoints `. When installating from a package, the package maintainer should have provided an init script / systemd.service file. You should thus be able to start zrepl daemon using your init system.