Skip to content
Draft
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
119 changes: 119 additions & 0 deletions admin/commands/inventory/add_agent_otel_collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright (C) 2023 Percona 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 inventory

import (
"strings"

"github.com/percona/pmm/admin/commands"
"github.com/percona/pmm/api/inventory/v1/json/client"
agents "github.com/percona/pmm/api/inventory/v1/json/client/agents_service"
)

var addAgentOTELCollectorResultT = commands.ParseTemplate(`
OTEL Collector added.
Agent ID : {{ .Agent.AgentID }}
PMM-Agent ID : {{ .Agent.PMMAgentID }}
Listen port : {{ .Agent.ListenPort }}

Status : {{ .Agent.Status }}
Disabled : {{ .Agent.Disabled }}
Custom labels : {{ .Agent.CustomLabels }}

Logs Collection : {{ if .Agent.LogsConfig }}{{ if .Agent.LogsConfig.Enabled }}Enabled{{ else }}Disabled{{ end }}{{ else }}Disabled{{ end }}
{{ if .Agent.LogsConfig }}{{ if .Agent.LogsConfig.LogSources }}Log Sources:
{{ range .Agent.LogsConfig.LogSources }} - {{ .ServiceName }}: {{ .Path }}
{{ end }}{{ end }}{{ end }}
`)

type addAgentOTELCollectorResult struct {
Agent *agents.AddAgentOKBodyOtelCollector `json:"otel_collector"`
}

func (res *addAgentOTELCollectorResult) Result() {}

func (res *addAgentOTELCollectorResult) String() string {
return commands.RenderTemplate(addAgentOTELCollectorResultT, res)
}

// AddAgentOTELCollectorCommand is used by Kong for CLI flags and commands.
// OTEL Collector collects logs (and future: traces, profiles, eBPF) from database nodes.
type AddAgentOTELCollectorCommand struct {
PMMAgentID string `arg:"" help:"The pmm-agent identifier which runs this instance"`
CustomLabels map[string]string `mapsep:"," help:"Custom user-assigned labels"`

// Logs collection settings
EnableLogs bool `help:"Enable logs collection"`
LogSources []string `help:"Log sources in format 'service_name:path:parser_type' (e.g., 'mysql:/var/log/mysql/error.log:regex')"`
EnableSyslog bool `help:"Enable syslog receiver"`
SyslogPort uint32 `default:"514" help:"Port for syslog receiver"`
EnableJournald bool `help:"Enable journald receiver for systemd logs"`
JournaldUnits []string `help:"Systemd units to collect logs from (e.g., 'mysqld', 'postgresql')"`
}

