Skip to content

PMM-12548 Slowlog as new MongoDB query source. #3776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 35 commits into
base: v3
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3f3c960
PMM-12548 Admin part.
JiriCtvrtka Mar 5, 2025
eda4fec
PMM-12548 New mongo agents structure.
JiriCtvrtka Mar 5, 2025
2d29da3
PMM-12548 Changes for agent.
JiriCtvrtka Mar 13, 2025
1aba2ea
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Mar 13, 2025
1abd56f
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Mar 24, 2025
8bc1ca3
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Mar 24, 2025
755b360
PMM-12548 Compose.
JiriCtvrtka Mar 25, 2025
3513beb
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Mar 25, 2025
03308e5
PMM-12548 Change SSL to TLS since its deprecated.
JiriCtvrtka Mar 27, 2025
6e707e9
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Mar 27, 2025
6bceb49
PMM-12548 Slowlog prefixes for testing, tls instead ssl.
JiriCtvrtka Mar 28, 2025
7f765ec
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Mar 28, 2025
47f721b
PMM-12548 Reader test.
JiriCtvrtka Mar 28, 2025
1a0f27d
PMM-12548 Percona Mongo, formatting.
JiriCtvrtka Mar 28, 2025
298fdbb
PMM-12548 Add aggregator.
JiriCtvrtka Apr 4, 2025
2d87252
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Apr 4, 2025
170b9e2
PMM-12548 Renaming to mongolog.
JiriCtvrtka Apr 7, 2025
898abbd
PMM-12548 Managed part, changes in agent.
JiriCtvrtka Apr 7, 2025
d6be814
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Apr 7, 2025
7d2d105
PMM-12548 Small refactor.
JiriCtvrtka Apr 7, 2025
b804de8
PMM-12548 Move file reader.
JiriCtvrtka Apr 11, 2025
4b40566
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Apr 11, 2025
0c17f11
PMM-12548 Fix.
JiriCtvrtka Apr 11, 2025
cf7cb81
PMM-12548 Empty line.
JiriCtvrtka Apr 11, 2025
be53fbf
PMM-12548 Percona Toolkit with json tags.
JiriCtvrtka Apr 15, 2025
49a18ae
PMM-12548 Sum.
JiriCtvrtka Apr 15, 2025
7427063
PMM-12548 Refactor, logic changes.
JiriCtvrtka Apr 24, 2025
e572dbf
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Apr 24, 2025
eca8c3c
PMM-12548 Missed add/change agents in API.
JiriCtvrtka Apr 24, 2025
6d6469e
PMM-12548 Lint.
JiriCtvrtka Apr 25, 2025
ff27a35
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Apr 25, 2025
83f14f0
PMM-12548 Fix Mongolog admin response.
JiriCtvrtka Apr 28, 2025
17890a0
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Apr 28, 2025
ba33053
PMM-12548 Lint.
JiriCtvrtka Apr 28, 2025
5e80b7a
Merge branch 'mainv3' into PMM-12548-new-mongo-query-source
JiriCtvrtka Apr 28, 2025
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
9 changes: 5 additions & 4 deletions admin/cmd/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func getDefaultKongOptions(appName string) []kong.Option {

mongoDBQuerySources := []string{
management.MongodbQuerySourceProfiler,
management.MongodbQuerySourceMongolog,
management.MongodbQuerySourceNone,
}

Expand All @@ -158,13 +159,13 @@ func getDefaultKongOptions(appName string) []kong.Option {
"nodeIp": nodeinfo.PublicAddress,
"nodeTypeDefault": nodeTypeDefault,
"hostname": hostname,
"serviceTypesEnum": strings.Join(management.AllServiceTypesKeys, ","),
"serviceTypesEnum": strings.Join(management.AllServiceTypesKeys, ", "),
"defaultMachineID": defaultMachineID,
"distro": nodeinfo.Distro,
"metricsModesEnum": strings.Join(management.MetricsModes, ","),
"mysqlQuerySourcesEnum": strings.Join(mysqlQuerySources, ","),
"metricsModesEnum": strings.Join(management.MetricsModes, ", "),
"mysqlQuerySourcesEnum": strings.Join(mysqlQuerySources, ", "),
"mysqlQuerySourceDefault": mysqlQuerySources[0],
"mongoDbQuerySourcesEnum": strings.Join(mongoDBQuerySources, ","),
"mongoDbQuerySourcesEnum": strings.Join(mongoDBQuerySources, ", "),
"mongoDbQuerySourceDefault": mongoDBQuerySources[0],
"externalDefaultServiceName": management.DefaultServiceNameSuffix,
"externalDefaultGroupExporter": management.DefaultGroupExternalExporter,
Expand Down
112 changes: 112 additions & 0 deletions admin/commands/inventory/add_agent_qan_mongodb_mongolog_agent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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 (
"github.com/percona/pmm/admin/commands"
"github.com/percona/pmm/admin/pkg/flags"
"github.com/percona/pmm/api/inventory/v1/json/client"
agents "github.com/percona/pmm/api/inventory/v1/json/client/agents_service"
)

var addAgentQANMongoDBMongologAgentResultT = commands.ParseTemplate(`
QAN MongoDB Mongolog agent added.
Agent ID : {{ .Agent.AgentID }}
PMM-Agent ID : {{ .Agent.PMMAgentID }}
Service ID : {{ .Agent.ServiceID }}
Username : {{ .Agent.Username }}
TLS enabled : {{ .Agent.TLS }}
Skip TLS verification : {{ .Agent.TLSSkipVerify }}

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

type addAgentQANMongoDBMongologAgentResult struct {
Agent *agents.AddAgentOKBodyQANMongodbMongologAgent `json:"qan_mongodb_mongolog_agent"`
}

func (res *addAgentQANMongoDBMongologAgentResult) Result() {}

func (res *addAgentQANMongoDBMongologAgentResult) String() string {
return commands.RenderTemplate(addAgentQANMongoDBMongologAgentResultT, res)
}

// AddAgentQANMongoDBMongologAgentCommand is used by Kong for CLI flags and commands.
//
//nolint:lll

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [golangci] reported by reviewdog 🐶
directive //nolint:lll is unused for linter "lll" (nolintlint)

type AddAgentQANMongoDBMongologAgentCommand struct {
PMMAgentID string `arg:"" help:"The pmm-agent identifier which runs this instance"`
ServiceID string `arg:"" help:"Service identifier"`
Username string `arg:"" optional:"" help:"MongoDB username for scraping metrics"`
Password string `help:"MongoDB password for scraping metrics"`
CustomLabels map[string]string `mapsep:"," help:"Custom user-assigned labels"`
SkipConnectionCheck bool `help:"Skip connection check"`
MaxQueryLength int32 `placeholder:"NUMBER" help:"Limit query length in QAN (default: server-defined; -1: no limit)"`
DisableQueryExamples bool `name:"disable-queryexamples" help:"Disable collection of query examples"`
TLS bool `help:"Use TLS to connect to the database"`
TLSSkipVerify bool `help:"Skip TLS certificates validation"`
TLSCertificateKeyFile string `help:"Path to TLS certificate PEM file"`
TLSCertificateKeyFilePassword string `help:"Password for certificate"`
TLSCaFile string `help:"Path to certificate authority file"`
AuthenticationMechanism string `help:"Authentication mechanism. Default is empty. Use MONGODB-X509 for ssl certificates"`

flags.LogLevelFatalFlags
}

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

tlsCertificateKey, err := commands.ReadFile(cmd.TLSCertificateKeyFile)
if err != nil {
return nil, err
}
tlsCa, err := commands.ReadFile(cmd.TLSCaFile)
if err != nil {
return nil, err
}

params := &agents.AddAgentParams{
Body: agents.AddAgentBody{
QANMongodbMongologAgent: &agents.AddAgentParamsBodyQANMongodbMongologAgent{
PMMAgentID: cmd.PMMAgentID,
ServiceID: cmd.ServiceID,
Username: cmd.Username,
Password: cmd.Password,
CustomLabels: customLabels,
SkipConnectionCheck: cmd.SkipConnectionCheck,
MaxQueryLength: cmd.MaxQueryLength,
TLS: cmd.TLS,
TLSSkipVerify: cmd.TLSSkipVerify,
TLSCertificateKey: tlsCertificateKey,
TLSCertificateKeyFilePassword: cmd.TLSCertificateKeyFilePassword,
TLSCa: tlsCa,
AuthenticationMechanism: cmd.AuthenticationMechanism,
LogLevel: cmd.LogLevelFatalFlags.LogLevel.EnumValue(),
},
},
Context: commands.Ctx,
}

resp, err := client.Default.AgentsService.AddAgent(params)
if err != nil {
return nil, err
}
return &addAgentQANMongoDBMongologAgentResult{
Agent: resp.Payload.QANMongodbMongologAgent,
}, nil
}
1 change: 1 addition & 0 deletions admin/commands/inventory/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type AddAgentCommand struct {
ProxysqlExporter AddAgentProxysqlExporterCommand `cmd:"" help:"Add proxysql_exporter 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"`
QANMySQLPerfSchemaAgent AddAgentQANMySQLPerfSchemaAgentCommand `cmd:"" name:"qan-mysql-perfschema-agent" help:"Add QAN MySQL perf schema agent to inventory"`
QANMySQLSlowlogAgent AddAgentQANMySQLSlowlogAgentCommand `cmd:"" name:"qan-mysql-slowlog-agent" help:"Add QAN MySQL slowlog agent to inventory"`
QANPostgreSQLPgStatementsAgent AddAgentQANPostgreSQLPgStatementsAgentCommand `cmd:"" name:"qan-postgresql-pgstatements-agent" help:"Add QAN PostgreSQL Stat Statements Agent to inventory"`
Expand Down
1 change: 1 addition & 0 deletions admin/commands/inventory/list_agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var acceptableAgentTypes = map[string][]string{
types.AgentTypeQANMySQLPerfSchemaAgent: {types.AgentTypeName(types.AgentTypeQANMySQLPerfSchemaAgent), "qan-mysql-perfschema-agent"},
types.AgentTypeQANMySQLSlowlogAgent: {types.AgentTypeName(types.AgentTypeQANMySQLSlowlogAgent), "qan-mysql-slowlog-agent"},
types.AgentTypeQANMongoDBProfilerAgent: {types.AgentTypeName(types.AgentTypeQANMongoDBProfilerAgent), "qan-mongodb-profiler-agent"},
types.AgentTypeQANMongoDBMongologAgent: {types.AgentTypeName(types.AgentTypeQANMongoDBMongologAgent), "qan-mongodb-mongolog-agent"},
types.AgentTypeQANPostgreSQLPgStatementsAgent: {types.AgentTypeName(types.AgentTypeQANPostgreSQLPgStatementsAgent), "qan-postgresql-pgstatements-agent"},
types.AgentTypeQANPostgreSQLPgStatMonitorAgent: {types.AgentTypeName(types.AgentTypeQANPostgreSQLPgStatMonitorAgent), "qan-postgresql-pgstatmonitor-agent"},
types.AgentTypeRDSExporter: {types.AgentTypeName(types.AgentTypeRDSExporter), "rds-exporter"},
Expand Down
11 changes: 11 additions & 0 deletions admin/commands/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,17 @@ func agentsList(agentsRes *agents.ListAgentsOK, nodeID string) []listResultAgent
})
}
}
for _, a := range agentsRes.Payload.QANMongodbMongologAgent {
if _, ok := pmmAgentIDs[a.PMMAgentID]; ok {
agentsList = append(agentsList, listResultAgent{
AgentType: types.AgentTypeQANMongoDBMongologAgent,
AgentID: a.AgentID,
ServiceID: a.ServiceID,
Status: getStatus(a.Status),
Disabled: a.Disabled,
})
}
}
for _, a := range agentsRes.Payload.QANPostgresqlPgstatementsAgent {
if _, ok := pmmAgentIDs[a.PMMAgentID]; ok {
agentsList = append(agentsList, listResultAgent{
Expand Down
3 changes: 3 additions & 0 deletions admin/commands/management/add_mongodb.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
const (
// MongodbQuerySourceProfiler defines available source name for profiler.
MongodbQuerySourceProfiler = "profiler"
// MongodbQuerySourceMongolog defines available source name for profiler.
MongodbQuerySourceMongolog = "mongolog"
// MongodbQuerySourceNone defines available source name for profiler.
MongodbQuerySourceNone = "none"
)
Expand Down Expand Up @@ -175,6 +177,7 @@ func (cmd *AddMongoDBCommand) RunCmd() (commands.Result, error) {
AgentPassword: cmd.AgentPassword,

QANMongodbProfiler: cmd.QuerySource == MongodbQuerySourceProfiler,
QANMongodbMongolog: cmd.QuerySource == MongodbQuerySourceMongolog,

CustomLabels: customLabels,
SkipConnectionCheck: cmd.SkipConnectionCheck,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import (
mongostats "github.com/percona/percona-toolkit/src/go/mongolib/stats"
"github.com/sirupsen/logrus"

"github.com/percona/pmm/agent/agents/mongodb/internal/profiler/fingerprinter"
"github.com/percona/pmm/agent/agents/mongodb/internal/report"
"github.com/percona/pmm/agent/agents/mongodb/mongolog/internal/fingerprinter"
"github.com/percona/pmm/agent/agents/mongodb/mongolog/internal/report"
"github.com/percona/pmm/agent/utils/truncate"
agentv1 "github.com/percona/pmm/api/agent/v1"
inventoryv1 "github.com/percona/pmm/api/inventory/v1"
Expand Down Expand Up @@ -244,11 +244,11 @@ func (a *Aggregator) newInterval(ts time.Time) {
func (a *Aggregator) createResult(_ context.Context) *report.Result {
queries := a.mongostats.Queries()
queryStats := queries.CalcQueriesStats(int64(DefaultInterval))
var buckets []*agentv1.MetricsBucket

a.logger.Tracef("Queries: %#v", queries)
a.logger.Tracef("Query Stats: %#v", queryStats)

var buckets []*agentv1.MetricsBucket //nolint:prealloc
for _, v := range queryStats {
db := ""
collection := ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"

"github.com/percona/pmm/agent/agents/mongodb/internal/report"
"github.com/percona/pmm/agent/agents/mongodb/mongolog/internal/report"
"github.com/percona/pmm/agent/utils/truncate"
agentv1 "github.com/percona/pmm/api/agent/v1"
inventoryv1 "github.com/percona/pmm/api/inventory/v1"
Expand Down
153 changes: 153 additions & 0 deletions agent/agents/mongodb/mongolog/internal/collector/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// 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 collector implements collecting mongo logs from file.
package collector

import (
"context"
"runtime/pprof"
"sync"
"time"

"github.com/percona/percona-toolkit/src/go/mongolib/proto"
"github.com/sirupsen/logrus"
)

// New creates new Collector.
func New(logsPath string, logger *logrus.Entry) *Collector {
return &Collector{
logsPath: logsPath,
logger: logger.WithField("log", logsPath),
}
}

// Collector is used by Mongolog agent.
type Collector struct {
// dependencies
logsPath string
logger *logrus.Entry

// provides
docsChan chan proto.SystemProfile

// state
m sync.Mutex // Lock() to protect internal consistency of the service
running bool // Is this service running?
doneChan chan struct{} // close(doneChan) to notify goroutines that they should shutdown
wg *sync.WaitGroup // Wait() for goroutines to stop after being notified they should shutdown
}

// Start starts but doesn't wait until it exits
func (c *Collector) Start(ctx context.Context) (<-chan proto.SystemProfile, error) {
c.m.Lock()
defer c.m.Unlock()
if c.running {
return nil, nil
}

// create new channels over which we will communicate to...
// ... outside world by sending collected docs
c.docsChan = make(chan proto.SystemProfile, 100)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [golangci] reported by reviewdog 🐶
Magic number: 100, in detected (mnd)

// ... inside goroutine to close it
c.doneChan = make(chan struct{})

// start a goroutine and Add() it to WaitGroup
// so we could later Wait() for it to finish
c.wg = &sync.WaitGroup{}
c.wg.Add(1)

// create ready sync.Cond so we could know when goroutine actually started getting data from db
ready := sync.NewCond(&sync.Mutex{})
ready.L.Lock()
defer ready.L.Unlock()

labels := pprof.Labels("component", "mongodb.aggregator")
go pprof.Do(ctx, labels, func(ctx context.Context) {
start(
ctx,
c.wg,
c.logsPath,
c.docsChan,
c.doneChan,
ready,
c.logger)
})

// wait until we actually fetch data from db
ready.Wait()

c.running = true

return c.docsChan, nil
}

// Stop stops running
func (c *Collector) Stop() {
c.m.Lock()
defer c.m.Unlock()

if !c.running {
return
}

c.running = false
close(c.doneChan) // notify goroutine to close
c.wg.Wait() // wait for goroutines to exit
close(c.docsChan) // we can now safely close channels goroutines write to as goroutine is stopped
}

func start(ctx context.Context, wg *sync.WaitGroup, logsPath string,
docsChan chan<- proto.SystemProfile, doneChan <-chan struct{}, ready *sync.Cond, logger *logrus.Entry,
) {
// signal WaitGroup when goroutine finished
defer wg.Done()

fr, err := NewReader(ctx, docsChan, doneChan, logsPath, logger)
if err != nil {
logger.Error(err)
return
}
go func() {
fr.ReadFile()
logger.Debugln("reading routine quit")
}()

firstTry := true

for {
select {
// check if we should shutdown
case <-ctx.Done():
return
case <-doneChan:
return
// wait some time before reconnecting
case <-time.After(1 * time.Second):
}

// After first failure in connection we signal that we are ready anyway
// this way service starts, and will automatically connect when db is available.
if firstTry {
signalReady(ready)
firstTry = false
}
}
}

func signalReady(ready *sync.Cond) {
ready.L.Lock()
defer ready.L.Unlock()
ready.Broadcast()
}
Loading
Loading