apps/saphana.go (148 lines of code) (raw):

// Copyright 2022 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" "github.com/GoogleCloudPlatform/ops-agent/internal/secret" ) type LoggingProcessorSapHanaTrace struct { confgenerator.ConfigComponent `yaml:",inline"` } func (LoggingProcessorSapHanaTrace) Type() string { return "saphana" } func (p LoggingProcessorSapHanaTrace) Components(ctx context.Context, tag string, uid string) []fluentbit.Component { c := confgenerator.LoggingProcessorParseRegex{ // Undocumented Format: [thread_id]{connection_id}[transaction_id/update_transaction_id] timestamp severity_flag component source_file : message // Sample line: [7893]{200068}[20/40637286] 2021-11-04 13:13:25.025767 w FileIO FileSystem.cpp(00085) : Unsupported file system "ext4" for "/usr/sap/MMM/SYS/global/hdb/data/mnt00001" // Sample line: [18048]{-1}[-1/-1] 2020-11-10 12:24:23.424024 i Crypto RootKeyStoreAccessor.cpp(00818) : Created new root key /usr/sap/MMM/SYS/global/hdb/security/ssfs:HDB_SERVER/3/PERSISTENCE // Sample line: [18048]{-1}[-1/-1] 2020-11-10 12:24:20.988943 e commlib commlibImpl.cpp(00986) : ERROR: comm::connect to Host: 127.0.0.1, port: 30001, Error: exception 1: no.2110017 (Basis/IO/Stream/impl/NetworkChannel.cpp:2989) // System error: SO_ERROR has pending error for socket. rc=111: Connection refused. channel={<NetworkChannel>={<NetworkChannelBase>={this=139745749931736, fd=21, refCnt=1, local=127.0.0.1/58654_tcp, remote=127.0.0.1/30001_tcp, state=ConnectWait, pending=[----]}}} // exception throw location: // 1: 0x00007f1937d9095c in .LTHUNK27.lto_priv.2256+0x558 (libhdbbasis.so) // ... // 25: 0x0000563f44888831 in _GLOBAL__sub_I_setServiceStarting.cpp.lto_priv.239+0x520 (hdbnsutil) Regex: `^\[(?<thread_id>\d+)\]\{(?<connection_id>-?\d+)\}\[(?<transaction_id>-?\d+)\/(?<update_transaction_id>-?\d+)\]\s+(?<time>\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d{3,6}\d+)\s+(?<severity_flag>\w+)\s+(?<component>\w+)\s+(?<source_file>[\w\.]+)(?:\((?<source_line>\d+)\))\s+:\s+(?<message>[\s\S]+)`, ParserShared: confgenerator.ParserShared{ TimeKey: "time", TimeFormat: "%Y-%m-%d %H:%M:%S.%L", Types: map[string]string{ "thread_id": "int", "connection_id": "int", "transaction_id": "int", "update_transaction_id": "int", "source_line": "int", }, }, }.Components(ctx, tag, uid) c = append(c, confgenerator.LoggingProcessorModifyFields{ Fields: map[string]*confgenerator.ModifyField{ "severity": { CopyFrom: "jsonPayload.severity_flag", MapValues: map[string]string{ "d": "DEBUG", "i": "INFO", "w": "WARNING", "e": "ERROR", "f": "ALERT", }, MapValuesExclusive: true, }, // If a log is not associated with a connection/transaction the related // fields will be "-1", and we do not want to report those fields "jsonPayload.connection_id": { OmitIf: "jsonPayload.connection_id = -1", }, "jsonPayload.transaction_id": { OmitIf: "jsonPayload.transaction_id = -1", }, "jsonPayload.update_transaction_id": { OmitIf: "jsonPayload.update_transaction_id = -1", }, `sourceLocation.file`: { MoveFrom: "jsonPayload.source_file", }, `sourceLocation.line`: { MoveFrom: "jsonPayload.source_line", }, InstrumentationSourceLabel: instrumentationSourceValue(p.Type()), }, }.Components(ctx, tag, uid)..., ) return c } type LoggingReceiverSapHanaTrace struct { LoggingProcessorSapHanaTrace `yaml:",inline"` ReceiverMixin confgenerator.LoggingReceiverFilesMixin `yaml:",inline" validate:"structonly"` } func (r LoggingReceiverSapHanaTrace) Components(ctx context.Context, tag string) []fluentbit.Component { if len(r.ReceiverMixin.IncludePaths) == 0 { r.ReceiverMixin.IncludePaths = []string{ "/usr/sap/*/HDB*/${HOSTNAME}/trace/*.trc", } } if len(r.ReceiverMixin.ExcludePaths) == 0 { r.ReceiverMixin.ExcludePaths = []string{ "/usr/sap/*/HDB*/${HOSTNAME}/trace/nameserver_history*.trc", "/usr/sap/*/HDB*/${HOSTNAME}/trace/nameserver*loads*.trc", "/usr/sap/*/HDB*/${HOSTNAME}/trace/nameserver*executed_statements*.trc", } } r.ReceiverMixin.MultilineRules = []confgenerator.MultilineRule{ { StateName: "start_state", NextState: "cont", Regex: `^\[\d+\]\{-?\d+\}`, }, { StateName: "cont", NextState: "cont", Regex: `^(?!\[\d+\]\{-?\d+\})`, }, } c := r.ReceiverMixin.Components(ctx, tag) c = append(c, r.LoggingProcessorSapHanaTrace.Components(ctx, tag, r.Type())...) return c } func init() { confgenerator.LoggingProcessorTypes.RegisterType(func() confgenerator.LoggingProcessor { return &LoggingProcessorSapHanaTrace{} }) confgenerator.LoggingReceiverTypes.RegisterType(func() confgenerator.LoggingReceiver { return &LoggingReceiverSapHanaTrace{} }) } type MetricsReceiverSapHana struct { confgenerator.ConfigComponent `yaml:",inline"` confgenerator.MetricsReceiverSharedTLS `yaml:",inline"` confgenerator.MetricsReceiverShared `yaml:",inline"` Endpoint string `yaml:"endpoint" validate:"omitempty,hostname_port|startswith=/"` Password secret.String `yaml:"password" validate:"omitempty"` Username string `yaml:"username" validate:"omitempty"` } const defaultSapHanaEndpoint = "localhost:30015" func (s MetricsReceiverSapHana) Type() string { return "saphana" } func (s MetricsReceiverSapHana) Pipelines(_ context.Context) ([]otel.ReceiverPipeline, error) { if s.Endpoint == "" { s.Endpoint = defaultSapHanaEndpoint } return []otel.ReceiverPipeline{{ Receiver: otel.Component{ Type: "saphana", Config: map[string]interface{}{ "collection_interval": s.CollectionIntervalString(), "endpoint": s.Endpoint, "password": s.Password.SecretValue(), "username": s.Username, "tls": s.TLSConfig(true), }, }, Processors: map[string][]otel.Component{"metrics": { otel.MetricsFilter( "exclude", "strict", "saphana.uptime", ), otel.NormalizeSums(), otel.MetricsTransform( otel.AddPrefix("workload.googleapis.com"), ), otel.TransformationMetrics( otel.FlattenResourceAttribute("saphana.host", "host"), ), otel.ModifyInstrumentationScope(s.Type(), "1.0"), }}, }}, nil } func init() { confgenerator.MetricsReceiverTypes.RegisterType(func() confgenerator.MetricsReceiver { return &MetricsReceiverSapHana{} }) }