Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions apis/v1alpha2/nginxproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,12 @@ type NginxLogging struct {
// +optional
// +kubebuilder:default=info
AgentLevel *AgentLogLevel `json:"agentLevel,omitempty"`

// AccessLog defines the access log settings, including format itself and disabling option.
// For now only path /dev/stdout can be used.
//
// +optional
AccessLog *NginxAccessLog `json:"accessLog,omitempty"`
}

// NginxErrorLogLevel type defines the log level of error logs for NGINX.
Expand Down Expand Up @@ -352,6 +358,22 @@ const (
AgentLogLevelFatal AgentLogLevel = "fatal"
)

// NginxAccessLog defines the configuration for an NGINX access log.
type NginxAccessLog struct {
// Disabled turns off access logging when set to true.
//
// +optional
Disabled *bool `json:"disabled,omitempty"`

// Format specifies the custom log format string.
// If not specified, NGINX default 'combined' format is used.
// For now only path /dev/stdout can be used.
// See https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format
//
// +optional
Format *string `json:"format,omitempty"`
}

// NginxPlus specifies NGINX Plus additional settings. These will only be applied if NGINX Plus is being used.
type NginxPlus struct {
// AllowedAddresses specifies IPAddresses or CIDR blocks to the allow list for accessing the NGINX Plus API.
Expand Down
30 changes: 30 additions & 0 deletions apis/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions config/crd/bases/gateway.nginx.org_nginxproxies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8016,6 +8016,23 @@ spec:
logging:
description: Logging defines logging related settings for NGINX.
properties:
accessLog:
description: |-
AccessLog defines the access log settings, including format itself and disabling option.
For now only path /dev/stdout can be used.
properties:
disabled:
description: Disabled turns off access logging when set to
true.
type: boolean
format:
description: |-
Format specifies the custom log format string.
If not specified, NGINX default 'combined' format is used.
For now only path /dev/stdout can be used.
See https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format
type: string
type: object
agentLevel:
default: info
description: |-
Expand Down
17 changes: 17 additions & 0 deletions deploy/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8603,6 +8603,23 @@ spec:
logging:
description: Logging defines logging related settings for NGINX.
properties:
accessLog:
description: |-
AccessLog defines the access log settings, including format itself and disabling option.
For now only path /dev/stdout can be used.
properties:
disabled:
description: Disabled turns off access logging when set to
true.
type: boolean
format:
description: |-
Format specifies the custom log format string.
If not specified, NGINX default 'combined' format is used.
For now only path /dev/stdout can be used.
See https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format
type: string
type: object
agentLevel:
default: info
description: |-
Expand Down
24 changes: 24 additions & 0 deletions internal/controller/nginx/config/base_http_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ import (

var baseHTTPTemplate = gotemplate.Must(gotemplate.New("baseHttp").Parse(baseHTTPTemplateText))

type AccessLog struct {
Format string // User's format string
Path string // Where to write logs (/dev/stdout)
FormatName string // Internal format name (ngf_user_defined_log_format)
Disabled bool // User's disabled flag
}
type httpConfig struct {
DNSResolver *dataplane.DNSResolverConfig
AccessLog *AccessLog
Includes []shared.Include
NginxReadinessProbePort int32
IPFamily shared.IPFamily
Expand All @@ -27,6 +34,7 @@ func executeBaseHTTPConfig(conf dataplane.Configuration) []executeResult {
NginxReadinessProbePort: conf.BaseHTTPConfig.NginxReadinessProbePort,
IPFamily: getIPFamily(conf.BaseHTTPConfig),
DNSResolver: conf.BaseHTTPConfig.DNSResolver,
AccessLog: buildAccessLog(conf.Logging.AccessLog),
}

results := make([]executeResult, 0, len(includes)+1)
Expand All @@ -38,3 +46,19 @@ func executeBaseHTTPConfig(conf dataplane.Configuration) []executeResult {

return results
}

func buildAccessLog(accessLogConfig *dataplane.AccessLog) *AccessLog {
if accessLogConfig != nil {
accessLog := &AccessLog{
Path: dataplane.DefaultAccessLogPath,
FormatName: dataplane.DefaultLogFormatName,
}
if accessLogConfig.Format != "" {
accessLog.Format = accessLogConfig.Format
}
accessLog.Disabled = accessLogConfig.Disabled

return accessLog
}
return nil
}
13 changes: 13 additions & 0 deletions internal/controller/nginx/config/base_http_config_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ server {
}
}

{{- /* Define custom log format */ -}}
{{- /* We use a fixed name for user-defined log format to avoid complexity of passing the name around. */ -}}
{{- if .AccessLog }}
{{- if .AccessLog.Disabled }}
access_log off;
{{- else }}
{{- if .AccessLog.Format }}
log_format {{ .AccessLog.FormatName }} '{{ .AccessLog.Format }}';
access_log {{ .AccessLog.Path }} {{ .AccessLog.FormatName }};
{{- end }}
{{- end }}
{{- end }}

{{ range $i := .Includes -}}
include {{ $i.Name }};
{{ end -}}
Expand Down
72 changes: 72 additions & 0 deletions internal/controller/nginx/config/base_http_config_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"fmt"
"sort"
"strings"
"testing"
Expand All @@ -10,6 +11,77 @@ import (
"github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/dataplane"
)

func TestLoggingSettingsTemplate(t *testing.T) {
t.Parallel()

logFormat := "$remote_addr - [$time_local] \"$request\" $status $body_bytes_sent"

tests := []struct {
name string
accessLog *dataplane.AccessLog
expectedOutputs []string
unexpectedOutputs []string
}{
{
name: "Log format and access log with custom format",
accessLog: &dataplane.AccessLog{Format: logFormat},
expectedOutputs: []string{
fmt.Sprintf("log_format %s '%s'", dataplane.DefaultLogFormatName, logFormat),
fmt.Sprintf("access_log %s %s", dataplane.DefaultAccessLogPath, dataplane.DefaultLogFormatName),
},
},
{
name: "Empty format",
accessLog: &dataplane.AccessLog{Format: ""},
unexpectedOutputs: []string{
fmt.Sprintf("log_format %s '%s'", dataplane.DefaultLogFormatName, logFormat),
fmt.Sprintf("access_log %s %s", dataplane.DefaultAccessLogPath, dataplane.DefaultLogFormatName),
},
},
{
name: "Access log off while format presented",
accessLog: &dataplane.AccessLog{Disabled: true, Format: logFormat},
expectedOutputs: []string{
`access_log off;`,
},
unexpectedOutputs: []string{
fmt.Sprintf("access_log off %s", dataplane.DefaultLogFormatName),
},
},
{
name: "Access log off",
accessLog: &dataplane.AccessLog{Disabled: true},
expectedOutputs: []string{
`access_log off;`,
},
unexpectedOutputs: []string{
`log_format`,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
g := NewWithT(t)

conf := dataplane.Configuration{
Logging: dataplane.Logging{AccessLog: tt.accessLog},
}

res := executeBaseHTTPConfig(conf)
g.Expect(res).To(HaveLen(1))
httpConfig := string(res[0].data)
for _, expectedOutput := range tt.expectedOutputs {
g.Expect(httpConfig).To(ContainSubstring(expectedOutput))
}
for _, unexpectedOutput := range tt.unexpectedOutputs {
g.Expect(httpConfig).ToNot(ContainSubstring(unexpectedOutput))
}
})
}
}

func TestExecuteBaseHttp_HTTP2(t *testing.T) {
t.Parallel()
confOn := dataplane.Configuration{
Expand Down
28 changes: 28 additions & 0 deletions internal/controller/state/dataplane/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ const (
defaultErrorLogLevel = "info"
DefaultWorkerConnections = int32(1024)
DefaultNginxReadinessProbePort = int32(8081)
// DefaultLogFormatName is used when user provides custom access_log format.
DefaultLogFormatName = "ngf_user_defined_log_format"
// DefaultAccessLogPath is the default path for the access log.
DefaultAccessLogPath = "/dev/stdout"
)

// BuildConfiguration builds the Configuration from the Graph.
Expand Down Expand Up @@ -1206,6 +1210,8 @@ func convertAddresses(addresses []ngfAPIv1alpha2.RewriteClientIPAddress) []strin
return trustedAddresses
}

// buildLogging converts the API logging spec (currently singular LogFormat / AccessLog fields
// in v1alpha2) into internal representation used by templates.
func buildLogging(gateway *graph.Gateway) Logging {
logSettings := Logging{ErrorLevel: defaultErrorLogLevel}

Expand All @@ -1218,11 +1224,33 @@ func buildLogging(gateway *graph.Gateway) Logging {
if ngfProxy.Logging.ErrorLevel != nil {
logSettings.ErrorLevel = string(*ngfProxy.Logging.ErrorLevel)
}

srcLogSettings := ngfProxy.Logging

if accessLog := buildAccessLog(srcLogSettings); accessLog != nil {
logSettings.AccessLog = accessLog
}
}

return logSettings
}

func buildAccessLog(srcLogSettings *ngfAPIv1alpha2.NginxLogging) *AccessLog {
if srcLogSettings.AccessLog != nil {
if srcLogSettings.AccessLog.Disabled != nil && *srcLogSettings.AccessLog.Disabled {
return &AccessLog{Disabled: true}
}

if srcLogSettings.AccessLog.Format != nil && *srcLogSettings.AccessLog.Format != "" {
return &AccessLog{
Format: *srcLogSettings.AccessLog.Format,
}
}
}

return nil
}

func buildWorkerConnections(gateway *graph.Gateway) int32 {
if gateway == nil || gateway.EffectiveNginxProxy == nil {
return DefaultWorkerConnections
Expand Down
Loading
Loading