Skip to content

Commit d169540

Browse files
author
Vladimir Smirnov
committed
Add option to specify URL Prefixes and different listener for expvar
* Add config option `prefix` to specify URL Prefixes * Add config option `expvar` to specify if expvars are exported over HTTP and on what address * Add ability to enabled pprof handlers (see `expvar` structure) * Update Changelog and Readme
1 parent d0e5ecc commit d169540

File tree

8 files changed

+191
-44
lines changed

8 files changed

+191
-44
lines changed

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ Changes
1313

1414
CHANGELOG
1515
---------
16-
**0.12.4 (WIP)**
16+
**0.12.4**
1717
- [Feature] Fuction Defines - allows to do custom metric aliases (thx to @lomik). See [doc/configuration.md](https://github.com/go-graphite/carbonapi/blob/master/doc/configuration.md#define) for config format
18+
- [Improvement] New config options that allows to prefix all URLs and to enable /debug/vars on a separate address:port. See [docs/configuration.md](https://github.com/go-graphite/carbonapi/blob/master/doc/configuration.md#expvar) for more information
1819
- [Improvement] `/render` queries now returns tags in json (as graphite-web do)
1920
- [Improvement] groupByTags should now support all available aggregation functions (#410)
2021
- [Fix] Fix panic when using carbonapi\_proto\_v3 and doing tag-related queries (#407)

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ For requirements see **Requirements** section below.
1414
Installation
1515
------------
1616

17-
At this moment we are building packages for CentOS 6, CentOS 7, Ubuntu 14.04 and Ubuntu 16.04. Installation guides are available on packagecloud (see the links below).
17+
At this moment we are building packages for CentOS 6, CentOS 7, Debian 9, Debian 10, Ubuntu 14.04, Ubuntu 16.04 and Ubuntu 18.04. Installation guides are available on packagecloud (see the links below).
1818

1919
Stable versions: [Stable repo](https://packagecloud.io/go-graphite/stable/install)
2020

2121
Autobuilds (master, might be unstable): [Autobuild repo](https://packagecloud.io/go-graphite/autobuilds/install)
2222

23+
Configuration guides: [docs/configuration.md](https://github.com/go-graphite/carbonapi/blob/master/doc/configuration.md) and [example config](https://github.com/go-graphite/carbonapi/blob/master/cmd/carbonapi/carbonapi.example.yaml).
24+
25+
There are multiple example configurations available for different backends: [prometheus](https://github.com/go-graphite/carbonapi/blob/master/cmd/carbonapi/carbonapi.example.prometheus.yaml), [graphtie-clickhouse](https://github.com/go-graphite/carbonapi/blob/master/cmd/carbonapi/carbonapi.example.clickhouse.yaml), [go-carbon](https://github.com/go-graphite/carbonapi/blob/master/cmd/carbonapi/carbonapi.example.yaml)
2326

2427
General information
2528
-------------------

cmd/carbonapi/carbonapi.example.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
# or graphite-clickhouse's http url.
1010
# Listen address, should always include hostname or ip address and a port.
1111
listen: "localhost:8081"
12+
# Specify URL Prefix for all handlers
13+
prefix: ""
14+
# Specify if metrics are exported over HTTP and if they are available on the same address or not
15+
# pprofEnabled controls if extra HTTP Handlers to profile and debug application will be available
16+
expvar:
17+
enabled: true
18+
pprofEnabled: false
19+
listen: ""
1220
# Allow extra charsets in metric names. By default only "Latin" is allowed
1321
# Please note that each unicodeRangeTables will slow down metric parsing a bit
1422
# For list of supported tables, see: https://golang.org/src/unicode/tables.go?#L3437
@@ -29,6 +37,12 @@ headersToLog:
2937
- "X-Dashboard-Id"
3038
- "X-Grafana-Org-Id"
3139
- "X-Panel-Id"
40+
# Specify custom function aliases.
41+
# This is example for alias "perMinute(metrics)" that will behave as "perSecond(metric)|scale(60)"
42+
define:
43+
-
44+
name: "perMinute"
45+
template: "perSecond({{.argString}})|scale(60)"
3246
# Max concurrent requests to CarbonZipper
3347
concurency: 1000
3448
cache:

cmd/carbonapi/config/config.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ type Define struct {
4141
Template string `mapstructure:"template"`
4242
}
4343

44+
type ExpvarConfig struct {
45+
Listen string `mapstructure:"listen"`
46+
Enabled bool `mapstructure:"enabled"`
47+
PProfEnabled bool `mapstructure:"pprofEnabled"`
48+
}
49+
4450
type ConfigType struct {
4551
ExtrapolateExperiment bool `mapstructure:"extrapolateExperiment"`
4652
Logger []zapwriter.Config `mapstructure:"logger"`
@@ -68,6 +74,8 @@ type ConfigType struct {
6874
HeadersToPass []string `mapstructure:"headersToPass"`
6975
HeadersToLog []string `mapstructure:"headersToLog"`
7076
Define []Define `mapstructure:"define"`
77+
Prefix string `mapstructure:"prefix"`
78+
Expvar ExpvarConfig `mapstructure:"expvar"`
7179

7280
QueryCache cache.BytesCache `mapstructure:"-" json:"-"`
7381
FindCache cache.BytesCache `mapstructure:"-" json:"-"`
@@ -131,4 +139,10 @@ var Config = ConfigType{
131139
},
132140
ExpireDelaySec: 10 * 60,
133141
GraphiteWeb09Compatibility: false,
142+
Prefix: "",
143+
Expvar: ExpvarConfig{
144+
Listen: "",
145+
Enabled: true,
146+
PProfEnabled: false,
147+
},
134148
}

cmd/carbonapi/config/init.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,13 @@ func SetUpConfig(logger *zap.Logger, BuildVersion string) {
9191
newStruct.ColorList = nil
9292
newStruct.YDivisors = nil
9393
sub := graphTemplatesViper.Sub(k)
94-
sub.Unmarshal(&newStruct)
94+
err = sub.Unmarshal(&newStruct)
95+
if err != nil {
96+
logger.Error("failed to parse graphTemplates config, settings will be ignored",
97+
zap.String("graphTemplate_path", Config.GraphTemplates),
98+
zap.Error(err),
99+
)
100+
}
95101
if newStruct.ColorList == nil || len(newStruct.ColorList) == 0 {
96102
newStruct.ColorList = make([]string, len(png.DefaultParams.ColorList))
97103
copy(newStruct.ColorList, png.DefaultParams.ColorList)

cmd/carbonapi/http/init.go

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,49 @@
11
package http
22

33
import (
4+
"expvar"
45
"net/http"
6+
"net/http/pprof"
57

68
"github.com/dgryski/httputil"
9+
"github.com/go-graphite/carbonapi/cmd/carbonapi/config"
710
"github.com/go-graphite/carbonapi/util/ctx"
811
)
912

1013
func InitHandlers(headersToPass, headersToLog []string) *http.ServeMux {
11-
r := http.DefaultServeMux
12-
r.HandleFunc("/render/", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(renderHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
13-
r.HandleFunc("/render", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(renderHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
14+
r := http.NewServeMux()
15+
r.HandleFunc(config.Config.Prefix+"/render/", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(renderHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
16+
r.HandleFunc(config.Config.Prefix+"/render", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(renderHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
1417

15-
r.HandleFunc("/metrics/find/", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(findHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
16-
r.HandleFunc("/metrics/find", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(findHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
18+
r.HandleFunc(config.Config.Prefix+"/metrics/find/", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(findHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
19+
r.HandleFunc(config.Config.Prefix+"/metrics/find", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(findHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
1720

18-
r.HandleFunc("/info/", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(infoHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
19-
r.HandleFunc("/info", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(infoHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
21+
r.HandleFunc(config.Config.Prefix+"/info/", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(infoHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
22+
r.HandleFunc(config.Config.Prefix+"/info", httputil.TrackConnections(httputil.TimeHandler(enrichContextWithHeaders(headersToPass, headersToLog, ctx.ParseCtx(infoHandler, ctx.HeaderUUIDAPI)), bucketRequestTimes)))
2023

21-
r.HandleFunc("/lb_check", lbcheckHandler)
24+
r.HandleFunc(config.Config.Prefix+"/lb_check", lbcheckHandler)
2225

23-
r.HandleFunc("/version", versionHandler)
24-
r.HandleFunc("/version/", versionHandler)
26+
r.HandleFunc(config.Config.Prefix+"/version", versionHandler)
27+
r.HandleFunc(config.Config.Prefix+"/version/", versionHandler)
2528

26-
r.HandleFunc("/functions", enrichContextWithHeaders(headersToPass, headersToLog, functionsHandler))
27-
r.HandleFunc("/functions/", enrichContextWithHeaders(headersToPass, headersToLog, functionsHandler))
29+
r.HandleFunc(config.Config.Prefix+"/functions", enrichContextWithHeaders(headersToPass, headersToLog, functionsHandler))
30+
r.HandleFunc(config.Config.Prefix+"/functions/", enrichContextWithHeaders(headersToPass, headersToLog, functionsHandler))
2831

29-
r.HandleFunc("/tags", enrichContextWithHeaders(headersToPass, headersToLog, tagHandler))
30-
r.HandleFunc("/tags/", enrichContextWithHeaders(headersToPass, headersToLog, tagHandler))
32+
r.HandleFunc(config.Config.Prefix+"/tags", enrichContextWithHeaders(headersToPass, headersToLog, tagHandler))
33+
r.HandleFunc(config.Config.Prefix+"/tags/", enrichContextWithHeaders(headersToPass, headersToLog, tagHandler))
3134

32-
r.HandleFunc("/", enrichContextWithHeaders(headersToPass, headersToLog, usageHandler))
35+
r.HandleFunc(config.Config.Prefix+"/", enrichContextWithHeaders(headersToPass, headersToLog, usageHandler))
36+
37+
if config.Config.Expvar.Enabled {
38+
if config.Config.Expvar.Listen == "" || config.Config.Expvar.Listen == config.Config.Listen {
39+
r.HandleFunc(config.Config.Prefix+"/debug/vars", expvar.Handler().ServeHTTP)
40+
if config.Config.Expvar.PProfEnabled {
41+
r.HandleFunc(config.Config.Prefix+"/debug/pprof/heap", pprof.Index)
42+
r.HandleFunc(config.Config.Prefix+"/debug/pprof/profile", pprof.Profile)
43+
r.HandleFunc(config.Config.Prefix+"/debug/pprof/symbol", pprof.Symbol)
44+
r.HandleFunc(config.Config.Prefix+"/debug/pprof/trace", pprof.Trace)
45+
}
46+
}
47+
}
3348
return r
3449
}

cmd/carbonapi/main.go

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package main
22

33
import (
4+
"expvar"
45
"flag"
56
"log"
67
"net/http"
8+
"net/http/pprof"
79
_ "net/http/pprof"
10+
"sync"
811

912
"github.com/facebookgo/grace/gracehttp"
1013
"github.com/go-graphite/carbonapi/cmd/carbonapi/config"
@@ -46,14 +49,60 @@ func main() {
4649
handler = handlers.CORS()(handler)
4750
handler = handlers.ProxyHeaders(handler)
4851

49-
err = gracehttp.Serve(&http.Server{
50-
Addr: config.Config.Listen,
51-
Handler: handler,
52-
})
52+
wg := sync.WaitGroup{}
53+
if config.Config.Expvar.Enabled {
54+
if config.Config.Expvar.Listen != "" || config.Config.Expvar.Listen != config.Config.Listen {
55+
r := http.NewServeMux()
56+
r.HandleFunc(config.Config.Prefix+"/debug/vars", expvar.Handler().ServeHTTP)
57+
if config.Config.Expvar.PProfEnabled {
58+
r.HandleFunc(config.Config.Prefix+"/debug/pprof/heap", pprof.Index)
59+
r.HandleFunc(config.Config.Prefix+"/debug/pprof/profile", pprof.Profile)
60+
r.HandleFunc(config.Config.Prefix+"/debug/pprof/symbol", pprof.Symbol)
61+
r.HandleFunc(config.Config.Prefix+"/debug/pprof/trace", pprof.Trace)
62+
}
5363

54-
if err != nil {
55-
logger.Fatal("gracehttp failed",
56-
zap.Error(err),
57-
)
64+
handler := handlers.CompressHandler(r)
65+
handler = handlers.CORS()(handler)
66+
handler = handlers.ProxyHeaders(handler)
67+
68+
logger.Info("expvar handler will listen on a separate address/port",
69+
zap.String("expvar_listen", config.Config.Expvar.Listen),
70+
zap.Bool("pprof_enabled", config.Config.Expvar.PProfEnabled),
71+
)
72+
73+
wg.Add(1)
74+
go func() {
75+
err = gracehttp.Serve(&http.Server{
76+
Addr: config.Config.Expvar.Listen,
77+
Handler: handler,
78+
})
79+
80+
if err != nil {
81+
logger.Fatal("gracehttp failed",
82+
zap.Error(err),
83+
)
84+
}
85+
86+
wg.Done()
87+
}()
88+
}
5889
}
90+
91+
wg.Add(1)
92+
go func() {
93+
err = gracehttp.Serve(&http.Server{
94+
Addr: config.Config.Listen,
95+
Handler: handler,
96+
})
97+
98+
if err != nil {
99+
logger.Fatal("gracehttp failed",
100+
zap.Error(err),
101+
)
102+
}
103+
104+
wg.Done()
105+
}()
106+
107+
wg.Wait()
59108
}

doc/configuration.md

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,48 @@ Table of Contents
44
* [General configuration for carbonapi](#general-configuration-for-carbonapi)
55
* [listen](#listen)
66
* [Example:](#example)
7-
* [headersToPass](#headerstopass)
7+
* [prefix](#prefix)
88
* [Example:](#example-1)
9-
* [headersToLog](#headerstolog)
9+
* [headersToPass](#headerstopass)
1010
* [Example:](#example-2)
11-
* [headersToLog](#define)
11+
* [headersToLog](#headerstolog)
1212
* [Example:](#example-3)
13+
* [headersToLog](#define)
14+
* [Example:](#example-4)
1315
* [unicodeRangeTables](#unicoderangetables)
14-
* [Example](#example-4)
15-
* [cache](#cache)
1616
* [Example](#example-5)
17-
* [cpus](#cpus)
17+
* [cache](#cache)
1818
* [Example](#example-6)
19-
* [tz](#tz)
19+
* [cpus](#cpus)
2020
* [Example](#example-7)
21-
* [functionsConfig](#functionsconfig)
21+
* [tz](#tz)
2222
* [Example](#example-8)
23-
* [graphite](#graphite)
23+
* [functionsConfig](#functionsconfig)
2424
* [Example](#example-9)
25-
* [pidFile](#pidfile)
25+
* [graphite](#graphite)
2626
* [Example](#example-10)
27-
* [graphTemplates](#graphtemplates)
27+
* [pidFile](#pidfile)
2828
* [Example](#example-11)
29-
* [defaultColors](#defaultcolors)
29+
* [graphTemplates](#graphtemplates)
3030
* [Example](#example-12)
31-
* [logger](#logger)
31+
* [defaultColors](#defaultcolors)
3232
* [Example](#example-13)
33+
* [expvar](#expvar)
34+
* [Example](#example-14)
35+
* [logger](#logger)
36+
* [Example](#example-15)
3337
* [Carbonzipper configuration](#carbonzipper-configuration)
3438
* [concurency](#concurency)
35-
* [Example](#example-14)
39+
* [Example](#example-16)
3640
* [maxBatchSize](#maxbatchsize)
37-
* [Example](#example-15)
41+
* [Example](#example-17)
3842
* [idleConnections](#idleconnections)
3943
* [upstreams](#upstreams)
40-
* [Example](#example-16)
44+
* [Example](#example-18)
4145
* [For go\-carbon and prometheus](#for-go-carbon-and-prometheus)
4246
* [For graphite\-clickhouse](#for-graphite-clickhouse)
4347
* [expireDelaySec](#expiredelaysec)
44-
* [Example](#example-17)
48+
* [Example](#example-19)
4549

4650
# General configuration for carbonapi
4751

@@ -65,6 +69,20 @@ This will make it available on all IPv4 addresses, port 8080:
6569
listen: "0.0.0.0:8080"
6670
```
6771
72+
***
73+
## prefix
74+
75+
Specify prefix for all URLs. Might be useful when you cannot afford to listen on different port.
76+
77+
Default: None
78+
79+
### Example:
80+
This will make carbonapi handlers accessible on `/graphite`, e.x. `http://localhost:8080/render` will become `http://localhost:8080/graphite/render`
81+
82+
```yaml
83+
prefix: "graphite"
84+
```
85+
6886
***
6987
## headersToPass
7088

@@ -111,7 +129,7 @@ Defines are done by templating this custom aliases to known set of functions.
111129

112130
Templating is done by utilizing golang text template language.
113131

114-
Supported values:
132+
Supported variables:
115133
- argString - argument as a string. E.x. in query `myDefine(foo, bar, baz)`, argString will be `foo, bar, baz`
116134
- args - indexed array of arguments. E.x. in case of `myDefine(foo, bar)`, `index .args 0` will be first argument, `index .args 1` will be second
117135
- kwargs - key-value arguments (map). This is required to support cases like `myDefine(foo, bar=baz)`, in this case `index .args 0` will contain `foo`, and `index .kwargs "bar"` will contain `baz`
@@ -285,6 +303,33 @@ defaultColors:
285303
"darkblue": "002173"
286304
```
287305

306+
***
307+
## expvar
308+
309+
Controls whether expvar (contains internal metrics, config, etc) is enabled and if it's accessible on a separate address:port.
310+
Also allows to enable pprof handlers (useful for profiling and debugging).
311+
312+
Please note, that exposing pprof handlers to untrusted network is *dangerous* and might lead to data leak.
313+
314+
Exposing expvars to untrusted network is not recommended as it might give 3rd party unnecessary amount of data about your infrastructure.
315+
316+
### Example
317+
This describes current defaults: expvar enabled, pprof handlers disabled, listen on the same address-port as main application.
318+
```yaml
319+
expvar:
320+
enabled: true
321+
pprofEnabled: false
322+
listen: ""
323+
```
324+
325+
This is useful to enable debugging and to move all related handlers and add exposed only on localhost, port 7070.
326+
```yaml
327+
expvar:
328+
enabled: true
329+
pprofEnabled: true
330+
listen: "localhost:7070"
331+
```
332+
288333
***
289334
## logger
290335

0 commit comments

Comments
 (0)