Skip to content

Commit

Permalink
Add support for JSON logging.
Browse files Browse the repository at this point in the history
Updates #624

Signed-off-by: Nick Young <ynick@vmware.com>
  • Loading branch information
Nick Young committed Sep 16, 2019
1 parent 43d0a89 commit 831c618
Show file tree
Hide file tree
Showing 10 changed files with 376 additions and 90 deletions.
3 changes: 3 additions & 0 deletions cmd/contour/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func registerServe(app *kingpin.Application) (*kingpin.CmdClause, *serveContext)
serve.Flag("envoy-service-https-port", "Kubernetes Service port for HTTPS requests").IntVar(&ctx.httpsPort)
serve.Flag("use-proxy-protocol", "Use PROXY protocol for all listeners").BoolVar(&ctx.useProxyProto)

serve.Flag("accesslog-format", "Format for Envoy access logs").StringVar(&ctx.AccessLogFormat)
serve.Flag("disable-leader-election", "Disable leader election mechanism").BoolVar(&ctx.DisableLeaderElection)
return serve, ctx
}
Expand Down Expand Up @@ -141,6 +142,8 @@ func doServe(log logrus.FieldLogger, ctx *serveContext) error {
HTTPSAddress: ctx.httpsAddr,
HTTPSPort: ctx.httpsPort,
HTTPSAccessLog: ctx.httpsAccessLog,
AccessLogType: ctx.AccessLogFormat,
AccessLogFields: ctx.AccessLogFields,
MinimumProtocolVersion: dag.MinProtoVersion(ctx.TLSConfig.MinimumProtocolVersion),
},
ListenerCache: contour.NewListenerCache(ctx.statsAddr, ctx.statsPort),
Expand Down
34 changes: 34 additions & 0 deletions cmd/contour/servecontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ type serveContext struct {
httpsPort int
httpsAccessLog string

// Envoy's access logging format options

// AccessLogFormat sets the global access log format.
// Valid options are 'clf' or 'json'
AccessLogFormat string `yaml:"accesslog-format"`

// AccessLogFields sets the fields that JSON logging will
// output when AccessLogFormat is json.
AccessLogFields []string `yaml:"json-fields"`

// PermitInsecureGRPC disables TLS on Contour's gRPC listener.
PermitInsecureGRPC bool `yaml:"-"`

Expand Down Expand Up @@ -109,6 +119,30 @@ func newServeContext() *serveContext {
PermitInsecureGRPC: false,
DisablePermitInsecure: false,
DisableLeaderElection: false,
AccessLogFormat: "clf",
AccessLogFields: []string{
"@timestamp",
"authority",
"bytes_received",
"bytes_sent",
"downstream_local_address",
"downstream_remote_address",
"duration",
"method",
"path",
"protocol",
"request_id",
"requested_server_name",
"response_code",
"response_flags",
"uber_trace_id",
"upstream_cluster",
"upstream_host",
"upstream_local_address",
"upstream_service_time",
"user_agent",
"x_forwarded_for",
},
LeaderElectionConfig: LeaderElectionConfig{
LeaseDuration: time.Second * 15,
RenewDeadline: time.Second * 10,
Expand Down
83 changes: 80 additions & 3 deletions internal/contour/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

envoy_api_v2_auth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
envoy_api_v2_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"
envoy_api_v2_accesslog "github.com/envoyproxy/go-control-plane/envoy/config/filter/accesslog/v2"

v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
"github.com/envoyproxy/go-control-plane/pkg/cache"
Expand All @@ -36,6 +37,7 @@ const (
DEFAULT_HTTPS_ACCESS_LOG = "/dev/stdout"
DEFAULT_HTTPS_LISTENER_ADDRESS = DEFAULT_HTTP_LISTENER_ADDRESS
DEFAULT_HTTPS_LISTENER_PORT = 8443
DEFAULT_ACCESS_LOG_TYPE = "clf"
)

// ListenerVisitorConfig holds configuration parameters for visitListeners.
Expand Down Expand Up @@ -71,6 +73,16 @@ type ListenerVisitorConfig struct {

// MinimumProtocolVersion defines the min tls protocol version to be used
MinimumProtocolVersion envoy_api_v2_auth.TlsParameters_TlsProtocol

// AccessLogType defines if Envoy logs should be output as CLF or JSON.
// Valid values: 'clf', 'json'
// If not set, defaults to 'clf'
AccessLogType string

// AccessLogFields sets the fields that should be shown in JSON logs.
// Valid entries are the keys from internal/envoy/accesslog.go:jsonheaders
// Defaults to a particular set of fields.
AccessLogFields []string
}

// httpAddress returns the port for the HTTP (non TLS)
Expand Down Expand Up @@ -127,6 +139,68 @@ func (lvc *ListenerVisitorConfig) httpsAccessLog() string {
return DEFAULT_HTTPS_ACCESS_LOG
}

// accesslogType returns the access log type that should be configured
// across all listener types or DEFAULT_ACCESS_LOG_TYPE if not configured.
func (lvc *ListenerVisitorConfig) accesslogType() string {
if lvc.AccessLogType != "" {
return lvc.AccessLogType
}
return DEFAULT_ACCESS_LOG_TYPE
}

// accesslogFields returns the access log fields that should be configured
// for Envoy, or a default set if not configured.
func (lvc *ListenerVisitorConfig) accesslogFields() []string {
if lvc.AccessLogFields != nil {
return lvc.AccessLogFields
}
return []string{
"@timestamp",
"authority",
"bytes_received",
"bytes_sent",
"downstream_local_address",
"downstream_remote_address",
"duration",
"method",
"path",
"protocol",
"request_id",
"requested_server_name",
"response_code",
"response_flags",
"uber_trace_id",
"upstream_cluster",
"upstream_host",
"upstream_local_address",
"upstream_service_time",
"user_agent",
"x_forwarded_for",
}
}

func (lvc *ListenerVisitorConfig) newInsecureAccessLog() []*envoy_api_v2_accesslog.AccessLog {
var accesslogger []*envoy_api_v2_accesslog.AccessLog
switch lvc.accesslogType() {
case "json":
accesslogger = envoy.FileAccessLogJSON(lvc.httpAccessLog(), lvc.accesslogFields())
default:
accesslogger = envoy.FileAccessLog(lvc.httpAccessLog())
}
return accesslogger
}

func (lvc *ListenerVisitorConfig) newSecureAccessLog() []*envoy_api_v2_accesslog.AccessLog {
var accesslogger []*envoy_api_v2_accesslog.AccessLog
switch lvc.accesslogType() {
case "json":
accesslogger = envoy.FileAccessLogJSON(lvc.httpsAccessLog(), lvc.accesslogFields())
default:
accesslogger = envoy.FileAccessLog(lvc.httpsAccessLog())
}
return accesslogger
}

// minProtocolVersion returns the requested minimum TLS protocol
// version or envoy_api_v2_auth.TlsParameters_TLSv1_1 if not configured {
func (lvc *ListenerVisitorConfig) minProtoVersion() envoy_api_v2_auth.TlsParameters_TlsProtocol {
Expand Down Expand Up @@ -178,6 +252,8 @@ func (c *ListenerCache) Contents() []proto.Message {
return values
}

// Query returns the proto.Messages in the ListenerCache that match
// a slice of strings
func (c *ListenerCache) Query(names []string) []proto.Message {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down Expand Up @@ -209,6 +285,7 @@ func (l listenersByName) Less(i, j int) bool {
return l[i].(*v2.Listener).Name < l[j].(*v2.Listener).Name
}

// TypeURL returns the cache's Listener Type.
func (*ListenerCache) TypeURL() string { return cache.ListenerType }

type listenerVisitor struct {
Expand Down Expand Up @@ -237,7 +314,7 @@ func visitListeners(root dag.Vertex, lvc *ListenerVisitorConfig) map[string]*v2.
ENVOY_HTTP_LISTENER,
lvc.httpAddress(), lvc.httpPort(),
proxyProtocol(lvc.UseProxyProto),
envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, lvc.httpAccessLog()),
envoy.HTTPConnectionManager(ENVOY_HTTP_LISTENER, lvc.newInsecureAccessLog()),
)

}
Expand Down Expand Up @@ -289,12 +366,12 @@ func (v *listenerVisitor) visit(vertex dag.Vertex) {
v.http = true
case *dag.SecureVirtualHost:
filters := envoy.Filters(
envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, v.httpsAccessLog()),
envoy.HTTPConnectionManager(ENVOY_HTTPS_LISTENER, v.ListenerVisitorConfig.newSecureAccessLog()),
)
alpnProtos := []string{"h2", "http/1.1"}
if vh.TCPProxy != nil {
filters = envoy.Filters(
envoy.TCPProxy(ENVOY_HTTPS_LISTENER, vh.TCPProxy, v.httpsAccessLog()),
envoy.TCPProxy(ENVOY_HTTPS_LISTENER, vh.TCPProxy, v.ListenerVisitorConfig.newSecureAccessLog()),
)
alpnProtos = nil // do not offer ALPN
}
Expand Down
Loading

0 comments on commit 831c618

Please sign in to comment.