apps/nginx.go (121 lines of code) (raw):

// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package apps import ( "context" "github.com/GoogleCloudPlatform/ops-agent/confgenerator" "github.com/GoogleCloudPlatform/ops-agent/confgenerator/fluentbit" "github.com/GoogleCloudPlatform/ops-agent/confgenerator/otel" ) type MetricsReceiverNginx struct { confgenerator.ConfigComponent `yaml:",inline"` confgenerator.MetricsReceiverShared `yaml:",inline"` StubStatusURL string `yaml:"stub_status_url" validate:"omitempty,url"` } const defaultStubStatusURL = "http://127.0.0.1/status" func (r MetricsReceiverNginx) Type() string { return "nginx" } func (r MetricsReceiverNginx) Pipelines(_ context.Context) ([]otel.ReceiverPipeline, error) { if r.StubStatusURL == "" { r.StubStatusURL = defaultStubStatusURL } return []otel.ReceiverPipeline{{ Receiver: otel.Component{ Type: "nginx", Config: map[string]interface{}{ "collection_interval": r.CollectionIntervalString(), "endpoint": r.StubStatusURL, }, }, Processors: map[string][]otel.Component{"metrics": { otel.NormalizeSums(), otel.MetricsTransform( otel.AddPrefix("workload.googleapis.com"), ), otel.ModifyInstrumentationScope(r.Type(), "1.0"), }}, }}, nil } func init() { confgenerator.MetricsReceiverTypes.RegisterType(func() confgenerator.MetricsReceiver { return &MetricsReceiverNginx{} }) } type LoggingProcessorNginxAccess struct { confgenerator.ConfigComponent `yaml:",inline"` } func (LoggingProcessorNginxAccess) Type() string { return "nginx_access" } func (p LoggingProcessorNginxAccess) Components(ctx context.Context, tag string, uid string) []fluentbit.Component { return genericAccessLogParser(ctx, p.Type(), tag, uid) } type LoggingProcessorNginxError struct { confgenerator.ConfigComponent `yaml:",inline"` } func (LoggingProcessorNginxError) Type() string { return "nginx_error" } func (p LoggingProcessorNginxError) Components(ctx context.Context, tag string, uid string) []fluentbit.Component { c := confgenerator.LoggingProcessorParseRegex{ // Format is not documented, sadly. // Basic fields: https://github.com/nginx/nginx/blob/c231640eba9e26e963460c83f2907ac6f9abf3fc/src/core/ngx_log.c#L102 // Request fields: https://github.com/nginx/nginx/blob/7bcb50c0610a18bf43bef0062b2d2dc550823b53/src/http/ngx_http_request.c#L3836 // Sample line: 2021/08/26 16:50:17 [error] 29060#29060: *2191 open() "/var/www/html/forbidden.html" failed (13: Permission denied), client: ::1, server: _, request: "GET /forbidden.html HTTP/1.1", host: "localhost:8080" Regex: `^(?<time>[0-9]+[./-][0-9]+[./-][0-9]+[- ][0-9]+:[0-9]+:[0-9]+) \[(?<level>[^\]]*)\] (?<pid>[0-9]+)#(?<tid>[0-9]+):(?: \*(?<connection>[0-9]+))? (?<message>.*?)(?:, client: (?<client>[^,]+))?(?:, server: (?<server>[^,]+))?(?:, request: "(?<request>[^"]*)")?(?:, subrequest: \"(?<subrequest>[^\"]*)\")?(?:, upstream: \"(?<upstream>[^"]*)\")?(?:, host: \"(?<host>[^\"]*)\")?(?:, referrer: \"(?<referer>[^"]*)\")?$`, ParserShared: confgenerator.ParserShared{ TimeKey: "time", TimeFormat: "%Y/%m/%d %H:%M:%S", Types: map[string]string{ "pid": "integer", "tid": "integer", "connection": "integer", }, }, }.Components(ctx, tag, uid) // Log levels documented: https://github.com/nginx/nginx/blob/master/src/core/ngx_syslog.c#L31 c = append(c, confgenerator.LoggingProcessorModifyFields{ Fields: map[string]*confgenerator.ModifyField{ "severity": { CopyFrom: "jsonPayload.level", MapValues: map[string]string{ "emerg": "EMERGENCY", "alert": "ALERT", "crit": "CRITICAL", "error": "ERROR", "warn": "WARNING", "notice": "NOTICE", "info": "INFO", "debug": "DEBUG", }, MapValuesExclusive: true, }, InstrumentationSourceLabel: instrumentationSourceValue(p.Type()), }, }.Components(ctx, tag, uid)..., ) return c } type LoggingReceiverNginxAccess struct { LoggingProcessorNginxAccess `yaml:",inline"` ReceiverMixin confgenerator.LoggingReceiverFilesMixin `yaml:",inline" validate:"structonly"` } func (r LoggingReceiverNginxAccess) Components(ctx context.Context, tag string) []fluentbit.Component { if len(r.ReceiverMixin.IncludePaths) == 0 { r.ReceiverMixin.IncludePaths = []string{"/var/log/nginx/access.log"} } c := r.ReceiverMixin.Components(ctx, tag) c = append(c, r.LoggingProcessorNginxAccess.Components(ctx, tag, "nginx_access")...) return c } type LoggingReceiverNginxError struct { LoggingProcessorNginxError `yaml:",inline"` ReceiverMixin confgenerator.LoggingReceiverFilesMixin `yaml:",inline" validate:"structonly"` } func (r LoggingReceiverNginxError) Components(ctx context.Context, tag string) []fluentbit.Component { if len(r.ReceiverMixin.IncludePaths) == 0 { r.ReceiverMixin.IncludePaths = []string{"/var/log/nginx/error.log"} } c := r.ReceiverMixin.Components(ctx, tag) c = append(c, r.LoggingProcessorNginxError.Components(ctx, tag, "nginx_error")...) return c } func init() { confgenerator.LoggingProcessorTypes.RegisterType(func() confgenerator.LoggingProcessor { return &LoggingProcessorNginxAccess{} }) confgenerator.LoggingProcessorTypes.RegisterType(func() confgenerator.LoggingProcessor { return &LoggingProcessorNginxError{} }) confgenerator.LoggingReceiverTypes.RegisterType(func() confgenerator.LoggingReceiver { return &LoggingReceiverNginxAccess{} }) confgenerator.LoggingReceiverTypes.RegisterType(func() confgenerator.LoggingReceiver { return &LoggingReceiverNginxError{} }) }