apps/apache.go (144 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 MetricsReceiverApache struct {
confgenerator.ConfigComponent `yaml:",inline"`
confgenerator.MetricsReceiverShared `yaml:",inline"`
ServerStatusURL string `yaml:"server_status_url" validate:"omitempty,url"`
}
const defaultServerStatusURL = "http://127.0.0.1:80/server-status?auto"
func (r MetricsReceiverApache) Type() string {
return "apache"
}
func (r MetricsReceiverApache) Pipelines(_ context.Context) ([]otel.ReceiverPipeline, error) {
if r.ServerStatusURL == "" {
r.ServerStatusURL = defaultServerStatusURL
}
return []otel.ReceiverPipeline{{
Receiver: otel.Component{
Type: "apache",
Config: map[string]interface{}{
"collection_interval": r.CollectionIntervalString(),
"endpoint": r.ServerStatusURL,
},
},
Processors: map[string][]otel.Component{"metrics": {
otel.MetricsFilter(
"exclude",
"strict",
"apache.uptime",
),
otel.NormalizeSums(),
otel.MetricsTransform(
otel.AddPrefix("workload.googleapis.com"),
),
otel.TransformationMetrics(
otel.FlattenResourceAttribute("apache.server.name", "server_name"),
),
otel.ModifyInstrumentationScope(r.Type(), "1.0"),
}},
}}, nil
}
func init() {
confgenerator.MetricsReceiverTypes.RegisterType(func() confgenerator.MetricsReceiver { return &MetricsReceiverApache{} })
}
type LoggingProcessorApacheError struct {
confgenerator.ConfigComponent `yaml:",inline"`
}
func (LoggingProcessorApacheError) Type() string {
return "apache_error"
}
func (p LoggingProcessorApacheError) Components(ctx context.Context, tag string, uid string) []fluentbit.Component {
c := confgenerator.LoggingProcessorParseRegex{
// Documentation: https://httpd.apache.org/docs/current/logs.html#errorlog
// Sample line 2.4: [Fri Sep 09 10:42:29.902022 2011] [core:error] [pid 35708:tid 4328636416] (13)Permission denied [client 72.15.99.187] File does not exist: /usr/local/apache2/htdocs/favicon.ico
// [Thu Sep 30 03:18:29.239182 2021] [ssl:error] [pid 2451:tid 140169666050176] AH02217: ssl_stapling_init_cert: Can't retrieve issuer certificate!
// Sample line 2.2: [Fri Sep 09 10:42:29.902022 2011] [error] [pid 35708:tid 4328636416] [client 72.15.99.187] File does not exist: /usr/local/apache2/htdocs/favicon.ico
// TODO - Support time parsing for version 2.0 where smallest resolution is seconds
// Sample line 2.0: [Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] client denied by server configuration: /export/home/live/ap/htdocs/test
Regex: `^\[(?<time>[^\]]+)\] \[(?:(?<module>\w+):)?(?<level>[\w\d]+)\](?: \[pid (?<pid>\d+)(?::tid (?<tid>[0-9]+))?\])?(?: (?<errorCode>[^\[:]*):?)?(?: \[client (?<client>[^\]]*)\])? (?<message>.*)$`,
ParserShared: confgenerator.ParserShared{
TimeKey: "time",
TimeFormat: "%a %b %d %H:%M:%S.%L %Y",
Types: map[string]string{
"pid": "integer",
"tid": "integer",
},
},
}.Components(ctx, tag, uid)
// Log levels documented: https://httpd.apache.org/docs/2.4/mod/core.html#loglevel
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",
"trace1": "DEBUG",
"trace2": "DEBUG",
"trace3": "DEBUG",
"trace4": "DEBUG",
"trace5": "DEBUG",
"trace6": "DEBUG",
"trace7": "DEBUG",
"trace8": "DEBUG",
},
MapValuesExclusive: true,
},
InstrumentationSourceLabel: instrumentationSourceValue(p.Type()),
},
}.Components(ctx, tag, uid)...,
)
return c
}
type LoggingProcessorApacheAccess struct {
confgenerator.ConfigComponent `yaml:",inline"`
}
func (p LoggingProcessorApacheAccess) Components(ctx context.Context, tag string, uid string) []fluentbit.Component {
return genericAccessLogParser(ctx, p.Type(), tag, uid)
}
func (LoggingProcessorApacheAccess) Type() string {
return "apache_access"
}
type LoggingReceiverApacheAccess struct {
LoggingProcessorApacheAccess `yaml:",inline"`
ReceiverMixin confgenerator.LoggingReceiverFilesMixin `yaml:",inline" validate:"structonly"`
}
func (r LoggingReceiverApacheAccess) Components(ctx context.Context, tag string) []fluentbit.Component {
if len(r.ReceiverMixin.IncludePaths) == 0 {
r.ReceiverMixin.IncludePaths = []string{
// Default log file path on Debian / Ubuntu
"/var/log/apache2/access.log",
// Default log file path RHEL / CentOS
"/var/log/apache2/access_log",
// Default log file path SLES
"/var/log/httpd/access_log",
}
}
c := r.ReceiverMixin.Components(ctx, tag)
c = append(c, r.LoggingProcessorApacheAccess.Components(ctx, tag, "apache_access")...)
return c
}
type LoggingReceiverApacheError struct {
LoggingProcessorApacheError `yaml:",inline"`
ReceiverMixin confgenerator.LoggingReceiverFilesMixin `yaml:",inline" validate:"structonly"`
}
func (r LoggingReceiverApacheError) Components(ctx context.Context, tag string) []fluentbit.Component {
if len(r.ReceiverMixin.IncludePaths) == 0 {
r.ReceiverMixin.IncludePaths = []string{
// Default log file path on Debian / Ubuntu
"/var/log/apache2/error.log",
// Default log file path RHEL / CentOS
"/var/log/apache2/error_log",
// Default log file path SLES
"/var/log/httpd/error_log",
}
}
c := r.ReceiverMixin.Components(ctx, tag)
c = append(c, r.LoggingProcessorApacheError.Components(ctx, tag, "apache_error")...)
return c
}
func init() {
confgenerator.LoggingProcessorTypes.RegisterType(func() confgenerator.LoggingProcessor { return &LoggingProcessorApacheAccess{} })
confgenerator.LoggingProcessorTypes.RegisterType(func() confgenerator.LoggingProcessor { return &LoggingProcessorApacheError{} })
confgenerator.LoggingReceiverTypes.RegisterType(func() confgenerator.LoggingReceiver { return &LoggingReceiverApacheAccess{} })
confgenerator.LoggingReceiverTypes.RegisterType(func() confgenerator.LoggingReceiver { return &LoggingReceiverApacheError{} })
}