// RunCmd executes the AddAgentOTELCollectorCommand and returns the result.
func (cmd *AddAgentOTELCollectorCommand) RunCmd() (commands.Result, error) {
customLabels := commands.ParseKeyValuePair(cmd.CustomLabels)

// Parse log sources from CLI format
var logSources []*agents.AddAgentParamsBodyOtelCollectorLogsConfigLogSourcesItems0
for _, src := range cmd.LogSources {
parts := strings.SplitN(src, ":", 3)
if len(parts) >= 2 {
logSource := &agents.AddAgentParamsBodyOtelCollectorLogsConfigLogSourcesItems0{
ServiceName: parts[0],
Path: parts[1],
}
if len(parts) >= 3 {
logSource.ParserType = parts[2]
} else {
logSource.ParserType = "raw" // default parser
}
logSources = append(logSources, logSource)
}
}

// Build logs config
var logsConfig *agents.AddAgentParamsBodyOtelCollectorLogsConfig
if cmd.EnableLogs || len(logSources) > 0 || cmd.EnableSyslog || cmd.EnableJournald {
logsConfig = &agents.AddAgentParamsBodyOtelCollectorLogsConfig{
Enabled: cmd.EnableLogs || len(logSources) > 0,
LogSources: logSources,
EnableSyslog: cmd.EnableSyslog,
SyslogPort: int64(cmd.SyslogPort),
EnableJournald: cmd.EnableJournald,
JournaldUnits: cmd.JournaldUnits,
}
}

params := &agents.AddAgentParams{
Body: agents.AddAgentBody{
OtelCollector: &agents.AddAgentParamsBodyOtelCollector{
PMMAgentID: cmd.PMMAgentID,
CustomLabels: customLabels,
LogsConfig: logsConfig,
},
},
Context: commands.Ctx,
}

resp, err := client.Default.AgentsService.AddAgent(params)
if err != nil {
return nil, err
}
return &addAgentOTELCollectorResult{
Agent: resp.Payload.OtelCollector,
}, nil
}
1 change: 1 addition & 0 deletions admin/commands/inventory/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type AddAgentCommand struct {
PostgresExporter AddAgentPostgresExporterCommand `cmd:"" help:"Add postgres_exporter to inventory"`
ProxysqlExporter AddAgentProxysqlExporterCommand `cmd:"" help:"Add proxysql_exporter to inventory"`
ValkeyExporter AddAgentValkeyExporterCommand `cmd:"" help:"Add valkey_exporter to inventory"`
OTELCollector AddAgentOTELCollectorCommand `cmd:"" name:"otel-collector" help:"Add OTEL Collector for logs collection to inventory"`

QANMongoDBProfilerAgent AddAgentQANMongoDBProfilerAgentCommand `cmd:"" name:"qan-mongodb-profiler-agent" help:"Add QAN MongoDB profiler agent to inventory"`
QANMongoDBMongologAgent AddAgentQANMongoDBMongologAgentCommand `cmd:"" name:"qan-mongodb-mongolog-agent" help:"Add QAN MongoDB mongolog agent to inventory"`
Expand Down
14 changes: 14 additions & 0 deletions agent/agents/supervisor/supervisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,20 @@ func (s *Supervisor) processParams(agentID string, agentProcess *agentv1.SetStat
templateParams["nomad_data_dir"] = cfg.Paths.NomadDataDir
processParams.Path = cfg.Paths.Nomad
processParams.Env = append(processParams.Env, os.Environ()...)

// ==========================================================================
// OTEL Collector - OpenTelemetry-based observability agent
// ==========================================================================
// Phase 1: Log collection from database and system log files.
// The OTEL Collector (otelcol-contrib) is spawned as a subprocess.
// Configuration is passed via a config file generated by PMM Server.
// Future phases will add: traces, eBPF instrumentation, profiles.
case inventoryv1.AgentType_AGENT_TYPE_OTEL_COLLECTOR:
processParams.Path = cfg.Paths.OTELCollector
// OTEL collector doesn't need special template params for now.
// The config file is passed via TextFiles and --config argument.
// Future: Add template params for dynamic endpoint configuration.

default:
return nil, errors.Errorf("unhandled agent type %[1]s (%[1]d).", agentProcess.Type) //nolint:revive
}
Expand Down
9 changes: 9 additions & 0 deletions agent/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ type Paths struct {
VMAgent string `yaml:"vmagent"`
Nomad string `yaml:"nomad"`

// OTELCollector is the path to the OpenTelemetry Collector binary (otelcol-contrib).
// Phase 1: Used for log collection from database and system files.
// Future phases: Will also handle traces, eBPF instrumentation, profiles.
OTELCollector string `yaml:"otel_collector"`

TempDir string `yaml:"tempdir"`
NomadDataDir string `yaml:"nomad_data_dir"`

Expand Down Expand Up @@ -235,6 +240,9 @@ func get(args []string, cfg *Config, l *logrus.Entry) (string, error) { //nolint
&cfg.Paths.RDSExporter: "rds_exporter",
&cfg.Paths.AzureExporter: "azure_exporter",
&cfg.Paths.VMAgent: "vmagent",
// OpenTelemetry Collector binary (otelcol-contrib).
// Used for log collection (Phase 1), future: traces, eBPF, profiles.
&cfg.Paths.OTELCollector: "otelcol",
&cfg.Paths.PTSummary: "tools/pt-summary",
&cfg.Paths.PTPGSummary: "tools/pt-pg-summary",
&cfg.Paths.PTMongoDBSummary: "tools/pt-mongodb-summary",
Expand Down Expand Up @@ -298,6 +306,7 @@ func get(args []string, cfg *Config, l *logrus.Entry) (string, error) { //nolint
"rds_exporter": &cfg.Paths.RDSExporter,
"azure_exporter": &cfg.Paths.AzureExporter,
"vmagent": &cfg.Paths.VMAgent,
"otelcol": &cfg.Paths.OTELCollector, // OpenTelemetry Collector
} {
if cfg.Paths.ExportersBase != "" && !filepath.IsAbs(*sp) {
*sp = filepath.Join(cfg.Paths.ExportersBase, *sp)
Expand Down

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

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

10 changes: 6 additions & 4 deletions api/agentlocal/v1/json/v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
"x-order": 0
},
"agent_type": {
"description": "AgentType describes supported Agent types.",
"description": "AgentType describes supported Agent types.\n\n - AGENT_TYPE_OTEL_COLLECTOR: OTEL Collector for logs, traces, profiles, and eBPF collection",
"type": "string",
"default": "AGENT_TYPE_UNSPECIFIED",
"enum": [
Expand All @@ -178,7 +178,8 @@
"AGENT_TYPE_EXTERNAL_EXPORTER",
"AGENT_TYPE_RDS_EXPORTER",
"AGENT_TYPE_AZURE_DATABASE_EXPORTER",
"AGENT_TYPE_NOMAD_AGENT"
"AGENT_TYPE_NOMAD_AGENT",
"AGENT_TYPE_OTEL_COLLECTOR"
],
"x-order": 1
},
Expand Down Expand Up @@ -354,7 +355,7 @@
"x-order": 0
},
"agent_type": {
"description": "AgentType describes supported Agent types.",
"description": "AgentType describes supported Agent types.\n\n - AGENT_TYPE_OTEL_COLLECTOR: OTEL Collector for logs, traces, profiles, and eBPF collection",
"type": "string",
"default": "AGENT_TYPE_UNSPECIFIED",
"enum": [
Expand All @@ -376,7 +377,8 @@
"AGENT_TYPE_EXTERNAL_EXPORTER",
"AGENT_TYPE_RDS_EXPORTER",
"AGENT_TYPE_AZURE_DATABASE_EXPORTER",
"AGENT_TYPE_NOMAD_AGENT"
"AGENT_TYPE_NOMAD_AGENT",
"AGENT_TYPE_OTEL_COLLECTOR"
],
"x-order": 1
},
Expand Down
1 change: 1 addition & 0 deletions api/inventory/v1/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ func (*RDSExporter) sealedAgent() {}
func (*ExternalExporter) sealedAgent() {}
func (*AzureDatabaseExporter) sealedAgent() {}
func (*ValkeyExporter) sealedAgent() {}
func (*OTELCollector) sealedAgent() {}
Loading