[MM-54593] HA aware Support Packet (#27598)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3f4b8e8137
Коммит
1158e6358c
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/einterfaces"
|
||||
)
|
||||
|
||||
@@ -144,6 +145,9 @@ func (c *ClusterMock) GetLogs(page, perPage int) ([]string, *model.AppError)
|
||||
func (c *ClusterMock) QueryLogs(page, perPage int) (map[string][]string, *model.AppError) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *ClusterMock) GenerateSupportPacket(rctx request.CTX, options *model.SupportPacketOptions) (map[string][]model.FileData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (c *ClusterMock) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { return nil, nil }
|
||||
func (c *ClusterMock) ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError {
|
||||
return nil
|
||||
|
||||
@@ -6,14 +6,16 @@ package platform
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/config"
|
||||
)
|
||||
|
||||
@@ -206,6 +208,40 @@ func (ps *PlatformService) GetLogsSkipSend(page, perPage int, logFilter *model.L
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) GetLogFile(_ request.CTX) (*model.FileData, error) {
|
||||
if !*ps.Config().LogSettings.EnableFile {
|
||||
return nil, errors.New("Unable to retrieve mattermost logs because LogSettings.EnableFile is set to false")
|
||||
}
|
||||
|
||||
mattermostLog := config.GetLogFileLocation(*ps.Config().LogSettings.FileLocation)
|
||||
mattermostLogFileData, err := os.ReadFile(mattermostLog)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed read mattermost log file at path %s", mattermostLog)
|
||||
}
|
||||
|
||||
return &model.FileData{
|
||||
Filename: config.LogFilename,
|
||||
Body: mattermostLogFileData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) GetNotificationLogFile(_ request.CTX) (*model.FileData, error) {
|
||||
if !*ps.Config().NotificationLogSettings.EnableFile {
|
||||
return nil, errors.New("Unable to retrieve notifications logs because NotificationLogSettings.EnableFile is set to false")
|
||||
}
|
||||
|
||||
notificationsLog := config.GetNotificationsLogFileLocation(*ps.Config().LogSettings.FileLocation)
|
||||
notificationsLogFileData, err := os.ReadFile(notificationsLog)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed read notifcation log file at path %s", notificationsLog)
|
||||
}
|
||||
|
||||
return &model.FileData{
|
||||
Filename: config.LogNotificationFilename,
|
||||
Body: notificationsLogFileData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func isLogFilteredByLevel(logFilter *model.LogFilter, entry *model.LogEntry) bool {
|
||||
logLevels := logFilter.LogLevels
|
||||
if len(logLevels) == 0 {
|
||||
|
||||
104
server/channels/app/platform/log_test.go
Обычный файл
104
server/channels/app/platform/log_test.go
Обычный файл
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/v8/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetMattermostLog(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
// disable mattermost log file setting in config so we should get an warning
|
||||
th.Service.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.LogSettings.EnableFile = false
|
||||
})
|
||||
|
||||
fileData, err := th.Service.GetLogFile(th.Context)
|
||||
assert.Nil(t, fileData)
|
||||
assert.ErrorContains(t, err, "Unable to retrieve mattermost logs because LogSettings.EnableFile is set to false")
|
||||
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err = os.RemoveAll(dir)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
// Enable log file but point to an empty directory to get an error trying to read the file
|
||||
th.Service.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.LogSettings.EnableFile = true
|
||||
*cfg.LogSettings.FileLocation = dir
|
||||
})
|
||||
|
||||
logLocation := config.GetLogFileLocation(dir)
|
||||
|
||||
// There is no mattermost.log file yet, so this fails
|
||||
fileData, err = th.Service.GetLogFile(th.Context)
|
||||
assert.Nil(t, fileData)
|
||||
assert.ErrorContains(t, err, "failed read mattermost log file at path "+logLocation)
|
||||
|
||||
// Happy path where we get a log file and no warning
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err = os.WriteFile(logLocation, d1, 0777)
|
||||
require.NoError(t, err)
|
||||
|
||||
fileData, err = th.Service.GetLogFile(th.Context)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fileData)
|
||||
assert.Equal(t, "mattermost.log", fileData.Filename)
|
||||
assert.Positive(t, len(fileData.Body))
|
||||
}
|
||||
|
||||
func TestGetNotificationLogFile(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
// Disable notifications file setting in config so we should get an warning
|
||||
th.Service.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.NotificationLogSettings.EnableFile = false
|
||||
})
|
||||
|
||||
fileData, err := th.Service.GetNotificationLogFile(th.Context)
|
||||
assert.Nil(t, fileData)
|
||||
assert.ErrorContains(t, err, "Unable to retrieve notifications logs because NotificationLogSettings.EnableFile is set to false")
|
||||
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err = os.RemoveAll(dir)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
// Enable notifications file but point to an empty directory to get an error trying to read the file
|
||||
th.Service.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.NotificationLogSettings.EnableFile = true
|
||||
*cfg.LogSettings.FileLocation = dir
|
||||
})
|
||||
|
||||
logLocation := config.GetNotificationsLogFileLocation(dir)
|
||||
|
||||
// There is no notifications.log file yet, so this fails
|
||||
fileData, err = th.Service.GetNotificationLogFile(th.Context)
|
||||
assert.Nil(t, fileData)
|
||||
assert.ErrorContains(t, err, "failed read notifcation log file at path "+logLocation)
|
||||
|
||||
// Happy path where we have file and no error
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err = os.WriteFile(logLocation, d1, 0777)
|
||||
require.NoError(t, err)
|
||||
|
||||
fileData, err = th.Service.GetNotificationLogFile(th.Context)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, fileData)
|
||||
assert.Equal(t, "notifications.log", fileData.Filename)
|
||||
assert.Positive(t, len(fileData.Body))
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"net/http/pprof"
|
||||
"path"
|
||||
"runtime"
|
||||
rpprof "runtime/pprof"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
@@ -23,11 +25,15 @@ import (
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/plugin"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/utils"
|
||||
"github.com/mattermost/mattermost/server/v8/einterfaces"
|
||||
)
|
||||
|
||||
const TimeToWaitForConnectionsToCloseOnServerShutdown = time.Second
|
||||
const (
|
||||
TimeToWaitForConnectionsToCloseOnServerShutdown = time.Second
|
||||
cpuProfileDuration = 5 * time.Second
|
||||
)
|
||||
|
||||
type platformMetrics struct {
|
||||
server *http.Server
|
||||
@@ -247,3 +253,52 @@ func (ps *PlatformService) Metrics() einterfaces.MetricsInterface {
|
||||
|
||||
return ps.metricsIFace
|
||||
}
|
||||
|
||||
func (ps *PlatformService) CreateCPUProfile(_ request.CTX) (*model.FileData, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
err := rpprof.StartCPUProfile(&b)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to start CPU profile")
|
||||
}
|
||||
|
||||
time.Sleep(cpuProfileDuration)
|
||||
|
||||
rpprof.StopCPUProfile()
|
||||
|
||||
fileData := &model.FileData{
|
||||
Filename: "cpu.prof",
|
||||
Body: b.Bytes(),
|
||||
}
|
||||
return fileData, nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) CreateHeapProfile(_ request.CTX) (*model.FileData, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
err := rpprof.Lookup("heap").WriteTo(&b, 0)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to lookup heap profile")
|
||||
}
|
||||
|
||||
fileData := &model.FileData{
|
||||
Filename: "heap.prof",
|
||||
Body: b.Bytes(),
|
||||
}
|
||||
return fileData, nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) CreateGoroutineProfile(_ request.CTX) (*model.FileData, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
err := rpprof.Lookup("goroutine").WriteTo(&b, 2)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to lookup goroutine profile")
|
||||
}
|
||||
|
||||
fileData := &model.FileData{
|
||||
Filename: "goroutines",
|
||||
Body: b.Bytes(),
|
||||
}
|
||||
return fileData, nil
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user