diff --git a/.goreleaser.yml b/.goreleaser.yml index 41fd76d..9f4b030 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -43,6 +43,8 @@ archive: - LICENSE - README.md - CHANGELOG.md + - service/* + - etc/example-circonus-logwatch.yaml - etc/log.d/README.md release: diff --git a/CHANGELOG.md b/CHANGELOG.md index b9d60c3..66a553d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# v0.5.0 + +* upd: include service and example configs in release +* add: systemd service configuration in `service/` +* add: example config `etc/example-circonus-logwatch.yaml` +* upd: change found metric message priority from Info to Debug +* upd: tomb->context+errgroup +* add: api ca file load/config for circonus metric destination +* upd: switch to circonus-gometrics v3 +* upd: condense/consolidate code +* upd: switch to go mod + # v0.4.0 * upd: release file names use x86_64, facilitate automated builds and testing diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 6105850..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,183 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - name = "github.com/alecthomas/units" - packages = ["."] - revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" - -[[projects]] - name = "github.com/circonus-labs/circonus-gometrics" - packages = [".","api","api/config","checkmgr"] - revision = "b25d14eeef390159289ad3e8521eff3162c59685" - version = "v2.1.0" - -[[projects]] - branch = "master" - name = "github.com/circonus-labs/circonusllhist" - packages = ["."] - revision = "6e85b9352cf0c2bb969831347491388bb3ae9c69" - -[[projects]] - name = "github.com/fsnotify/fsnotify" - packages = ["."] - revision = "629574ca2a5df945712d3079857300b5e4da0236" - version = "v1.4.2" - -[[projects]] - branch = "master" - name = "github.com/hashicorp/go-cleanhttp" - packages = ["."] - revision = "06c9ea3a335b7443026f8124b22619524420291b" - -[[projects]] - branch = "master" - name = "github.com/hashicorp/go-retryablehttp" - packages = ["."] - revision = "794af36148bf63c118d6db80eb902a136b907e71" - -[[projects]] - branch = "master" - name = "github.com/hashicorp/hcl" - packages = [".","hcl/ast","hcl/parser","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"] - revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8" - -[[projects]] - name = "github.com/hpcloud/tail" - packages = [".","ratelimiter","util","watch","winfile"] - revision = "a30252cb686a21eb2d0b98132633053ec2f7f1e5" - version = "v1.0.0" - -[[projects]] - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - name = "github.com/magiconair/properties" - packages = ["."] - revision = "d419a98cdbed11a922bf76f257b7c4be79b50e73" - version = "v1.7.4" - -[[projects]] - name = "github.com/maier/go-appstats" - packages = ["."] - revision = "812a586e5d1f053211dda0c664e8ce6cb7c38396" - version = "v0.2.0" - -[[projects]] - branch = "master" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - revision = "06020f85339e21b2478f756a78e295255ffa4d6a" - -[[projects]] - name = "github.com/pelletier/go-toml" - packages = ["."] - revision = "16398bac157da96aa88f98a2df640c7f32af1da2" - version = "v1.0.1" - -[[projects]] - name = "github.com/pkg/errors" - packages = ["."] - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - name = "github.com/rs/zerolog" - packages = [".","internal/json","log"] - revision = "3ac71fc58dbd43122668a912b755b0979ba9ce1f" - version = "v1.3.0" - -[[projects]] - name = "github.com/spf13/afero" - packages = [".","mem"] - revision = "8d919cbe7e2627e417f3e45c3c0e489a5b7e2536" - version = "v1.0.0" - -[[projects]] - name = "github.com/spf13/cast" - packages = ["."] - revision = "acbeb36b902d72a7a4c18e8f3241075e7ab763e4" - version = "v1.1.0" - -[[projects]] - branch = "master" - name = "github.com/spf13/cobra" - packages = ["."] - revision = "ccaecb155a2177302cb56cae929251a256d0f646" - -[[projects]] - branch = "master" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - revision = "12bd96e66386c1960ab0f74ced1362f66f552f7b" - -[[projects]] - name = "github.com/spf13/pflag" - packages = ["."] - revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66" - version = "v1.0.0" - -[[projects]] - name = "github.com/spf13/viper" - packages = ["."] - revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" - version = "v1.0.0" - -[[projects]] - branch = "master" - name = "github.com/tv42/httpunix" - packages = ["."] - revision = "b75d8614f926c077e48d85f1f8f7885b758c6225" - -[[projects]] - branch = "master" - name = "golang.org/x/net" - packages = ["context"] - revision = "d866cfc389cec985d6fda2859936a575a55a3ab6" - -[[projects]] - branch = "master" - name = "golang.org/x/sys" - packages = ["unix"] - revision = "d5840adf789d732bc8b00f37b26ca956a7cc8e79" - -[[projects]] - branch = "master" - name = "golang.org/x/text" - packages = ["internal/gen","internal/triegen","internal/ucd","transform","unicode/cldr","unicode/norm"] - revision = "73f72487b7d8a81970fbf571cd9b660d3d5e604c" - -[[projects]] - name = "gopkg.in/fsnotify.v1" - packages = ["."] - revision = "629574ca2a5df945712d3079857300b5e4da0236" - version = "v1.4.2" - -[[projects]] - branch = "v1" - name = "gopkg.in/tomb.v1" - packages = ["."] - revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" - -[[projects]] - branch = "v2" - name = "gopkg.in/tomb.v2" - packages = ["."] - revision = "d5d1b5820637886def9eef33e03a27a9f166942c" - -[[projects]] - branch = "v2" - name = "gopkg.in/yaml.v2" - packages = ["."] - revision = "287cf08546ab5e7e37d55a84f7ed3fd1db036de5" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "1c7b08dc67f65b472e95422851720e99d5a0d9b791b3c87fe63a2bcad1b9f65f" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 783ba24..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,62 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - branch = "master" - name = "github.com/alecthomas/units" - -[[constraint]] - name = "github.com/hpcloud/tail" - version = "1.0.0" - -[[constraint]] - name = "github.com/pelletier/go-toml" - version = "1.0.0" - -[[constraint]] - name = "github.com/pkg/errors" - version = "0.8.0" - -[[constraint]] - name = "github.com/rs/zerolog" - version = "1.3.0" - -[[constraint]] - branch = "master" - name = "github.com/spf13/cobra" - -[[constraint]] - name = "github.com/spf13/viper" - version = "1.0.0" - -[[constraint]] - branch = "master" - name = "golang.org/x/sys" - -[[constraint]] - branch = "v2" - name = "gopkg.in/tomb.v2" - -[[constraint]] - branch = "v2" - name = "gopkg.in/yaml.v2" diff --git a/etc/example-circonus-logwatch.yaml b/etc/example-circonus-logwatch.yaml new file mode 100644 index 0000000..3f76d87 --- /dev/null +++ b/etc/example-circonus-logwatch.yaml @@ -0,0 +1,62 @@ +--- +# location of log metric configurations +log_conf_dir: /opt/circonus/logwatch/etc/log.d +# stats for the process (e.g. curl localhost:33284/stats) +app_stat_port: "33284" +# turns on debugging messages (e.g. log.level=debug) +debug: false +# debug circonus api interactions +debug_cgm: false +# debug log tailing +debug_tail: false +# debug metric processing +debug_metric: false +api: # applicable when using a 'check' destination + key: "" + app: circonus-logwatch + url: https://api.circonus.com/v2/ + ca_file: "" +destination: + # log|agent|check|statsd + type: log + config: + # Circonus Check destination + # + # check or check bundle id /check/# or /check_bundle/# + #cid: "" + # + # searching for an existing check and creating a new check + #instance_id: "" + # + # searching for an existing check with a specific tag + #search_tag: "" + # + # searching for an existing check and creating a new check + #target: "" + # + # submission url of an existing check + #url: "" + + # StatsD destination + # + # grouping/prefix for metrics + #id: circonus-logwatch + # + # port a statsd server is listening to (default: 8125) + #port: "8125" + # + # prefix for metrics + #statsd_prefix: host. + + # Agent destination + # + # grouping/prefix for metrics + #id: circonus-logwatch + # + # port agent is listening to (default: 2609) + #port: "2609" +log: + level: info + # helpful when running process at command line + # makes log messages easier to read + pretty: false diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cb0cee6 --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module github.com/circonus-labs/circonus-logwatch + +require ( + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf + github.com/circonus-labs/circonus-gometrics/v3 v3.0.0-alpha.1 + github.com/hpcloud/tail v1.0.0 + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/maier/go-appstats v0.2.0 + github.com/pelletier/go-toml v1.2.0 + github.com/pkg/errors v0.8.0 + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/zerolog v1.10.0 + github.com/spf13/cobra v0.0.3 + github.com/spf13/viper v1.2.1 + github.com/stretchr/testify v1.2.2 // indirect + golang.org/x/net v0.0.0-20181029044818-c44066c5c816 // indirect + golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f + golang.org/x/sys v0.0.0-20181031143558-9b800f95dbbc + gopkg.in/fsnotify.v1 v1.4.7 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/yaml.v2 v2.2.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c18fa8f --- /dev/null +++ b/go.sum @@ -0,0 +1,71 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/circonus-labs/circonus-gometrics/v3 v3.0.0-alpha.1 h1:LpiieQEinVC+nqeapziqvCrQj/N97j61xOLgUOf2aHI= +github.com/circonus-labs/circonus-gometrics/v3 v3.0.0-alpha.1/go.mod h1:Yz3lD+BtalWoVQLMAWdxCW6cMK0zCwwUVL39pH2UOI4= +github.com/circonus-labs/circonusllhist v0.1.0 h1:3s0i9irZZhzwHAqAbx4BqbnOCVti+XiuoSiTpysNAuE= +github.com/circonus-labs/circonusllhist v0.1.0/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/circonus-labs/go-apiclient v0.5.0 h1:88sTJ4mW+XEKlKF2XYgTpIpxJXurXIzthost6kStg+k= +github.com/circonus-labs/go-apiclient v0.5.0/go.mod h1:FEKOXTaR6ZQ3aXlDtrXxT5ehfECSuuOSuA3kfqeMv6c= +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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 h1:qCv4319q2q7XKn0MQbi8p37hsJ+9Xo8e6yojA73JVxk= +github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6/go.mod h1:fXcdFsQoipQa7mwORhKad5jmDCeSy/RCGzWA08PO0lM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/maier/go-appstats v0.2.0 h1:Ka+CGMNWaqKaqdFmh1Ilm61lRWwBAuO7wleEvjko7Zo= +github.com/maier/go-appstats v0.2.0/go.mod h1:7wqGl4T89dYIsQOhSTrpMCMQL6MnyBdgyRYPu9u0nOI= +github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I= +github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/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/rs/zerolog v1.10.0 h1:roFDW4AgYGbHnTOAMZ2K8mHJZ/7bSj7txPfvbABIj88= +github.com/rs/zerolog v1.10.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= +github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.2.1 h1:bIcUwXqLseLF3BDAZduuNfekWG87ibtFxi59Bq+oI9M= +github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816 h1:mVFkLpejdFLXVUv9E42f3XJVfMdqd0IVLVIVLjZWn5o= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181031143558-9b800f95dbbc h1:SdCq5U4J+PpbSDIl9bM0V1e1Ug1jsnBkAFvTs1htn7U= +golang.org/x/sys v0.0.0-20181031143558-9b800f95dbbc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/agent/main.go b/internal/agent/main.go index 4e822a5..d99e026 100644 --- a/internal/agent/main.go +++ b/internal/agent/main.go @@ -6,14 +6,17 @@ package agent import ( + "context" "expvar" "net" "net/http" "os" "os/signal" + "time" "github.com/circonus-labs/circonus-logwatch/internal/config" "github.com/circonus-labs/circonus-logwatch/internal/configs" + "github.com/circonus-labs/circonus-logwatch/internal/metrics" "github.com/circonus-labs/circonus-logwatch/internal/metrics/circonus" "github.com/circonus-labs/circonus-logwatch/internal/metrics/logonly" "github.com/circonus-labs/circonus-logwatch/internal/metrics/statsd" @@ -22,16 +25,34 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/spf13/viper" + "golang.org/x/sync/errgroup" ) +// Agent holds the main circonus-logwatch process +type Agent struct { + group *errgroup.Group + groupCtx context.Context + groupCancel context.CancelFunc + watchers []*watcher.Watcher + signalCh chan os.Signal + destClient metrics.Destination + svrHTTP *http.Server +} + func init() { http.Handle("/stats", expvar.Handler()) } // New returns a new agent instance func New() (*Agent, error) { + ctx, cancel := context.WithCancel(context.Background()) + g, gctx := errgroup.WithContext(ctx) + a := Agent{ - signalCh: make(chan os.Signal), + group: g, + groupCtx: gctx, + groupCancel: cancel, + signalCh: make(chan os.Signal, 10), } // @@ -80,7 +101,7 @@ func New() (*Agent, error) { a.watchers = make([]*watcher.Watcher, len(cfgs)) for idx, cfg := range cfgs { - w, err := watcher.New(a.destClient, cfg) + w, err := watcher.New(a.groupCtx, a.destClient, cfg) if err != nil { log.Error().Err(err).Str("id", cfg.ID).Msg("adding watcher, log will NOT be processed") } @@ -97,39 +118,35 @@ func New() (*Agent, error) { // Start the agent func (a *Agent) Start() error { - a.t.Go(a.handleSignals) - + a.group.Go(a.handleSignals) for _, w := range a.watchers { - a.t.Go(w.Start) + a.group.Go(w.Start) } - - a.t.Go(a.serveMetrics) + a.group.Go(a.serveMetrics) log.Debug(). Int("pid", os.Getpid()). Str("name", release.NAME). - Str("ver", release.VERSION).Msg("Starting wait") + Str("ver", release.VERSION).Msg("Started") - return a.t.Wait() + return a.group.Wait() } // Stop cleans up and shuts down the Agent func (a *Agent) Stop() { - a.stopSignalHandler() - - for _, w := range a.watchers { - w.Stop() - } - - a.svrHTTP.Close() - log.Debug(). Int("pid", os.Getpid()). Str("name", release.NAME). - Str("ver", release.VERSION).Msg("Stopped") + Str("ver", release.VERSION).Msg("Stopping") - if a.t.Alive() { - a.t.Kill(nil) + a.stopSignalHandler() + a.groupCancel() + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + err := a.svrHTTP.Shutdown(ctx) + if err != nil { + log.Warn().Err(err).Msg("closing HTTP server") } } diff --git a/internal/agent/sighndlr_bsdsol.go b/internal/agent/sighndlr_bsdsol.go index 25b65e4..9bb4925 100644 --- a/internal/agent/sighndlr_bsdsol.go +++ b/internal/agent/sighndlr_bsdsol.go @@ -34,7 +34,7 @@ func (a *Agent) handleSignals() error { for { select { - case <-a.t.Dying(): + case <-a.groupCtx.Done(): return nil case sig := <-a.signalCh: log.Info().Str("signal", sig.String()).Msg("Received signal") diff --git a/internal/agent/sighndlr_linux.go b/internal/agent/sighndlr_linux.go index 0d13bad..0390efd 100644 --- a/internal/agent/sighndlr_linux.go +++ b/internal/agent/sighndlr_linux.go @@ -34,7 +34,7 @@ func (a *Agent) handleSignals() error { for { select { - case <-a.t.Dying(): + case <-a.groupCtx.Done(): return nil case sig := <-a.signalCh: log.Info().Str("signal", sig.String()).Msg("Received signal") diff --git a/internal/agent/sighndlr_windows.go b/internal/agent/sighndlr_windows.go index 95d8985..8e6db9b 100644 --- a/internal/agent/sighndlr_windows.go +++ b/internal/agent/sighndlr_windows.go @@ -34,7 +34,7 @@ func (a *Agent) handleSignals() error { for { select { - case <-a.t.Dying(): + case <-a.groupCtx.Done(): return nil case sig := <-a.signalCh: log.Info().Str("signal", sig.String()).Msg("Received signal") diff --git a/internal/agent/vars.go b/internal/agent/vars.go deleted file mode 100644 index 14a5f14..0000000 --- a/internal/agent/vars.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package agent - -import ( - "net/http" - "os" - - "github.com/circonus-labs/circonus-logwatch/internal/metrics" - "github.com/circonus-labs/circonus-logwatch/internal/watcher" - tomb "gopkg.in/tomb.v2" -) - -// Agent holds the main circonus-logwatch process -type Agent struct { - watchers []*watcher.Watcher - signalCh chan os.Signal - t tomb.Tomb - destClient metrics.Destination - svrHTTP *http.Server -} diff --git a/internal/config/main.go b/internal/config/main.go index 01a190a..3868cb6 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -26,6 +26,135 @@ import ( yaml "gopkg.in/yaml.v2" ) +// Log defines the running config.log structure +type Log struct { + Level string `json:"level" yaml:"level" toml:"level"` + Pretty bool `json:"pretty" yaml:"pretty" toml:"pretty"` +} + +// API defines the running config.api structure +type API struct { + Key string `json:"key" yaml:"key" toml:"key"` + App string `json:"app" yaml:"app" toml:"app"` + URL string `json:"url" yaml:"url" toml:"url"` + CAFile string `mapstructure:"ca_file" json:"ca_file" yaml:"ca_file" toml:"ca_file"` +} + +// DestConfig defines the running config.destination.config structure +type DestConfig struct { + ID string `json:"id" yaml:"id" toml:"id"` + Port string `json:"port" yaml:"port" toml:"port"` + StatsdPrefix string `mapstructure:"statsd_prefix" json:"statsd_prefix" yaml:"statsd_prefix" toml:"statsd_prefix"` + CID string `json:"cid" yaml:"cid" toml:"cid"` + URL string `json:"url" yaml:"url" toml:"url"` + Target string `json:"target" yaml:"target" toml:"target"` + SearchTag string `mapstructure:"search_tag" json:"search_tag" yaml:"search_tag" toml:"search_tag"` + InstanceID string `mapstructure:"instance_id" json:"instance_id" yaml:"instance_id" toml:"instance_id"` +} + +// Destination defines the running config.destination structure +type Destination struct { + Type string `json:"type" yaml:"type" toml:"type"` + Config DestConfig `json:"config" yaml:"config" toml:"config"` +} + +// Config defines the running config structure +type Config struct { + LogConfDir string `mapstructure:"log_conf_dir" json:"log_conf_dir" yaml:"log_conf_dir" toml:"log_conf_dir"` + AppStatPort string `mapstructure:"app_stat_port" json:"app_stat_port" yaml:"app_stat_port" toml:"app_stat_port"` + Debug bool `json:"debug" yaml:"debug" toml:"debug"` + DebugCGM bool `mapstructure:"debug_cgm" json:"debug_cgm" yaml:"debug_cgm" toml:"debug_cgm"` + DebugTail bool `mapstructure:"debug_tail" json:"debug_tail" yaml:"debug_tail" toml:"debug_tail"` + DebugMetric bool `mapstructure:"debug_metric" json:"debug_metric" yaml:"debug_metric" toml:"debug_metric"` + API API `json:"api" yaml:"api" toml:"api"` + Destination Destination `json:"destination" yaml:"destination" toml:"destination"` + Log Log `json:"log" yaml:"log" toml:"log"` +} + +// +// NOTE: adding a Key* MUST be reflected in the Config structures above +// +const ( + // KeyAPICAFile custom ca for circonus api (e.g. inside) + KeyAPICAFile = "api.ca_file" + + // KeyAPITokenApp circonus api token key application name + KeyAPITokenApp = "api.app" + + // KeyAPITokenKey circonus api token key + KeyAPITokenKey = "api.key" + + // KeyAPIURL custom circonus api url (e.g. inside) + KeyAPIURL = "api.url" + + // KeyDebug enables debug messages + KeyDebug = "debug" + + // KeyDebugCGM enables debug messages for circonus-gometrics + KeyDebugCGM = "debug_cgm" + + // KeyDebugTail enables debug messages for log tailing + KeyDebugTail = "debug_tail" + + // KeyDebugMetric enables detailed log line/metric rule debugging messages + KeyDebugMetric = "debug_metric" + + // KeyAppStatPort on which to expose runtime stats (expvar) + KeyAppStatPort = "app_stat_port" + + // KeyLogConfDir log configuration directory + KeyLogConfDir = "log_conf_dir" + + // KeyLogLevel logging level (panic, fatal, error, warn, info, debug, disabled) + KeyLogLevel = "log.level" + + // KeyLogPretty output formatted log lines (for running in foreground) + KeyLogPretty = "log.pretty" + + // KeyShowConfig - show configuration and exit + KeyShowConfig = "show-config" + + // KeyShowVersion - show version information and exit + KeyShowVersion = "version" + + // KeyDestType of destination where metrics are being sent (none|statsd|agent|check) + KeyDestType = "destination.type" + + // KeyDestCfgID for destination type (statsd|agent) + KeyDestCfgID = "destination.config.id" + + // KeyDestCfgCID for destination type (check, check bundle id) + KeyDestCfgCID = "destination.config.cid" + + // KeyDestCfgURL for destination type (check, submission url) + KeyDestCfgURL = "destination.config.url" + + // KeyDestCfgPort for destination type (statsd|agent, port to use agent=2609, statsd=8125) + KeyDestCfgPort = "destination.config.port" + + // KeyDestCfgTarget for destination type (check, target, for search|create) + KeyDestCfgTarget = "destination.config.target" + + // KeyDestCfgSearchTag for destination type (check, tag, for search|create) + KeyDestCfgSearchTag = "destination.config.search_tag" + + // KeyDestCfgInstanceID for destination type (check, instance id, for search|create) + KeyDestCfgInstanceID = "destination.config.instance_id" + + // KeyDestCfgStatsdPrefix to prepend on every metric + KeyDestCfgStatsdPrefix = "destination.config.statsd_prefix" + + // KeyDestAgentURL defines the submission url for the agent destination + // NOTE: this is dymanically created by config validation, it is NOT part of Config + KeyDestAgentURL = "destination.agentURL" + + cosiName = "cosi" +) + +var ( + cosiCfgFile = filepath.Join(defaults.BasePath, "..", cosiName, "etc", "cosi.json") +) + // Validate the configuration options supplied func Validate() error { diff --git a/internal/config/vars.go b/internal/config/vars.go deleted file mode 100644 index f4c37b2..0000000 --- a/internal/config/vars.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package config - -import ( - "path/filepath" - - "github.com/circonus-labs/circonus-logwatch/internal/config/defaults" -) - -// Log defines the running config.log structure -type Log struct { - Level string `json:"level" yaml:"level" toml:"level"` - Pretty bool `json:"pretty" yaml:"pretty" toml:"pretty"` -} - -// API defines the running config.api structure -type API struct { - Key string `json:"key" yaml:"key" toml:"key"` - App string `json:"app" yaml:"app" toml:"app"` - URL string `json:"url" yaml:"url" toml:"url"` - CAFile string `mapstructure:"ca_file" json:"ca_file" yaml:"ca_file" toml:"ca_file"` -} - -// DestConfig defines the running config.destination.config structure -type DestConfig struct { - ID string `json:"id" yaml:"id" toml:"id"` - Port string `json:"port" yaml:"port" toml:"port"` - StatsdPrefix string `mapstructure:"statsd_prefix" json:"statsd_prefix" yaml:"statsd_prefix" toml:"statsd_prefix"` - CID string `json:"cid" yaml:"cid" toml:"cid"` - URL string `json:"url" yaml:"url" toml:"url"` - Target string `json:"target" yaml:"target" toml:"target"` - SearchTag string `mapstructure:"search_tag" json:"search_tag" yaml:"search_tag" toml:"search_tag"` - InstanceID string `mapstructure:"instance_id" json:"instance_id" yaml:"instance_id" toml:"instance_id"` -} - -// Destination defines the running config.destination structure -type Destination struct { - Type string `json:"type" yaml:"type" toml:"type"` - Config DestConfig `json:"config" yaml:"config" toml:"config"` -} - -// Config defines the running config structure -type Config struct { - LogConfDir string `mapstructure:"log_conf_dir" json:"log_conf_dir" yaml:"log_conf_dir" toml:"log_conf_dir"` - AppStatPort string `mapstructure:"app_stat_port" json:"app_stat_port" yaml:"app_stat_port" toml:"app_stat_port"` - Debug bool `json:"debug" yaml:"debug" toml:"debug"` - DebugCGM bool `mapstructure:"debug_cgm" json:"debug_cgm" yaml:"debug_cgm" toml:"debug_cgm"` - DebugTail bool `mapstructure:"debug_tail" json:"debug_tail" yaml:"debug_tail" toml:"debug_tail"` - DebugMetric bool `mapstructure:"debug_metric" json:"debug_metric" yaml:"debug_metric" toml:"debug_metric"` - API API `json:"api" yaml:"api" toml:"api"` - Destination Destination `json:"destination" yaml:"destination" toml:"destination"` - Log Log `json:"log" yaml:"log" toml:"log"` -} - -// -// NOTE: adding a Key* MUST be reflected in the Config structures above -// -const ( - // KeyAPICAFile custom ca for circonus api (e.g. inside) - KeyAPICAFile = "api.ca_file" - - // KeyAPITokenApp circonus api token key application name - KeyAPITokenApp = "api.app" - - // KeyAPITokenKey circonus api token key - KeyAPITokenKey = "api.key" - - // KeyAPIURL custom circonus api url (e.g. inside) - KeyAPIURL = "api.url" - - // KeyDebug enables debug messages - KeyDebug = "debug" - - // KeyDebugCGM enables debug messages for circonus-gometrics - KeyDebugCGM = "debug_cgm" - - // KeyDebugTail enables debug messages for log tailing - KeyDebugTail = "debug_tail" - - // KeyDebugMetric enables detailed log line/metric rule debugging messages - KeyDebugMetric = "debug_metric" - - // KeyAppStatPort on which to expose runtime stats (expvar) - KeyAppStatPort = "app_stat_port" - - // KeyLogConfDir log configuration directory - KeyLogConfDir = "log_conf_dir" - - // KeyLogLevel logging level (panic, fatal, error, warn, info, debug, disabled) - KeyLogLevel = "log.level" - - // KeyLogPretty output formatted log lines (for running in foreground) - KeyLogPretty = "log.pretty" - - // KeyShowConfig - show configuration and exit - KeyShowConfig = "show-config" - - // KeyShowVersion - show version information and exit - KeyShowVersion = "version" - - // KeyDestType of destination where metrics are being sent (none|statsd|agent|check) - KeyDestType = "destination.type" - - // KeyDestCfgID for destination type (statsd|agent) - KeyDestCfgID = "destination.config.id" - - // KeyDestCfgCID for destination type (check, check bundle id) - KeyDestCfgCID = "destination.config.cid" - - // KeyDestCfgURL for destination type (check, submission url) - KeyDestCfgURL = "destination.config.url" - - // KeyDestCfgPort for destination type (statsd|agent, port to use agent=2609, statsd=8125) - KeyDestCfgPort = "destination.config.port" - - // KeyDestCfgTarget for destination type (check, target, for search|create) - KeyDestCfgTarget = "destination.config.target" - - // KeyDestCfgSearchTag for destination type (check, tag, for search|create) - KeyDestCfgSearchTag = "destination.config.search_tag" - - // KeyDestCfgInstanceID for destination type (check, instance id, for search|create) - KeyDestCfgInstanceID = "destination.config.instance_id" - - // KeyDestCfgStatsdPrefix to prepend on every metric - KeyDestCfgStatsdPrefix = "destination.config.statsd_prefix" - - // KeyDestAgentURL defines the submission url for the agent destination - // NOTE: this is dymanically created by config validation, it is NOT part of Config - KeyDestAgentURL = "destination.agentURL" - - cosiName = "cosi" -) - -var ( - cosiCfgFile = filepath.Join(defaults.BasePath, "..", cosiName, "etc", "cosi.json") -) diff --git a/internal/configs/main.go b/internal/configs/main.go index 9826e11..8ed9c81 100644 --- a/internal/configs/main.go +++ b/internal/configs/main.go @@ -25,6 +25,24 @@ import ( "gopkg.in/yaml.v2" ) +// Metric is a metric definition for the log config +type Metric struct { + Matcher *regexp.Regexp + MatchParts []string + Namer *template.Template + ValueKey string + Match string `json:"match" yaml:"match" toml:"match"` + Name string `json:"name" yaml:"name" toml:"name"` + Type string `json:"type" yaml:"type" toml:"type"` +} + +// Config defines a log to watch +type Config struct { + ID string `json:"id" yaml:"id" toml:"id"` + LogFile string `json:"log_file" yaml:"log_file" toml:"log_file"` + Metrics []*Metric `json:"metrics" yaml:"metrics" toml:"metrics"` +} + // Load reads the log configurations from log config directory func Load() ([]*Config, error) { logger := log.With().Str("pkg", "configs").Logger() diff --git a/internal/configs/vars.go b/internal/configs/vars.go deleted file mode 100644 index e9d529e..0000000 --- a/internal/configs/vars.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package configs - -import ( - "regexp" - "text/template" -) - -// Metric is a metric definition for the log config -type Metric struct { - Matcher *regexp.Regexp - MatchParts []string - Namer *template.Template - ValueKey string - Match string `json:"match" yaml:"match" toml:"match"` - Name string `json:"name" yaml:"name" toml:"name"` - Type string `json:"type" yaml:"type" toml:"type"` -} - -// Config defines a log to watch -type Config struct { - ID string `json:"id" yaml:"id" toml:"id"` - LogFile string `json:"log_file" yaml:"log_file" toml:"log_file"` - Metrics []*Metric `json:"metrics" yaml:"metrics" toml:"metrics"` -} diff --git a/internal/metrics/circonus/main.go b/internal/metrics/circonus/main.go index dacb628..ff58a87 100644 --- a/internal/metrics/circonus/main.go +++ b/internal/metrics/circonus/main.go @@ -6,16 +6,26 @@ package circonus import ( + "crypto/tls" + "crypto/x509" "fmt" + "io/ioutil" stdlog "log" - cgm "github.com/circonus-labs/circonus-gometrics" + cgm "github.com/circonus-labs/circonus-gometrics/v3" "github.com/circonus-labs/circonus-logwatch/internal/config" "github.com/pkg/errors" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/viper" ) +// Circonus defines an instance of the circonus metrics destination +type Circonus struct { + logger zerolog.Logger + client *cgm.CirconusMetrics +} + // New returns a new instance of the circonus metrics destination func New() (*Circonus, error) { var client *cgm.CirconusMetrics @@ -53,8 +63,16 @@ func New() (*Circonus, error) { if viper.GetString(config.KeyAPIURL) != "" { cmc.CheckManager.API.URL = viper.GetString(config.KeyAPIURL) } - if viper.GetString(config.KeyAPICAFile) != "" { - // TODO: load the cert and add to config + if file := viper.GetString(config.KeyAPICAFile); file != "" { + cert, err := ioutil.ReadFile(file) + if err != nil { + return nil, errors.Wrapf(err, "reading specified API CA file (%s)", file) + } + cp := x509.NewCertPool() + if !cp.AppendCertsFromPEM(cert) { + return nil, errors.New("unable to add API CA Certificate to x509 cert pool") + } + cmc.CheckManager.API.TLSConfig = &tls.Config{RootCAs: cp} } if viper.GetString(config.KeyDestCfgCID) != "" { cmc.CheckManager.Check.ID = viper.GetString(config.KeyDestCfgCID) diff --git a/internal/metrics/circonus/vars.go b/internal/metrics/circonus/vars.go deleted file mode 100644 index bd93251..0000000 --- a/internal/metrics/circonus/vars.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package circonus - -import ( - cgm "github.com/circonus-labs/circonus-gometrics" - "github.com/rs/zerolog" - tomb "gopkg.in/tomb.v2" -) - -// Circonus defines an instance of the circonus metrics destination -type Circonus struct { - logger zerolog.Logger - client *cgm.CirconusMetrics - t tomb.Tomb -} diff --git a/internal/metrics/logonly/main.go b/internal/metrics/logonly/main.go index 8f03ee7..cb3595b 100644 --- a/internal/metrics/logonly/main.go +++ b/internal/metrics/logonly/main.go @@ -6,9 +6,22 @@ package logonly import ( + "sync" + + "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) +// LogOnly defines logging metrics only destination +type LogOnly struct { + logger zerolog.Logger +} + +var ( + client *LogOnly + once sync.Once +) + // New creates a new log only destination func New() (*LogOnly, error) { diff --git a/internal/metrics/logonly/vars.go b/internal/metrics/logonly/vars.go deleted file mode 100644 index e72ee33..0000000 --- a/internal/metrics/logonly/vars.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package logonly - -import ( - "sync" - - "github.com/rs/zerolog" -) - -// LogOnly defines logging metrics only destination -type LogOnly struct { - logger zerolog.Logger -} - -var ( - client *LogOnly - once sync.Once -) diff --git a/internal/metrics/statsd/main.go b/internal/metrics/statsd/main.go index d46c85c..146704f 100644 --- a/internal/metrics/statsd/main.go +++ b/internal/metrics/statsd/main.go @@ -12,14 +12,30 @@ import ( "math/big" "math/rand" "net" + "sync" "time" "github.com/circonus-labs/circonus-logwatch/internal/config" "github.com/pkg/errors" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/viper" ) +// Statsd defines the relevant properties of a StatsD connection. +type Statsd struct { + id string + port string + prefix string + conn net.Conn + logger zerolog.Logger +} + +var ( + client *Statsd + once sync.Once +) + func init() { n, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) if err != nil { diff --git a/internal/metrics/statsd/vars.go b/internal/metrics/statsd/vars.go deleted file mode 100644 index 98ef90a..0000000 --- a/internal/metrics/statsd/vars.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package statsd - -import ( - "net" - "sync" - - "github.com/rs/zerolog" -) - -// Statsd defines the relevant properties of a StatsD connection. -type Statsd struct { - id string - port string - prefix string - conn net.Conn - logger zerolog.Logger -} - -var ( - client *Statsd - once sync.Once -) diff --git a/internal/release/main.go b/internal/release/main.go index dd396cd..fc97340 100644 --- a/internal/release/main.go +++ b/internal/release/main.go @@ -9,6 +9,34 @@ import ( "expvar" ) +const ( + // NAME is the name of this application + NAME = "circonus-logwatch" + // ENVPREFIX is the environment variable prefix + ENVPREFIX = "CLW" +) + +// vars are manipulated at link time (see goreleaser) +var ( + // COMMIT of relase in git repo + COMMIT = "none" + // DATE of release + DATE = "unknown" + // TAG of release + TAG = "" + // VERSION of the release + VERSION = "dev" +) + +// Info contains release information +type Info struct { + Name string + Version string + Commit string + BuildDate string + Tag string +} + func init() { expvar.Publish("app", expvar.Func(info)) } diff --git a/internal/release/vars.go b/internal/release/vars.go deleted file mode 100644 index b6714c6..0000000 --- a/internal/release/vars.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package release - -const ( - // NAME is the name of this application - NAME = "circonus-logwatch" - // ENVPREFIX is the environment variable prefix - ENVPREFIX = "CLW" -) - -// vars are manipulated at link time (see goreleaser) -var ( - // COMMIT of relase in git repo - COMMIT = "none" - // DATE of release - DATE = "unknown" - // TAG of release - TAG = "" - // VERSION of the release - VERSION = "dev" -) - -// Info contains release information -type Info struct { - Name string - Version string - Commit string - BuildDate string - Tag string -} diff --git a/internal/watcher/main.go b/internal/watcher/main.go index a337de3..a92ccff 100644 --- a/internal/watcher/main.go +++ b/internal/watcher/main.go @@ -7,6 +7,7 @@ package watcher import ( "bytes" + "context" "fmt" "io" "io/ioutil" @@ -21,12 +22,45 @@ import ( "github.com/hpcloud/tail" "github.com/maier/go-appstats" "github.com/pkg/errors" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/viper" + "golang.org/x/sync/errgroup" +) + +type metric struct { + Name string + Type string + Value string +} + +type metricLine struct { + line string + matches *map[string]string + metricID int +} + +// Watcher defines a new log watcher +type Watcher struct { + group *errgroup.Group + groupCtx context.Context + cfg *configs.Config + trace bool + logger zerolog.Logger + metricLines chan metricLine + metrics chan metric + dest metrics.Destination + statTotalLines string + statMatchedLines string +} + +const ( + metricLineQueueSize = 1000 + metricQueueSize = 1000 ) // New creates a new watcher instance -func New(metricDest metrics.Destination, logConfig *configs.Config) (*Watcher, error) { +func New(ctx context.Context, metricDest metrics.Destination, logConfig *configs.Config) (*Watcher, error) { if metricDest == nil { return nil, errors.New("invalid metric destination (nil)") } @@ -34,7 +68,10 @@ func New(metricDest metrics.Destination, logConfig *configs.Config) (*Watcher, e return nil, errors.New("invalid log config (nil)") } + g, gctx := errgroup.WithContext(ctx) w := Watcher{ + group: g, + groupCtx: gctx, logger: log.With().Str("pkg", "watcher").Str("log_id", logConfig.ID).Logger(), cfg: logConfig, dest: metricDest, @@ -53,20 +90,24 @@ func New(metricDest metrics.Destination, logConfig *configs.Config) (*Watcher, e // Start the watcher func (w *Watcher) Start() error { - w.t.Go(w.save) - w.t.Go(w.parse) - w.t.Go(w.process) - return w.t.Wait() + w.group.Go(w.save) + w.group.Go(w.parse) + w.group.Go(w.process) + + go func() { + select { + case <-w.groupCtx.Done(): + w.logger.Debug().Msg("stopping watcher process") + w.Stop() + } + }() + + return w.group.Wait() } // Stop the watcher func (w *Watcher) Stop() error { - w.logger.Info().Msg("stopping") - if w.t.Alive() { - w.t.Kill(nil) - } - - return nil + return w.groupCtx.Err() } // process opens log and checks log lines for matches @@ -91,21 +132,21 @@ START_TAIL: tailer, err := tail.TailFile(w.cfg.LogFile, cfg) if err != nil { w.logger.Error().Err(err).Msg("starting tailer") - w.t.Kill(err) return err } w.logger.Debug().Msg("tail started, waiting for lines") for { select { - case <-w.t.Dying(): - w.logger.Debug().Msg("w.t dying, stopping tail") + case <-w.groupCtx.Done(): + w.logger.Debug().Msg("ctx done, stopping process tail") tailer.Cleanup() return nil case <-tailer.Dying(): w.logger.Debug().Err(tailer.Err()).Msg("tailer dying, restarting tailer") - // there is a not well handled scenario in tail where the inotify watcher - // is closed while the log reopener is waiting for log file creation events + // there is a not well handled scenario in tail where + // the inotify watcher is closed while the log reopener + // is waiting for log file creation events tailer.Cleanup() goto START_TAIL case line := <-tailer.Lines: @@ -116,7 +157,7 @@ START_TAIL: if !strings.Contains(err.Error(), "file already closed") { w.logger.Debug().Msg("!file already closed error, stopping tail") tailer.Cleanup() - w.t.Kill(err) + // w.t.Kill(err) return err } } @@ -169,7 +210,8 @@ START_TAIL: func (w *Watcher) parse() error { for { select { - case <-w.t.Dying(): + case <-w.groupCtx.Done(): + w.logger.Debug().Msg("ctx done, stopping parse") return nil case l := <-w.metricLines: appstats.IncrementInt(w.statMatchedLines) @@ -221,10 +263,11 @@ func (w *Watcher) parse() error { func (w *Watcher) save() error { for { select { - case <-w.t.Dying(): + case <-w.groupCtx.Done(): + w.logger.Debug().Msg("ctx done, stopping save") return nil case m := <-w.metrics: - w.logger.Info(). + w.logger.Debug(). Str("metric", fmt.Sprintf("%#v", m)). Msg("sending") @@ -264,7 +307,7 @@ func (w *Watcher) save() error { case "t": w.dest.SetTextValue(m.Name, m.Value) default: - w.logger.Info(). + w.logger.Warn(). Str("type", m.Type). Str("name", m.Name). Interface("val", m.Value). diff --git a/internal/watcher/vars.go b/internal/watcher/vars.go deleted file mode 100644 index 6d03f67..0000000 --- a/internal/watcher/vars.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2017 Circonus, Inc. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package watcher - -import ( - "github.com/circonus-labs/circonus-logwatch/internal/configs" - "github.com/circonus-labs/circonus-logwatch/internal/metrics" - "github.com/rs/zerolog" - tomb "gopkg.in/tomb.v2" -) - -type metric struct { - Name string - Type string - Value string -} - -type metricLine struct { - line string - matches *map[string]string - metricID int -} - -// Watcher defines a new log watcher -type Watcher struct { - cfg *configs.Config - trace bool - logger zerolog.Logger - metricLines chan metricLine - metrics chan metric - t tomb.Tomb - dest metrics.Destination - statTotalLines string - statMatchedLines string -} - -const ( - metricLineQueueSize = 1000 - metricQueueSize = 1000 -) diff --git a/service/README.md b/service/README.md new file mode 100644 index 0000000..93f747e --- /dev/null +++ b/service/README.md @@ -0,0 +1,14 @@ +# Circonus Logwatch service configurations + +## Systemd + +### Basic installation + +If circonus-logwatch is installed in default location `/opt/circonus/logwatch`. Otherwise, edit the `circonus-logwatch.service` file and change the path in the `ExecStart` command. + +``` +# cp circonus-logwatch.service /usr/lib/systemd/system/circonus-logwatch.service +# systemctl enable circonus-logwatch +# systemctl start circonus-logwatch +# systemctl status circonus-logwatch +``` diff --git a/service/circonus-logwatch.service b/service/circonus-logwatch.service new file mode 100644 index 0000000..479a129 --- /dev/null +++ b/service/circonus-logwatch.service @@ -0,0 +1,12 @@ +[Unit] +Description=Circonus Logwatch Daemon +Documentation=http://github.com/circonus-labs/circonus-logwatch +After=network.target + +[Service] +ExecStart=/opt/circonus/logwatch/circonus-logwatch +Restart=always +User=nobody + +[Install] +WantedBy=multi-user.target