diff --git a/.dockerignore b/.dockerignore index a738446..681a7a4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,3 @@ /vendor/ -/pagerduty-elasticsearch-exporter +/pagerduty2es *.exe diff --git a/.gitignore b/.gitignore index e0a0731..681a7a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /vendor/ -/pagerduty2elasticsearch-exporter +/pagerduty2es *.exe diff --git a/Dockerfile b/Dockerfile index 38f6c7d..5f177cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,23 @@ FROM golang:1.15 as build -WORKDIR /go/src/github.com/webdevops/pagerduty2elasticsearch-exporter +WORKDIR /go/src/github.com/webdevops/pagerduty2es # Get deps (cached) -COPY ./go.mod /go/src/github.com/webdevops/pagerduty2elasticsearch-exporter -COPY ./go.sum /go/src/github.com/webdevops/pagerduty2elasticsearch-exporter -COPY ./Makefile /go/src/github.com/webdevops/alertmanager2es +COPY ./go.mod /go/src/github.com/webdevops/pagerduty2es +COPY ./go.sum /go/src/github.com/webdevops/pagerduty2es +COPY ./Makefile /go/src/github.com/webdevops/pagerduty2es RUN make dependencies # Compile -COPY ./ /go/src/github.com/webdevops/pagerduty2elasticsearch-exporter +COPY ./ /go/src/github.com/webdevops/pagerduty2es RUN make lint RUN make build -RUN ./pagerduty2elasticsearch-exporter --help +RUN ./pagerduty2es --help ############################################# # FINAL IMAGE ############################################# FROM gcr.io/distroless/static -COPY --from=build /go/src/github.com/webdevops/pagerduty2elasticsearch-exporter/pagerduty2elasticsearch-exporter / +COPY --from=build /go/src/github.com/webdevops/pagerduty2es/pagerduty2es / USER 1000 -ENTRYPOINT ["/pagerduty2elasticsearch-exporter"] +ENTRYPOINT ["/pagerduty2es"] diff --git a/Makefile b/Makefile index 1fb498a..3360020 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: all build clean image check vendor dependencies -NAME := pagerduty2elasticsearch-exporter +NAME := pagerduty2es GIT_TAG := $(shell git describe --dirty --tags --always) GIT_COMMIT := $(shell git rev-parse --short HEAD) LDFLAGS := -X "main.gitTag=$(GIT_TAG)" -X "main.gitCommit=$(GIT_COMMIT)" -extldflags "-static" diff --git a/README.md b/README.md index e1d9bad..892a781 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -PagerDuty2Elasticsearch exporter -================================ +PagerDuty2es (elasticsearch) exporter +===================================== -[![license](https://img.shields.io/github/license/webdevops/pagerduty2elasticsearch-exporter.svg)](https://github.com/webdevops/pagerduty2elasticsearch-exporter/blob/master/LICENSE) -[![Docker](https://img.shields.io/badge/docker-webdevops%2Fpagerduty--exporter-blue.svg?longCache=true&style=flat&logo=docker)](https://hub.docker.com/r/webdevops/pagerduty2elasticsearch-exporter/) -[![Docker Build Status](https://img.shields.io/docker/build/webdevops/pagerduty2elasticsearch-exporter.svg)](https://hub.docker.com/r/webdevops/pagerduty2elasticsearch-exporter/) +[![license](https://img.shields.io/github/license/webdevops/pagerduty2es.svg)](https://github.com/webdevops/pagerduty2es/blob/master/LICENSE) +[![Docker](https://img.shields.io/docker/cloud/automated/webdevops/pagerduty2es)](https://hub.docker.com/r/webdevops/pagerduty2es/) +[![Docker Build Status](https://img.shields.io/docker/cloud/build/webdevops/pagerduty2es)](https://hub.docker.com/r/webdevops/pagerduty2es/) Exporter for incidents and logentries from PagerDuty to ElasticSearch @@ -12,23 +12,30 @@ Configuration ``` Usage: - pagerduty2elasticsearch-exporter [OPTIONS] + pagerduty2es [OPTIONS] Application Options: + --debug debug mode [$DEBUG] -v, --verbose verbose mode [$VERBOSE] - --bind= Server address (default: :8080) [$SERVER_BIND] - --scrape-time= Scrape time (time.duration) (default: 5m) [$SCRAPE_TIME] + --log.json Switch log output to json format [$LOG_JSON] --pagerduty.authtoken= PagerDuty auth token [$PAGERDUTY_AUTH_TOKEN] --pagerduty.date-range= PagerDuty date range (default: 168h) [$PAGERDUTY_DATE_RANGE] - --pagerduty.max-connections= Maximum numbers of TCP connections to PagerDuty API (concurrency) (default: 4) [$PAGERDUTY_MAX_CONNECTIONS] + --pagerduty.max-connections= Maximum numbers of TCP connections to PagerDuty API (concurrency) (default: 4) + [$PAGERDUTY_MAX_CONNECTIONS] --elasticsearch.address= ElasticSearch urls [$ELASTICSEARCH_ADDRESS] --elasticsearch.username= ElasticSearch username for HTTP Basic Authentication [$ELASTICSEARCH_USERNAME] - --elasticsearch.password= ElasticSearch password for HTTP Basic Authenticatio [$ELASTICSEARCH_PASSWORD] - --elasticsearch.apikey= ElasticSearch base64-encoded token for authorization; if set, overrides username and password [$ELASTICSEARCH_APIKEY] - --elasticsearch.index= ElasticSearch index name (placeholders: %y for year, %m for month and %d for day) (default: pagerduty) [$ELASTICSEARCH_INDEX] - --elasticsearch.batch-count= Number of documents which should be indexed in one request (default: 50) [$ELASTICSEARCH_BATCH_COUNT] + --elasticsearch.password= ElasticSearch password for HTTP Basic Authentication [$ELASTICSEARCH_PASSWORD] + --elasticsearch.apikey= ElasticSearch base64-encoded token for authorization; if set, overrides username and + password [$ELASTICSEARCH_APIKEY] + --elasticsearch.index= ElasticSearch index name (placeholders: %y for year, %m for month and %d for day) + (default: pagerduty) [$ELASTICSEARCH_INDEX] + --elasticsearch.batch-count= Number of documents which should be indexed in one request (default: 50) + [$ELASTICSEARCH_BATCH_COUNT] --elasticsearch.retry-count= ElasticSearch request retry count (default: 5) [$ELASTICSEARCH_RETRY_COUNT] - --elasticsearch.retry-delay= ElasticSearch request delay for reach retry (default: 5s) [$ELASTICSEARCH_RETRY_DELAY] + --elasticsearch.retry-delay= ElasticSearch request delay for reach retry (default: 5s) + [$ELASTICSEARCH_RETRY_DELAY] + --bind= Server address (default: :8080) [$SERVER_BIND] + --scrape-time= Scrape time (time.duration) (default: 5m) [$SCRAPE_TIME] Help Options: -h, --help Show this help message diff --git a/config/opts.go b/config/opts.go new file mode 100644 index 0000000..23c0011 --- /dev/null +++ b/config/opts.go @@ -0,0 +1,49 @@ +package config + +import ( + "encoding/json" + log "github.com/sirupsen/logrus" + "time" +) + +type ( + Opts struct { + // logger + Logger struct { + Debug bool ` long:"debug" env:"DEBUG" description:"debug mode"` + Verbose bool `short:"v" long:"verbose" env:"VERBOSE" description:"verbose mode"` + LogJson bool ` long:"log.json" env:"LOG_JSON" description:"Switch log output to json format"` + } + + // PagerDuty settings + PagerDuty struct { + AuthToken string `long:"pagerduty.authtoken" env:"PAGERDUTY_AUTH_TOKEN" description:"PagerDuty auth token" required:"true" json:"-"` + Since time.Duration `long:"pagerduty.date-range" env:"PAGERDUTY_DATE_RANGE" description:"PagerDuty date range" default:"168h"` + MaxConnections int `long:"pagerduty.max-connections" env:"PAGERDUTY_MAX_CONNECTIONS" description:"Maximum numbers of TCP connections to PagerDuty API (concurrency)" default:"4"` + } + + // ElasticSearch settings + Elasticsearch struct { + Addresses []string `long:"elasticsearch.address" env:"ELASTICSEARCH_ADDRESS" delim:" " description:"ElasticSearch urls" required:"true"` + Username string `long:"elasticsearch.username" env:"ELASTICSEARCH_USERNAME" description:"ElasticSearch username for HTTP Basic Authentication"` + Password string `long:"elasticsearch.password" env:"ELASTICSEARCH_PASSWORD" description:"ElasticSearch password for HTTP Basic Authentication" json:"-"` + ApiKey string `long:"elasticsearch.apikey" env:"ELASTICSEARCH_APIKEY" description:"ElasticSearch base64-encoded token for authorization; if set, overrides username and password" json:"-"` + Index string `long:"elasticsearch.index" env:"ELASTICSEARCH_INDEX" description:"ElasticSearch index name (placeholders: %y for year, %m for month and %d for day)" default:"pagerduty"` + BatchCount int `long:"elasticsearch.batch-count" env:"ELASTICSEARCH_BATCH_COUNT" description:"Number of documents which should be indexed in one request" default:"50"` + RetryCount int `long:"elasticsearch.retry-count" env:"ELASTICSEARCH_RETRY_COUNT" description:"ElasticSearch request retry count" default:"5"` + RetryDelay time.Duration `long:"elasticsearch.retry-delay" env:"ELASTICSEARCH_RETRY_DELAY" description:"ElasticSearch request delay for reach retry" default:"5s"` + } + + // general options + ServerBind string `long:"bind" env:"SERVER_BIND" description:"Server address" default:":8080"` + ScrapeTime time.Duration `long:"scrape-time" env:"SCRAPE_TIME" description:"Scrape time (time.duration)" default:"5m"` + } +) + +func (o *Opts) GetJson() []byte { + jsonBytes, err := json.Marshal(o) + if err != nil { + log.Panic(err) + } + return jsonBytes +} diff --git a/exporter.go b/exporter.go index 523f24a..f3617b7 100644 --- a/exporter.go +++ b/exporter.go @@ -8,6 +8,7 @@ import ( elasticsearch "github.com/elastic/go-elasticsearch/v7" esapi "github.com/elastic/go-elasticsearch/v7/esapi" "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" "net/http" "strings" "sync" @@ -107,7 +108,7 @@ func (e *PagerdutyElasticsearchExporter) SetScrapeTime(value time.Duration) { } func (e *PagerdutyElasticsearchExporter) ConnectPagerduty(token string, httpClient *http.Client) { - e.pagerdutyClient = pagerduty.NewClient(opts.PagerDutyAuthToken) + e.pagerdutyClient = pagerduty.NewClient(token) e.pagerdutyClient.HTTPClient = httpClient } @@ -130,7 +131,7 @@ func (e *PagerdutyElasticsearchExporter) ConnectElasticsearch(cfg elasticsearch. if tries >= 5 { panic(err) } else { - daemonLogger.Info("Failed to connect to ES, retry...") + log.Info("failed to connect to ES, retry...") time.Sleep(5 * time.Second) continue } @@ -165,13 +166,13 @@ func (e *PagerdutyElasticsearchExporter) RunDaemon() { } func (e *PagerdutyElasticsearchExporter) sleepUntilNextCollection() { - daemonLogger.Verbosef("sleeping %v", e.scrapeTime) + log.Debugf("sleeping %v", e.scrapeTime) time.Sleep(*e.scrapeTime) } func (e *PagerdutyElasticsearchExporter) runScrape() { var wgProcess sync.WaitGroup - daemonLogger.Verbosef("Starting scraping") + log.Info("starting scrape") since := time.Now().Add(-*e.pagerdutyDateRange).Format(time.RFC3339) listOpts := pagerduty.ListIncidentsOptions{ @@ -216,7 +217,7 @@ func (e *PagerdutyElasticsearchExporter) runScrape() { incident.Id = incident.ID } - daemonLogger.Verbosef(" - Incident %v", incident.Id) + log.Debugf(" - incident %v", incident.Id) e.indexIncident(incident, esIndexRequestChannel) listLogOpts := pagerduty.ListIncidentLogEntriesOptions{} @@ -226,7 +227,7 @@ func (e *PagerdutyElasticsearchExporter) runScrape() { } for _, logEntry := range incidentLogResponse.LogEntries { - daemonLogger.Verbosef(" - LogEntry %v", logEntry.ID) + log.Debugf(" - logEntry %v", logEntry.ID) e.indexIncidentLogEntry(incident, logEntry, esIndexRequestChannel) } } @@ -242,7 +243,7 @@ func (e *PagerdutyElasticsearchExporter) runScrape() { duration := time.Now().Sub(startTime) e.prometheus.duration.WithLabelValues().Set(duration.Seconds()) - daemonLogger.Verbosef("processing took %v", duration.String()) + log.WithField("duration", duration.String()).Info("finished scraping") } func (e *PagerdutyElasticsearchExporter) indexIncident(incident pagerduty.Incident, callback chan<- *esapi.IndexRequest) { @@ -357,11 +358,11 @@ func (e *PagerdutyElasticsearchExporter) doESIndexRequestBulk(bulkRequests []*es } if resp != nil { - daemonLogger.Errorf("Unexpected HTTP %v response: %v", resp.StatusCode, resp.String()) + log.Errorf("Unexpected HTTP %v response: %v", resp.StatusCode, resp.String()) } // got an error - daemonLogger.Errorf("Retrying ES index error: %v", err) + log.Errorf("Retrying ES index error: %v", err) e.prometheus.esRequestRetries.WithLabelValues().Inc() // wait until retry diff --git a/go.mod b/go.mod index f5f7c77..a865398 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,13 @@ -module github.com/webdevops/pagerduty2elasticsearch-exporter +module github.com/webdevops/pagerduty2es go 1.15 require ( github.com/PagerDuty/go-pagerduty v1.2.0 github.com/elastic/go-elasticsearch/v7 v7.8.0 - github.com/google/logger v1.1.0 github.com/jessevdk/go-flags v1.4.0 github.com/prometheus/client_golang v1.7.0 + github.com/sirupsen/logrus v1.6.0 golang.org/x/sys v0.0.0-20200620081246-981b61492c35 // indirect google.golang.org/protobuf v1.24.0 // indirect ) diff --git a/go.sum b/go.sum index 430f388..8870c8f 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,7 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elastic/go-elasticsearch/v7 v7.8.0 h1:M9D55OK13IEgg51Jb57mZgseag1AsncwAUn4C6j1vlc= github.com/elastic/go-elasticsearch/v7 v7.8.0/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4= @@ -51,8 +52,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/logger v1.1.0 h1:saB74Etb4EAJNH3z74CVbCKk75hld/8T0CsXKetWCwM= -github.com/google/logger v1.1.0/go.mod h1:w7O8nrRr0xufejBlQMI83MXqRusvREoJdaAxV+CoAB4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= @@ -60,10 +59,15 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -78,6 +82,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -97,11 +102,15 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -159,10 +168,12 @@ google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEG google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/logger.go b/logger.go deleted file mode 100644 index df60f53..0000000 --- a/logger.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "fmt" - "github.com/google/logger" - "io/ioutil" -) - -type DaemonLogger struct { - *logger.Logger - verbose bool -} - -func NewLogger(flags int, verbose bool) *DaemonLogger { - instance := &DaemonLogger{ - Logger: logger.Init("verbose", true, false, ioutil.Discard), - verbose: verbose, - } - logger.SetFlags(flags) - - return instance -} - -func (l *DaemonLogger) Verbosef(message string, sprintf ...interface{}) { - if l.verbose { - if len(sprintf) > 0 { - message = fmt.Sprintf(message, sprintf...) - } - - l.InfoDepth(1, message) - } -} - -func (l *DaemonLogger) VerboseErrorf(message string, sprintf ...interface{}) { - if l.verbose { - if len(sprintf) > 0 { - message = fmt.Sprintf(message, sprintf...) - } - - l.ErrorDepth(1, message) - } -} diff --git a/main.go b/main.go index 8eb09f8..81ec321 100644 --- a/main.go +++ b/main.go @@ -5,11 +5,14 @@ import ( elasticsearch "github.com/elastic/go-elasticsearch/v7" "github.com/jessevdk/go-flags" "github.com/prometheus/client_golang/prometheus/promhttp" - "log" + log "github.com/sirupsen/logrus" + "github.com/webdevops/pagerduty2es/config" "net" "net/http" "os" + "path" "runtime" + "strings" "time" ) @@ -21,58 +24,27 @@ const ( ) var ( - argparser *flags.Parser - verbose bool - daemonLogger *DaemonLogger + argparser *flags.Parser + opts config.Opts // Git version information gitCommit = "" gitTag = "" ) -var opts struct { - // general settings - Verbose []bool `long:"verbose" short:"v" env:"VERBOSE" description:"verbose mode"` - - // server settings - ServerBind string `long:"bind" env:"SERVER_BIND" description:"Server address" default:":8080"` - ScrapeTime time.Duration `long:"scrape-time" env:"SCRAPE_TIME" description:"Scrape time (time.duration)" default:"5m"` - - // PagerDuty settings - PagerDutyAuthToken string `long:"pagerduty.authtoken" env:"PAGERDUTY_AUTH_TOKEN" description:"PagerDuty auth token" required:"true"` - PagerDutySince time.Duration `long:"pagerduty.date-range" env:"PAGERDUTY_DATE_RANGE" description:"PagerDuty date range" default:"168h"` - PagerDutyMaxConnections int `long:"pagerduty.max-connections" env:"PAGERDUTY_MAX_CONNECTIONS" description:"Maximum numbers of TCP connections to PagerDuty API (concurrency)" default:"4"` - - // ElasticSearch settings - ElasticsearchAddresses []string `long:"elasticsearch.address" env:"ELASTICSEARCH_ADDRESS" delim:" " description:"ElasticSearch urls" required:"true"` - ElasticsearchUsername string `long:"elasticsearch.username" env:"ELASTICSEARCH_USERNAME" description:"ElasticSearch username for HTTP Basic Authentication"` - ElasticsearchPassword string `long:"elasticsearch.password" env:"ELASTICSEARCH_PASSWORD" description:"ElasticSearch password for HTTP Basic Authenticatio"` - ElasticsearchApiKey string `long:"elasticsearch.apikey" env:"ELASTICSEARCH_APIKEY" description:"ElasticSearch base64-encoded token for authorization; if set, overrides username and password"` - ElasticsearchIndex string `long:"elasticsearch.index" env:"ELASTICSEARCH_INDEX" description:"ElasticSearch index name (placeholders: %y for year, %m for month and %d for day)" default:"pagerduty"` - ElasticsearchBatchCount int `long:"elasticsearch.batch-count" env:"ELASTICSEARCH_BATCH_COUNT" description:"Number of documents which should be indexed in one request" default:"50"` - ElasticsearchRetryCount int `long:"elasticsearch.retry-count" env:"ELASTICSEARCH_RETRY_COUNT" description:"ElasticSearch request retry count" default:"5"` - ElasticsearchRetryDelay time.Duration `long:"elasticsearch.retry-delay" env:"ELASTICSEARCH_RETRY_DELAY" description:"ElasticSearch request delay for reach retry" default:"5s"` -} - func main() { initArgparser() - // set verbosity - verbose = len(opts.Verbose) >= 1 - - // Init logger - daemonLogger = NewLogger(log.Lshortfile, verbose) - defer daemonLogger.Close() + log.Infof("starting pagerduty2es v%s (%s; %s; by %v)", gitTag, gitCommit, runtime.Version(), author) + log.Info(string(opts.GetJson())) - daemonLogger.Infof("Init Pagerduty2ElasticSearch exporter v%s (%s; by %v)", gitTag, gitCommit, author) - - daemonLogger.Infof("Init exporter") + log.Infof("init exporter") exporter := PagerdutyElasticsearchExporter{} exporter.Init() exporter.SetScrapeTime(opts.ScrapeTime) - exporter.SetPagerdutyDateRange(opts.PagerDutySince) + exporter.SetPagerdutyDateRange(opts.PagerDuty.Since) exporter.ConnectPagerduty( - opts.PagerDutyAuthToken, + opts.PagerDuty.AuthToken, &http.Client{ Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, @@ -80,8 +52,8 @@ func main() { Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, - MaxConnsPerHost: opts.PagerDutyMaxConnections, - MaxIdleConns: opts.PagerDutyMaxConnections, + MaxConnsPerHost: opts.PagerDuty.MaxConnections, + MaxIdleConns: opts.PagerDuty.MaxConnections, IdleConnTimeout: 60 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, @@ -91,29 +63,29 @@ func main() { ) cfg := elasticsearch.Config{ - Addresses: opts.ElasticsearchAddresses, - Username: opts.ElasticsearchUsername, - Password: opts.ElasticsearchPassword, - APIKey: opts.ElasticsearchApiKey, + Addresses: opts.Elasticsearch.Addresses, + Username: opts.Elasticsearch.Username, + Password: opts.Elasticsearch.Password, + APIKey: opts.Elasticsearch.ApiKey, Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, }, } - exporter.ConnectElasticsearch(cfg, opts.ElasticsearchIndex) - exporter.SetElasticsearchBatchCount(opts.ElasticsearchBatchCount) - exporter.SetElasticsearchRetry(opts.ElasticsearchRetryCount, opts.ElasticsearchRetryDelay) + exporter.ConnectElasticsearch(cfg, opts.Elasticsearch.Index) + exporter.SetElasticsearchBatchCount(opts.Elasticsearch.BatchCount) + exporter.SetElasticsearchRetry(opts.Elasticsearch.RetryCount, opts.Elasticsearch.RetryDelay) if opts.ScrapeTime.Seconds() > 0 { - daemonLogger.Infof("Starting daemon run") + log.Infof("starting daemon run") exporter.RunDaemon() // daemon mode - daemonLogger.Infof("Starting http server on %s", opts.ServerBind) + log.Infof("starting http server on %s", opts.ServerBind) startHttpServer() } else { - daemonLogger.Infof("Starting single run") + log.Infof("starting single run") exporter.RunSingle() - daemonLogger.Infof("completed single run") + log.Infof("completed single run") } } @@ -132,6 +104,37 @@ func initArgparser() { os.Exit(1) } } + + // verbose level + if opts.Logger.Verbose { + log.SetLevel(log.DebugLevel) + } + + // debug level + if opts.Logger.Debug { + log.SetReportCaller(true) + log.SetLevel(log.TraceLevel) + log.SetFormatter(&log.TextFormatter{ + CallerPrettyfier: func(f *runtime.Frame) (string, string) { + s := strings.Split(f.Function, ".") + funcName := s[len(s)-1] + return funcName, fmt.Sprintf("%s:%d", path.Base(f.File), f.Line) + }, + }) + } + + // json log format + if opts.Logger.LogJson { + log.SetReportCaller(true) + log.SetFormatter(&log.JSONFormatter{ + DisableTimestamp: true, + CallerPrettyfier: func(f *runtime.Frame) (string, string) { + s := strings.Split(f.Function, ".") + funcName := s[len(s)-1] + return funcName, fmt.Sprintf("%s:%d", path.Base(f.File), f.Line) + }, + }) + } } // start and handle prometheus handler @@ -139,10 +142,10 @@ func startHttpServer() { // healthz http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { if _, err := fmt.Fprint(w, "Ok"); err != nil { - daemonLogger.Error(err) + log.Error(err) } }) http.Handle("/metrics", promhttp.Handler()) - daemonLogger.Fatal(http.ListenAndServe(opts.ServerBind, nil)) + log.Fatal(http.ListenAndServe(opts.ServerBind, nil)) }