Skip to content

Commit

Permalink
Support getLog diagnostic command (#711)
Browse files Browse the repository at this point in the history
Closes #420.
  • Loading branch information
fenogentov authored Jul 25, 2022
1 parent fafa9aa commit 3d53f60
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 78 deletions.
96 changes: 84 additions & 12 deletions integration/commands_diagnostic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,105 @@
package integration

import (
"math"
"runtime"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"

"github.com/FerretDB/FerretDB/integration/setup"
)

func TestCommandsDiagnosticGetLog(t *testing.T) {
t.Parallel()
s := setup.SetupWithOpts(t, &setup.SetupOpts{
res := setup.SetupWithOpts(t, &setup.SetupOpts{
DatabaseName: "admin",
})

var actual bson.D
err := s.TargetCollection.Database().RunCommand(s.Ctx, bson.D{{"getLog", "startupWarnings"}}).Decode(&actual)
require.NoError(t, err)

m := actual.Map()
t.Log(m)

assert.Equal(t, float64(1), m["ok"])
assert.Equal(t, []string{"totalLinesWritten", "log", "ok"}, CollectKeys(t, actual))

assert.IsType(t, int32(0), m["totalLinesWritten"])
ctx, collection := res.Ctx, res.TargetCollection

for name, tc := range map[string]struct {
command bson.D
expected map[string]any
err *mongo.CommandError
alt string
}{
"Asterisk": {
command: bson.D{{"getLog", "*"}},
expected: map[string]any{
"names": bson.A(bson.A{"global", "startupWarnings"}),
"ok": float64(1),
},
},
"Global": {
command: bson.D{{"getLog", "global"}},
expected: map[string]any{
"totalLinesWritten": int64(1024),
"log": bson.A{},
"ok": float64(1),
},
},
"StartupWarnings": {
command: bson.D{{"getLog", "startupWarnings"}},
expected: map[string]any{
"totalLinesWritten": int64(1024),
"log": bson.A{},
"ok": float64(1),
},
},
"NonExistentName": {
command: bson.D{{"getLog", "nonExistentName"}},
err: &mongo.CommandError{
Code: 0,
Message: `no RamLog named: nonExistentName`,
},
alt: `no RecentEntries named: nonExistentName`,
},
"Nil": {
command: bson.D{{"getLog", nil}},
err: &mongo.CommandError{
Code: 14,
Name: "TypeMismatch",
Message: `Argument to getLog must be of type String; found null of type null`,
},
alt: "Argument to getLog must be of type String",
},
"NaN": {
command: bson.D{{"getLog", math.NaN()}},
err: &mongo.CommandError{
Code: 14,
Name: "TypeMismatch",
Message: `Argument to getLog must be of type String; found nan.0 of type double`,
},
alt: "Argument to getLog must be of type String",
},
} {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()

var actual bson.D
err := collection.Database().RunCommand(ctx, tc.command).Decode(&actual)
if err != nil {
AssertEqualAltError(t, *tc.err, tc.alt, err)
return
}
require.NoError(t, err)

m := actual.Map()
k := CollectKeys(t, actual)

for key, item := range tc.expected {
assert.Contains(t, k, key)
if key != "log" && key != "totalLinesWritten" {
assert.Equal(t, m[key], item)
}
}
})
}
}

func TestCommandsDiagnosticHostInfo(t *testing.T) {
Expand Down
96 changes: 64 additions & 32 deletions internal/handlers/pg/msg_getlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ import (
"strings"
"time"

"go.uber.org/zap/zapcore"

"github.com/FerretDB/FerretDB/internal/handlers/common"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/internal/util/logging"
"github.com/FerretDB/FerretDB/internal/util/must"
"github.com/FerretDB/FerretDB/internal/util/version"
"github.com/FerretDB/FerretDB/internal/wire"
Expand All @@ -36,52 +39,81 @@ func (h *Handler) MsgGetLog(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg,
return nil, lazyerrors.Error(err)
}

if l := document.Map()["getLog"]; l != "startupWarnings" {
errMsg := fmt.Sprintf("MsgGetLog: unhandled getLog value %q", l)
return nil, common.NewErrorMsg(common.ErrNotImplemented, errMsg)
}
command := document.Command()

var pv string
err = h.pgPool.QueryRow(ctx, "SHOW server_version").Scan(&pv)
getLog, err := document.Get(command)
if err != nil {
return nil, lazyerrors.Error(err)
}

pv, _, _ = strings.Cut(pv, " ")
mv := version.Get()
if _, ok := getLog.(string); !ok {
return nil, common.NewErrorMsg(common.ErrTypeMismatch, "Argument to getLog must be of type String")
}

var resDoc *types.Document
switch getLog {
case "*":
resDoc = must.NotFail(types.NewDocument(
"names", must.NotFail(types.NewArray("global", "startupWarnings")),
"ok", float64(1),
))

var log types.Array
for _, line := range []string{
"Powered by 🥭 FerretDB " + mv.Version + " and PostgreSQL " + pv + ".",
"Please star us on GitHub: https://github.com/FerretDB/FerretDB",
} {
b, err := json.Marshal(map[string]any{
"msg": line,
"tags": []string{"startupWarnings"},
"s": "I",
"c": "STORAGE",
"id": 42000,
"ctx": "initandlisten",
"t": map[string]string{
"$date": time.Now().UTC().Format("2006-01-02T15:04:05.999Z07:00"),
},
})
case "global":
log, err := logging.RecentEntries.GetArray(zapcore.DebugLevel)
if err != nil {
return nil, lazyerrors.Error(err)
}
if err = log.Append(string(b)); err != nil {
resDoc = must.NotFail(types.NewDocument(
"log", log,
"totalLinesWritten", int64(log.Len()),
"ok", float64(1),
))

case "startupWarnings":
var pv string
err = h.pgPool.QueryRow(ctx, "SHOW server_version").Scan(&pv)
if err != nil {
return nil, lazyerrors.Error(err)
}
}
pv, _, _ = strings.Cut(pv, " ")
mv := version.Get()

var reply wire.OpMsg
err = reply.SetSections(wire.OpMsgSection{
Documents: []*types.Document{must.NotFail(types.NewDocument(
"totalLinesWritten", int32(log.Len()),
var log types.Array
for _, line := range []string{
"Powered by 🥭 FerretDB " + mv.Version + " and PostgreSQL " + pv + ".",
"Please star us on GitHub: https://github.com/FerretDB/FerretDB",
} {
b, err := json.Marshal(map[string]any{
"msg": line,
"tags": []string{"startupWarnings"},
"s": "I",
"c": "STORAGE",
"id": 42000,
"ctx": "initandlisten",
"t": map[string]string{
"$date": time.Now().UTC().Format("2006-01-02T15:04:05.999Z07:00"),
},
})
if err != nil {
return nil, lazyerrors.Error(err)
}
if err = log.Append(string(b)); err != nil {
return nil, lazyerrors.Error(err)
}
}
resDoc = must.NotFail(types.NewDocument(
"log", &log,
"totalLinesWritten", int64(log.Len()),
"ok", float64(1),
))},
})
))

default:
errMsg := fmt.Sprintf("no RecentEntries named: %s", getLog)
return nil, common.NewErrorMsg(0, errMsg)
}

var reply wire.OpMsg
err = reply.SetSections(wire.OpMsgSection{Documents: []*types.Document{resDoc}})
if err != nil {
return nil, lazyerrors.Error(err)
}
Expand Down
94 changes: 65 additions & 29 deletions internal/handlers/tigris/msg_getlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ import (
"fmt"
"time"

"go.uber.org/zap/zapcore"

"github.com/FerretDB/FerretDB/internal/handlers/common"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/internal/util/logging"
"github.com/FerretDB/FerretDB/internal/util/must"
"github.com/FerretDB/FerretDB/internal/util/version"
"github.com/FerretDB/FerretDB/internal/wire"
Expand All @@ -35,48 +38,81 @@ func (h *Handler) MsgGetLog(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg,
return nil, lazyerrors.Error(err)
}

if l := document.Map()["getLog"]; l != "startupWarnings" {
errMsg := fmt.Sprintf("MsgGetLog: unhandled getLog value %q", l)
return nil, common.NewErrorMsg(common.ErrNotImplemented, errMsg)
}
command := document.Command()

info, err := h.driver.Info(ctx)
getLog, err := document.Get(command)
if err != nil {
return nil, lazyerrors.Error(err)
}

var log types.Array
for _, line := range []string{
"Powered by FerretDB " + version.Get().Version + " and Tigris " + info.ServerVersion + ".",
"Please star us on GitHub: https://github.com/FerretDB/FerretDB and https://github.com/tigrisdata/tigris",
} {
b, err := json.Marshal(map[string]any{
"msg": line,
"tags": []string{"startupWarnings"},
"s": "I",
"c": "STORAGE",
"id": 42000,
"ctx": "initandlisten",
"t": map[string]string{
"$date": time.Now().UTC().Format("2006-01-02T15:04:05.999Z07:00"),
},
})
if _, ok := getLog.(string); !ok {
return nil, common.NewErrorMsg(common.ErrTypeMismatch, "Argument to getLog must be of type String")
}

var resDoc *types.Document
switch getLog {
case "*":
resDoc = must.NotFail(types.NewDocument(
"names", must.NotFail(types.NewArray("global", "startupWarnings")),
"ok", float64(1),
))

case "global":
log, err := logging.RecentEntries.GetArray(zapcore.DebugLevel)
if err != nil {
return nil, lazyerrors.Error(err)
}
if err = log.Append(string(b)); err != nil {
resDoc = must.NotFail(types.NewDocument(
"log", log,
"totalLinesWritten", int64(log.Len()),
"ok", float64(1),
))

case "startupWarnings":
info, err := h.driver.Info(ctx)
if err != nil {
return nil, lazyerrors.Error(err)
}
}

var reply wire.OpMsg
must.NoError(reply.SetSections(wire.OpMsgSection{
Documents: []*types.Document{must.NotFail(types.NewDocument(
"totalLinesWritten", int32(log.Len()),
var log types.Array
for _, line := range []string{
"Powered by FerretDB " + version.Get().Version + " and Tigris " + info.ServerVersion + ".",
"Please star us on GitHub: https://github.com/FerretDB/FerretDB and https://github.com/tigrisdata/tigris",
} {
b, err := json.Marshal(map[string]any{
"msg": line,
"tags": []string{"startupWarnings"},
"s": "I",
"c": "STORAGE",
"id": 42000,
"ctx": "initandlisten",
"t": map[string]string{
"$date": time.Now().UTC().Format("2006-01-02T15:04:05.999Z07:00"),
},
})
if err != nil {
return nil, lazyerrors.Error(err)
}
if err = log.Append(string(b)); err != nil {
return nil, lazyerrors.Error(err)
}
}
resDoc = must.NotFail(types.NewDocument(
"log", &log,
"totalLinesWritten", int64(log.Len()),
"ok", float64(1),
))},
}))
))

default:
errMsg := fmt.Sprintf("no RecentEntries named: %s", getLog)
return nil, common.NewErrorMsg(0, errMsg)
}

var reply wire.OpMsg
err = reply.SetSections(wire.OpMsgSection{Documents: []*types.Document{resDoc}})
if err != nil {
return nil, lazyerrors.Error(err)
}

return &reply, nil
}
Loading

0 comments on commit 3d53f60

Please sign in to comment.