apps/mysql.go (502 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"
"fmt"
"strings"
"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 MetricsReceiverMySql struct {
confgenerator.ConfigComponent `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 defaultMySqlUnixEndpoint = "/var/run/mysqld/mysqld.sock"
func (r MetricsReceiverMySql) Type() string {
return "mysql"
}
func (r MetricsReceiverMySql) Pipelines(_ context.Context) ([]otel.ReceiverPipeline, error) {
transport := "tcp"
if r.Endpoint == "" {
transport = "unix"
r.Endpoint = defaultMySqlUnixEndpoint
} else if strings.HasPrefix(r.Endpoint, "/") {
transport = "unix"
}
if r.Username == "" {
r.Username = "root"
}
return []otel.ReceiverPipeline{
{
Receiver: otel.Component{
Type: "mysql",
Config: map[string]interface{}{
"collection_interval": r.CollectionIntervalString(),
"endpoint": r.Endpoint,
"username": r.Username,
"password": r.Password.SecretValue(),
"transport": transport,
"metrics": map[string]interface{}{
"mysql.commands": map[string]interface{}{
"enabled": true,
},
"mysql.index.io.wait.count": map[string]interface{}{
"enabled": false,
},
"mysql.index.io.wait.time": map[string]interface{}{
"enabled": false,
},
"mysql.mysqlx_connections": map[string]interface{}{
"enabled": false,
},
"mysql.opened_resources": map[string]interface{}{
"enabled": false,
},
"mysql.tmp_resources": map[string]interface{}{
"enabled": false,
},
"mysql.prepared_statements": map[string]interface{}{
"enabled": false,
},
"mysql.table.io.wait.count": map[string]interface{}{
"enabled": false,
},
"mysql.table.io.wait.time": map[string]interface{}{
"enabled": false,
},
"mysql.replica.sql_delay": map[string]interface{}{
"enabled": true,
},
"mysql.replica.time_behind_source": map[string]interface{}{
"enabled": true,
},
},
},
},
Processors: map[string][]otel.Component{"metrics": {
otel.NormalizeSums(),
otel.MetricsTransform(
// The following changes are here to ensure maximum backwards compatibility after the fixes
// introduced https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/7924
otel.ChangePrefix("mysql\\.buffer_pool\\.", "mysql.buffer_pool_"),
otel.UpdateMetric("mysql.buffer_pool_pages",
otel.ToggleScalarDataType,
),
otel.UpdateMetric("mysql.threads",
otel.ToggleScalarDataType,
),
otel.RenameMetric("mysql.buffer_pool_usage", "mysql.buffer_pool_size",
otel.RenameLabel("status", "kind"),
otel.ToggleScalarDataType,
),
otel.AddPrefix("workload.googleapis.com"),
),
otel.ModifyInstrumentationScope(r.Type(), "1.0"),
}},
},
}, nil
}
func init() {
confgenerator.MetricsReceiverTypes.RegisterType(func() confgenerator.MetricsReceiver { return &MetricsReceiverMySql{} })
}
const (
// MySQL <5.7, MariaDB <10.1.4
timeRegexOld = `\d{6}\s+\d{1,2}:\d{2}:\d{2}`
timeFormatOld = "%y%m%d %H:%M:%S"
// MySQL >=5.7
timeRegexMySQLNew = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+(?:Z|[+-]\d{2}:?\d{2})?`
timeFormatMySQLNew = "%Y-%m-%dT%H:%M:%S.%L%z"
// MariaDB >=10.1.5 error log
timeRegexMariaDBNew = `\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}:\d{2}`
timeFormatMariaDBNew = "%Y-%m-%d %H:%M:%S"
)
type LoggingProcessorMysqlError struct {
confgenerator.ConfigComponent `yaml:",inline"`
}
func (LoggingProcessorMysqlError) Type() string {
return "mysql_error"
}
func (p LoggingProcessorMysqlError) Components(ctx context.Context, tag string, uid string) []fluentbit.Component {
c := confgenerator.LoggingProcessorParseRegexComplex{
Parsers: []confgenerator.RegexParser{
{
// MySql >=5.7 documented: https://dev.mysql.com/doc/refman/8.0/en/error-log-format.html
// Sample Line: 2020-08-06T14:25:02.936146Z 0 [Warning] [MY-010068] [Server] CA certificate /var/mysql/sslinfo/cacert.pem is self signed.
// Sample Line: 2020-08-06T14:25:03.109022Z 5 [Note] Event Scheduler: scheduler thread started with id 5
Regex: fmt.Sprintf(
`^(?<time>%s)\s+(?<tid>\d+)\s+\[(?<level>[^\]]+)](?:\s+\[(?<errorCode>[^\]]+)])?(?:\s+\[(?<subsystem>[^\]]+)])?\s+(?<message>.*)$`,
timeRegexMySQLNew,
),
Parser: confgenerator.ParserShared{
TimeKey: "time",
TimeFormat: timeFormatMySQLNew,
Types: map[string]string{
"tid": "integer",
},
},
},
{
// Mysql <5.7, MariaDB <10.1.4, documented: https://mariadb.com/kb/en/error-log/#format
// Sample Line: 160615 16:53:08 [Note] InnoDB: The InnoDB memory heap is disabled
// TODO - time is in system time, not UTC, is there a way to resolve this in fluent bit?
Regex: fmt.Sprintf(
`^(?<time>%s)\s+\[(?<level>[^\]]+)]\s+(?<message>.*)$`,
timeRegexOld,
),
Parser: confgenerator.ParserShared{
TimeKey: "time",
TimeFormat: timeFormatOld,
},
},
{
// MariaDB >=10.1.5, documented: https://mariadb.com/kb/en/error-log/#format
// Sample Line: 2016-06-15 1:53:33 139651251140544 [Note] InnoDB: The InnoDB memory heap is disabled
Regex: fmt.Sprintf(
`^(?<time>%s)(?:\s+(?<tid>\d+))?(?:\s+\[(?<level>[^\]]+)])?\s+(?<message>.*)$`,
timeRegexMariaDBNew,
),
Parser: confgenerator.ParserShared{
TimeKey: "time",
TimeFormat: timeFormatMariaDBNew,
Types: map[string]string{
"tid": "integer",
},
},
},
},
}.Components(ctx, tag, uid)
c = append(c,
confgenerator.LoggingProcessorModifyFields{
Fields: map[string]*confgenerator.ModifyField{
"severity": {
CopyFrom: "jsonPayload.level",
MapValues: map[string]string{
"ERROR": "ERROR",
"Error": "ERROR",
"WARNING": "WARNING",
"Warning": "WARNING",
"SYSTEM": "INFO",
"System": "INFO",
"NOTE": "NOTICE",
"Note": "NOTICE",
},
MapValuesExclusive: true,
},
InstrumentationSourceLabel: instrumentationSourceValue(p.Type()),
},
}.Components(ctx, tag, uid)...,
)
return c
}
type LoggingProcessorMysqlGeneral struct {
confgenerator.ConfigComponent `yaml:",inline"`
}
func (LoggingProcessorMysqlGeneral) Type() string {
return "mysql_general"
}
func (p LoggingProcessorMysqlGeneral) Components(ctx context.Context, tag string, uid string) []fluentbit.Component {
c := confgenerator.LoggingProcessorParseMultilineRegex{
LoggingProcessorParseRegexComplex: confgenerator.LoggingProcessorParseRegexComplex{
Parsers: []confgenerator.RegexParser{
{
// Limited documentation: https://dev.mysql.com/doc/refman/8.0/en/query-log.html
// Sample line: 2021-10-12T01:12:37.732966Z 14 Connect root@localhost on using Socket
// Sample line: 2021-10-12T01:12:37.733135Z 14 Query select @@version_comment limit 1
Regex: fmt.Sprintf(
`^(?<time>%s)\s+(?<tid>\d+)\s+(?<command>\w+)(\s+(?<message>[\s|\S]*))?`,
timeRegexMySQLNew,
),
Parser: confgenerator.ParserShared{
TimeKey: "time",
TimeFormat: timeFormatMySQLNew,
Types: map[string]string{
"tid": "integer",
},
},
},
{
// MariaDB uses the same timestamp format here as old versions do for the error log:
// https://mariadb.com/kb/en/error-log/#format
// Sample line: 230707 1:41:38 40 Query select table_catalog, table_schema, table_name from information_schema.tables
// Sample line: 5 Connect root@localhost on using Socket
// When a timestamp is present, it is followed by a single tab character.
// When it is not, it means the timestamp is the same as a previous line, and it is replaced by another tab character.
Regex: fmt.Sprintf(
`^((?<time>%s)|\t)\s+(?<tid>\d+)\s+(?<command>\w+)(\s+(?<message>[\s|\S]*))?`,
timeRegexOld,
),
Parser: confgenerator.ParserShared{
TimeKey: "time",
TimeFormat: timeFormatOld,
Types: map[string]string{
"tid": "integer",
},
},
},
},
},
Rules: []confgenerator.MultilineRule{
{
StateName: "start_state",
NextState: "cont",
Regex: fmt.Sprintf(
`^(%s|%s|\t\t)`,
timeRegexMySQLNew,
timeRegexOld,
),
},
{
StateName: "cont",
NextState: "cont",
Regex: fmt.Sprintf(
`^(?!(%s|%s|\t\t))`,
timeRegexMySQLNew,
timeRegexOld,
),
},
},
}.Components(ctx, tag, uid)
c = append(c,
confgenerator.LoggingProcessorModifyFields{
Fields: map[string]*confgenerator.ModifyField{
InstrumentationSourceLabel: instrumentationSourceValue(p.Type()),
},
}.Components(ctx, tag, uid)...,
)
return c
}
type LoggingProcessorMysqlSlow struct {
confgenerator.ConfigComponent `yaml:",inline"`
}
func (LoggingProcessorMysqlSlow) Type() string {
return "mysql_slow"
}
func (p LoggingProcessorMysqlSlow) Components(ctx context.Context, tag string, uid string) []fluentbit.Component {
modifyFields := map[string]*confgenerator.ModifyField{
InstrumentationSourceLabel: instrumentationSourceValue(p.Type()),
}
// This format is for MySQL 8.0.14+
// Fields are split into this array to improve readability of the regex
mySQLFields := strings.Join([]string{
// Always present slow query log fields
`\s+Query_time:\s+(?<queryTime>[\d\.]+)`,
`\s+Lock_time:\s+(?<lockTime>[\d\.]+)`,
`\s+Rows_sent:\s+(?<rowsSent>\d+)`,
`\s+Rows_examined:\s(?<rowsExamined>\d+)`,
// Extra fields present if log_slow_extra == ON
`(?:\s+Thread_id:\s+\d+)?`, // Field also present in the 2nd line of the multiline log
`(?:\s+Errno:\s(?<errorNumber>\d+))?`,
`(?:\s+Killed:\s(?<killed>\d+))?`,
`(?:\s+Bytes_received:\s(?<bytesReceived>\d+))?`,
`(?:\s+Bytes_sent:\s(?<bytesSent>\d+))?`,
`(?:\s+Read_first:\s(?<readFirst>\d+))?`,
`(?:\s+Read_last:\s(?<readLast>\d+))?`,
`(?:\s+Read_key:\s(?<readKey>\d+))?`,
`(?:\s+Read_next:\s(?<readNext>\d+))?`,
`(?:\s+Read_prev:\s(?<readPrev>\d+))?`,
`(?:\s+Read_rnd:\s(?<readRnd>\d+))?`,
`(?:\s+Read_rnd_next:\s(?<readRndNext>\d+))?`,
`(?:\s+Sort_merge_passes:\s(?<sortMergePasses>\d+))?`,
`(?:\s+Sort_range_count:\s(?<sortRangeCount>\d+))?`,
`(?:\s+Sort_rows:\s(?<sortRows>\d+))?`,
`(?:\s+Sort_scan_count:\s(?<sortScanCount>\d+))?`,
`(?:\s+Created_tmp_disk_tables:\s(?<createdTmpDiskTables>\d+))?`,
`(?:\s+Created_tmp_tables:\s(?<createdTmpTables>\d+))?`,
`(?:\s+Start:\s(?<startTime>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z))?`,
`(?:\s+End:\s(?<endTime>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z))?`,
}, "")
parsers := []confgenerator.RegexParser{{
// Fields documented: https://dev.mysql.com/doc/refman/8.0/en/slow-query-log.html
// Sample line: # Time: 2021-10-12T01:13:38.132884Z
// # User@Host: root[root] @ localhost [] Id: 15
// # Query_time: 0.001855 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0
// SET timestamp=1634001218;
// SET GLOBAL slow_query_log = 1;
// Extra fields w/ low_slow_extra = 'ON'
// Sample line: # Time: 2021-10-12T01:34:15.231930Z
// # User@Host: root[root] @ localhost [] Id: 21
// # Query_time: 0.012740 Lock_time: 0.000810 Rows_sent: 327 Rows_examined: 586 Thread_id: 21 Errno: 0 Killed: 0 Bytes_received: 0 Bytes_sent: 41603 Read_first: 2 Read_last: 0 Read_key: 361 Read_next: 361 Read_prev: 0 Read_rnd: 0 Read_rnd_next: 5 Sort_merge_passes: 0 Sort_range_count: 0 Sort_rows: 0 Sort_scan_count: 0 Created_tmp_disk_tables: 0 Created_tmp_tables: 0 Start: 2021-10-12T01:34:15.219190Z End: 2021-10-12T01:34:15.231930Z
// SET timestamp=1634002455;
// select * from information_schema.tables;
Regex: fmt.Sprintf(
`^(?:# Time: (?<time>%s)\s)?# User@Host:\s+(?<user>[^\[]*)\[(?<database>[^\]]*)\]\s+@\s+((?<host>[^\s]+)\s)?\[(?:(?<ipAddress>[\w\d\.:]+)?)\]\s+Id:\s+(?<tid>\d+)\s+#%s\s+(?<message>[\s\S]+)`,
timeRegexMySQLNew,
mySQLFields,
),
Parser: confgenerator.ParserShared{
TimeKey: "time",
TimeFormat: timeFormatMySQLNew,
Types: map[string]string{
"tid": "integer",
"queryTime": "float",
"lockTime": "float",
"rowsSent": "integer",
"rowsExamined": "integer",
"errorNumber": "integer",
"killed": "integer",
"bytesReceived": "integer",
"bytesSent": "integer",
"readFirst": "integer",
"readLast": "integer",
"readKey": "integer",
"readNext": "integer",
"readPrev": "integer",
"readRnd": "integer",
"readRndNext": "integer",
"sortMergePasses": "integer",
"sortRangeCount": "integer",
"sortRows": "integer",
"sortScanCount": "integer",
"createdTmpDiskTables": "integer",
"createdTmpTables": "integer",
},
},
}}
// This format is for old MySQL and all MariaDB.
// Docs:
// https://mariadb.com/kb/en/slow-query-log-extended-statistics/
// https://mariadb.com/kb/en/explain-in-the-slow-query-log/
// Sample MariaDB line:
// # User@Host: root[root] @ localhost []
// # Thread_id: 32 Schema: dbt3sf1 QC_hit: No
// # Query_time: 0.000130 Lock_time: 0.000068 Rows_sent: 0 Rows_examined: 0
// # Rows_affected: 0 Bytes_sent: 1351
// SET timestamp=1689286831;
// SELECT OBJECT_SCHEMA, OBJECT_NAME, COUNT_DELETE, COUNT_FETCH, COUNT_INSERT, COUNT_UPDATE,SUM_TIMER_DELETE, SUM_TIMER_FETCH, SUM_TIMER_INSERT, SUM_TIMER_UPDATE FROM performance_schema.table_io_waits_summary_by_table WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema');
const (
float = `[\d\.]+`
integer = `\d+`
boolean = `Yes|No`
)
oldFields := [][]struct {
identifier, jsonField, regex string
}{
{
// "# Thread_id: %lu Schema: %s QC_hit: %s\n"
{"Thread_id", "tid", integer},
{"Schema", "database", `\S*`}, // N.B. MariaDB will still show the field with an empty string if the connection doesn't have an active database.
{"QC_hit", "queryCacheHit", boolean},
},
{
// "# Query_time: %s Lock_time: %s Rows_sent: %lu Rows_examined: %lu\n"
{"Query_time", "queryTime", float},
{"Lock_time", "lockTime", float},
{"Rows_sent", "rowsSent", integer},
{"Rows_examined", "rowsExamined", integer},
},
{
// MariaDB 10.3.1+
// "# Rows_affected: %lu Bytes_sent: %lu\n",
{"Rows_affected", "rowsAffected", integer},
{"Bytes_sent", "bytesSent", integer},
},
{
// MariaDB 5.5.37+ if thd->tmp_tables_used with LOG_SLOW_VERBOSITY_QUERY_PLAN
// "# Tmp_tables: %lu Tmp_disk_tables: %lu Tmp_table_sizes: %s\n"
{"Tmp_tables", "createdTmpTables", integer},
{"Tmp_disk_tables", "createdTmpDiskTables", integer},
{"Tmp_table_sizes", "createdTmpTableSizes", integer},
},
{
// MariaDB 10.3.4+ if thd->spcont != NULL
// "# Stored_routine: %s\n"
{"Stored_routine", "storedRoutine", `\S+`},
},
{
// MariaDB 5.5.37+ with LOG_SLOW_VERBOSITY_QUERY_PLAN
// "# Full_scan: %s Full_join: %s Tmp_table: %s Tmp_table_on_disk: %s\n"
{"Full_scan", "fullScan", boolean},
{"Full_join", "fullJoin", boolean},
{"Tmp_table", "", boolean},
{"Tmp_table_on_disk", "", boolean},
},
{
// MariaDB 5.5.37+ with LOG_SLOW_VERBOSITY_QUERY_PLAN
// "# Filesort: %s Filesort_on_disk: %s Merge_passes: %lu Priority_queue: %s\n",
{"Filesort", "filesort", boolean},
{"Filesort_on_disk", "filesortOnDisk", boolean},
{"Merge_passes", "sortMergePasses", integer},
{"Priority_queue", "priorityQueue", boolean},
},
}
// LOG_SLOW_VERBOSITY_EXPLAIN causes additional comment lines
// to be added containing the output of EXPLAIN; it's probably
// not worth parsing them since they're somewhat freeform.
oldLines := []string{
fmt.Sprintf(`^(?:# Time: (?<time>%s)\s)?`, timeRegexOld),
// N.B. MySQL logs two usernames (i.e. "root[root]"). The first username is the "priv_user", i.e. the username used for privilege checking.
// The second username is the "user", which is the string the user provided when connecting.
// We only report the priv_user here.
// See https://dev.mysql.com/doc/refman/8.0/en/audit-log-file-formats.html
`# User@Host:\s+(?<user>[^\[]*)\[[^\]]*\]\s+@\s+((?<host>[^\s]+)\s)?\[(?:(?<ipAddress>[\w\d\.:]+)?)\]`,
}
oldTypes := make(map[string]string)
for _, lineFields := range oldFields {
var out []string
for _, field := range lineFields {
valueRegex := fmt.Sprintf(`(?:%s)`, field.regex)
if field.jsonField != "" {
valueRegex = fmt.Sprintf(`(?<%s>%s)`, field.jsonField, field.regex)
switch field.regex {
case float:
oldTypes[field.jsonField] = "float"
case integer:
oldTypes[field.jsonField] = "integer"
case boolean:
modifyFields[fmt.Sprintf(`jsonPayload.%s`, field.jsonField)] = &confgenerator.ModifyField{
Type: "YesNoBoolean",
}
}
}
optional := "?"
if len(out) == 0 {
// First field on each line is not optional.
// Otherwise we'll consume the "# " of the following line and prevent it from matching the next line's regex.
optional = ""
}
out = append(out, fmt.Sprintf(
`(?:\s+%s:\s%s)%s`,
field.identifier,
valueRegex,
optional,
))
}
oldLines = append(oldLines, fmt.Sprintf(
`(?:\s+#%s)?`,
strings.Join(out, ""),
))
}
oldLines = append(oldLines, `\s+(?<message>[\s\S]+)`)
parsers = append(parsers, confgenerator.RegexParser{
Regex: strings.Join(oldLines, ""),
Parser: confgenerator.ParserShared{
TimeKey: "time",
TimeFormat: timeFormatOld,
Types: oldTypes,
},
})
c := confgenerator.LoggingProcessorParseMultilineRegex{
LoggingProcessorParseRegexComplex: confgenerator.LoggingProcessorParseRegexComplex{
Parsers: parsers,
},
Rules: []confgenerator.MultilineRule{
// Logs start with Time: or User@Host: (omitting time if it's the same as the previous entry).
{
StateName: "start_state",
NextState: "comment",
Regex: fmt.Sprintf(
`^# (User@Host: |Time: (%s|%s))`,
timeRegexMySQLNew,
timeRegexOld,
),
},
// Explicitly consume the next line, which might be User@Host.
{
StateName: "comment",
NextState: "cont",
Regex: `^# `,
},
// Then consume everything until the next Time or User@Host.
{
StateName: "cont",
NextState: "cont",
Regex: fmt.Sprintf(
`^(?!# (User@Host: |Time: (%s|%s)))`,
timeRegexMySQLNew,
timeRegexOld,
),
},
},
}.Components(ctx, tag, uid)
c = append(c,
confgenerator.LoggingProcessorModifyFields{
Fields: modifyFields,
}.Components(ctx, tag, uid)...,
)
return c
}
type LoggingReceiverMysqlGeneral struct {
LoggingProcessorMysqlGeneral `yaml:",inline"`
ReceiverMixin confgenerator.LoggingReceiverFilesMixin `yaml:",inline" validate:"structonly"`
}
func (r LoggingReceiverMysqlGeneral) Components(ctx context.Context, tag string) []fluentbit.Component {
if len(r.ReceiverMixin.IncludePaths) == 0 {
r.ReceiverMixin.IncludePaths = []string{
// Default log path for CentOS / RHEL / SLES / Debain / Ubuntu
"/var/lib/mysql/${HOSTNAME}.log",
}
}
c := r.ReceiverMixin.Components(ctx, tag)
c = append(c, r.LoggingProcessorMysqlGeneral.Components(ctx, tag, "mysql_general")...)
return c
}
type LoggingReceiverMysqlSlow struct {
LoggingProcessorMysqlSlow `yaml:",inline"`
ReceiverMixin confgenerator.LoggingReceiverFilesMixin `yaml:",inline" validate:"structonly"`
}
func (r LoggingReceiverMysqlSlow) Components(ctx context.Context, tag string) []fluentbit.Component {
if len(r.ReceiverMixin.IncludePaths) == 0 {
r.ReceiverMixin.IncludePaths = []string{
// Default log path for CentOS / RHEL / SLES / Debain / Ubuntu
"/var/lib/mysql/${HOSTNAME}-slow.log",
}
}
c := r.ReceiverMixin.Components(ctx, tag)
c = append(c, r.LoggingProcessorMysqlSlow.Components(ctx, tag, "mysql_slow")...)
return c
}
type LoggingReceiverMysqlError struct {
LoggingProcessorMysqlError `yaml:",inline"`
ReceiverMixin confgenerator.LoggingReceiverFilesMixin `yaml:",inline" validate:"structonly"`
}
func (r LoggingReceiverMysqlError) Components(ctx context.Context, tag string) []fluentbit.Component {
if len(r.ReceiverMixin.IncludePaths) == 0 {
r.ReceiverMixin.IncludePaths = []string{
// Default log path for CentOS / RHEL
"/var/log/mysqld.log",
// Default log path for SLES
"/var/log/mysql/mysqld.log",
// Default log path for Oracle MySQL on Debian / Ubuntu
"/var/log/mysql/error.log",
// Default log path for MariaDB on Debian
"/run/mysqld/mysqld.err",
// Default log path for MariaDB upstream
// https://mariadb.com/kb/en/error-log/#writing-the-error-log-to-a-file
"/var/lib/mysql/${HOSTNAME}.err",
}
}
c := r.ReceiverMixin.Components(ctx, tag)
c = append(c, r.LoggingProcessorMysqlError.Components(ctx, tag, "mysql_error")...)
return c
}
func init() {
confgenerator.LoggingProcessorTypes.RegisterType(func() confgenerator.LoggingProcessor { return &LoggingProcessorMysqlError{} })
confgenerator.LoggingProcessorTypes.RegisterType(func() confgenerator.LoggingProcessor { return &LoggingProcessorMysqlGeneral{} })
confgenerator.LoggingProcessorTypes.RegisterType(func() confgenerator.LoggingProcessor { return &LoggingProcessorMysqlSlow{} })
confgenerator.LoggingReceiverTypes.RegisterType(func() confgenerator.LoggingReceiver { return &LoggingReceiverMysqlError{} })
confgenerator.LoggingReceiverTypes.RegisterType(func() confgenerator.LoggingReceiver { return &LoggingReceiverMysqlGeneral{} })
confgenerator.LoggingReceiverTypes.RegisterType(func() confgenerator.LoggingReceiver { return &LoggingReceiverMysqlSlow{} })
}