Skip to content

Commit

Permalink
Merge pull request #3667 from haytok/issue_3486
Browse files Browse the repository at this point in the history
add fields such as CONTAINER_NAME to journald log entries sent to by containers
  • Loading branch information
fahedouch authored Nov 29, 2024
2 parents 1dd11e1 + b060ead commit 3c41efe
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 40 deletions.
47 changes: 36 additions & 11 deletions cmd/nerdctl/container/container_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,19 +329,44 @@ func TestRunWithJournaldLogDriver(t *testing.T) {
time.Sleep(3 * time.Second)
journalctl, err := exec.LookPath("journalctl")
assert.NilError(t, err)

inspectedContainer := base.InspectContainer(containerName)
found := 0
check := func(log poll.LogT) poll.Result {
res := icmd.RunCmd(icmd.Command(journalctl, "--no-pager", "--since", "2 minutes ago", fmt.Sprintf("SYSLOG_IDENTIFIER=%s", inspectedContainer.ID[:12])))
assert.Equal(t, 0, res.ExitCode, res)
if strings.Contains(res.Stdout(), "bar") && strings.Contains(res.Stdout(), "foo") {
found = 1
return poll.Success()
}
return poll.Continue("reading from journald is not yet finished")

type testCase struct {
name string
filter string
}
testCases := []testCase{
{
name: "filter journald logs using SYSLOG_IDENTIFIER field",
filter: fmt.Sprintf("SYSLOG_IDENTIFIER=%s", inspectedContainer.ID[:12]),
},
{
name: "filter journald logs using CONTAINER_NAME field",
filter: fmt.Sprintf("CONTAINER_NAME=%s", containerName),
},
{
name: "filter journald logs using IMAGE_NAME field",
filter: fmt.Sprintf("IMAGE_NAME=%s", testutil.CommonImage),
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
found := 0
check := func(log poll.LogT) poll.Result {
res := icmd.RunCmd(icmd.Command(journalctl, "--no-pager", "--since", "2 minutes ago", tc.filter))
assert.Equal(t, 0, res.ExitCode, res)
if strings.Contains(res.Stdout(), "bar") && strings.Contains(res.Stdout(), "foo") {
found = 1
return poll.Success()
}
return poll.Continue("reading from journald is not yet finished")
}
poll.WaitOn(t, check, poll.WithDelay(100*time.Microsecond), poll.WithTimeout(20*time.Second))
assert.Equal(t, 1, found)
})
}
poll.WaitOn(t, check, poll.WithDelay(100*time.Microsecond), poll.WithTimeout(20*time.Second))
assert.Equal(t, 1, found)
}

func TestRunWithJournaldLogDriverAndLogOpt(t *testing.T) {
Expand Down
7 changes: 4 additions & 3 deletions pkg/cmd/container/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func Create(ctx context.Context, client *containerd.Client, args []string, netMa
// 1, nerdctl run --name demo -it imagename
// 2, ctrl + c to stop demo container
// 3, nerdctl start/restart demo
logConfig, err := generateLogConfig(dataStore, id, options.LogDriver, options.LogOpt, options.GOptions.Namespace)
logConfig, err := generateLogConfig(dataStore, id, options.LogDriver, options.LogOpt, options.GOptions.Namespace, options.GOptions.Address)
if err != nil {
return nil, generateRemoveStateDirFunc(ctx, id, internalLabels), err
}
Expand Down Expand Up @@ -819,12 +819,13 @@ func writeCIDFile(path, id string) error {
}

// generateLogConfig creates a LogConfig for the current container store
func generateLogConfig(dataStore string, id string, logDriver string, logOpt []string, ns string) (logConfig logging.LogConfig, err error) {
func generateLogConfig(dataStore string, id string, logDriver string, logOpt []string, ns, address string) (logConfig logging.LogConfig, err error) {
var u *url.URL
if u, err = url.Parse(logDriver); err == nil && u.Scheme != "" {
logConfig.LogURI = logDriver
} else {
logConfig.Driver = logDriver
logConfig.Address = address
logConfig.Opts, err = parseKVStringsMapFromLogOpt(logOpt, logDriver)
if err != nil {
return logConfig, err
Expand All @@ -834,7 +835,7 @@ func generateLogConfig(dataStore string, id string, logDriver string, logOpt []s
logConfigB []byte
lu *url.URL
)
logDriverInst, err = logging.GetDriver(logDriver, logConfig.Opts)
logDriverInst, err = logging.GetDriver(logDriver, logConfig.Opts, logConfig.Address)
if err != nil {
return logConfig, err
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/cmd/system/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/containerd/nerdctl/v2/pkg/infoutil"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
"github.com/containerd/nerdctl/v2/pkg/logging"
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
"github.com/containerd/nerdctl/v2/pkg/strutil"
)
Expand Down Expand Up @@ -72,6 +73,7 @@ func Info(ctx context.Context, client *containerd.Client, options types.SystemIn
if err != nil {
return err
}
infoCompat.Plugins.Log = logging.Drivers()
default:
return fmt.Errorf("unknown mode %q", options.Mode)
}
Expand Down
2 changes: 0 additions & 2 deletions pkg/infoutil/infoutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import (
"github.com/containerd/nerdctl/v2/pkg/buildkitutil"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
"github.com/containerd/nerdctl/v2/pkg/logging"
"github.com/containerd/nerdctl/v2/pkg/version"
)

Expand Down Expand Up @@ -82,7 +81,6 @@ func Info(ctx context.Context, client *containerd.Client, snapshotter, cgroupMan
info.ID = daemonIntro.UUID
// Storage drivers and logging drivers are not really Server concept for nerdctl, but mimics `docker info` output
info.Driver = snapshotter
info.Plugins.Log = logging.Drivers()
info.Plugins.Storage = snapshotterPlugins
info.SystemTime = time.Now().Format(time.RFC3339Nano)
info.LoggingDriver = "json-file" // hard-coded
Expand Down
3 changes: 2 additions & 1 deletion pkg/logging/fluentd_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package logging

import (
"context"
"fmt"
"math"
"net/url"
Expand Down Expand Up @@ -99,7 +100,7 @@ func (f *FluentdLogger) Init(dataStore, ns, id string) error {
return nil
}

func (f *FluentdLogger) PreProcess(_ string, config *logging.Config) error {
func (f *FluentdLogger) PreProcess(_ context.Context, _ string, config *logging.Config) error {
if runtime.GOOS == "windows" {
// TODO: support fluentd on windows
return fmt.Errorf("logging to fluentd is not supported on windows")
Expand Down
38 changes: 35 additions & 3 deletions pkg/logging/journald_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package logging

import (
"bytes"
"context"
"errors"
"fmt"
"io"
Expand All @@ -35,6 +36,8 @@ import (
"github.com/containerd/containerd/v2/core/runtime/v2/logging"
"github.com/containerd/log"

"github.com/containerd/nerdctl/v2/pkg/clientutil"
"github.com/containerd/nerdctl/v2/pkg/containerutil"
"github.com/containerd/nerdctl/v2/pkg/strutil"
)

Expand All @@ -52,8 +55,9 @@ func JournalLogOptsValidate(logOptMap map[string]string) error {
}

type JournaldLogger struct {
Opts map[string]string
vars map[string]string
Opts map[string]string
vars map[string]string
Address string
}

type identifier struct {
Expand All @@ -66,7 +70,7 @@ func (journaldLogger *JournaldLogger) Init(dataStore, ns, id string) error {
return nil
}

func (journaldLogger *JournaldLogger) PreProcess(dataStore string, config *logging.Config) error {
func (journaldLogger *JournaldLogger) PreProcess(ctx context.Context, dataStore string, config *logging.Config) error {
if !journal.Enabled() {
return errors.New("the local systemd journal is not available for logging")
}
Expand Down Expand Up @@ -95,9 +99,37 @@ func (journaldLogger *JournaldLogger) PreProcess(dataStore string, config *loggi
syslogIdentifier = b.String()
}
}

client, ctx, cancel, err := clientutil.NewClient(ctx, config.Namespace, journaldLogger.Address)
if err != nil {
return err
}
defer func() {
cancel()
client.Close()
}()
containerID := config.ID
container, err := client.LoadContainer(ctx, containerID)
if err != nil {
return err
}
containerLabels, err := container.Labels(ctx)
if err != nil {
return err
}
containerInfo, err := container.Info(ctx)
if err != nil {
return err
}

// construct log metadata for the container
vars := map[string]string{
"SYSLOG_IDENTIFIER": syslogIdentifier,
"CONTAINER_TAG": syslogIdentifier,
"CONTAINER_ID": shortID,
"CONTAINER_ID_FULL": containerID,
"CONTAINER_NAME": containerutil.GetContainerName(containerLabels),
"IMAGE_NAME": containerInfo.Image,
}
journaldLogger.vars = vars
return nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/logging/json_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (jsonLogger *JSONLogger) Init(dataStore, ns, id string) error {
return nil
}

func (jsonLogger *JSONLogger) PreProcess(dataStore string, config *logging.Config) error {
func (jsonLogger *JSONLogger) PreProcess(ctx context.Context, dataStore string, config *logging.Config) error {
var jsonFilePath string
if logPath, ok := jsonLogger.Opts[LogPath]; ok {
jsonFilePath = logPath
Expand Down
31 changes: 16 additions & 15 deletions pkg/logging/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ const (

type Driver interface {
Init(dataStore, ns, id string) error
PreProcess(dataStore string, config *logging.Config) error
PreProcess(ctx context.Context, dataStore string, config *logging.Config) error
Process(stdout <-chan string, stderr <-chan string) error
PostProcess() error
}

type DriverFactory func(map[string]string) (Driver, error)
type DriverFactory func(map[string]string, string) (Driver, error)
type LogOptsValidateFunc func(logOptMap map[string]string) error

var drivers = make(map[string]DriverFactory)
Expand All @@ -81,28 +81,28 @@ func Drivers() []string {
return ss
}

func GetDriver(name string, opts map[string]string) (Driver, error) {
func GetDriver(name string, opts map[string]string, address string) (Driver, error) {
driverFactory, ok := drivers[name]
if !ok {
return nil, fmt.Errorf("unknown logging driver %q: %w", name, errdefs.ErrNotFound)
}
return driverFactory(opts)
return driverFactory(opts, address)
}

func init() {
RegisterDriver("none", func(opts map[string]string) (Driver, error) {
RegisterDriver("none", func(opts map[string]string, address string) (Driver, error) {
return &NoneLogger{}, nil
}, NoneLogOptsValidate)
RegisterDriver("json-file", func(opts map[string]string) (Driver, error) {
RegisterDriver("json-file", func(opts map[string]string, address string) (Driver, error) {
return &JSONLogger{Opts: opts}, nil
}, JSONFileLogOptsValidate)
RegisterDriver("journald", func(opts map[string]string) (Driver, error) {
return &JournaldLogger{Opts: opts}, nil
RegisterDriver("journald", func(opts map[string]string, address string) (Driver, error) {
return &JournaldLogger{Opts: opts, Address: address}, nil
}, JournalLogOptsValidate)
RegisterDriver("fluentd", func(opts map[string]string) (Driver, error) {
RegisterDriver("fluentd", func(opts map[string]string, address string) (Driver, error) {
return &FluentdLogger{Opts: opts}, nil
}, FluentdLogOptsValidate)
RegisterDriver("syslog", func(opts map[string]string) (Driver, error) {
RegisterDriver("syslog", func(opts map[string]string, address string) (Driver, error) {
return &SyslogLogger{Opts: opts}, nil
}, SyslogOptsValidate)
}
Expand All @@ -121,9 +121,10 @@ func Main(argv2 string) error {

// LogConfig is marshalled as "log-config.json"
type LogConfig struct {
Driver string `json:"driver"`
Opts map[string]string `json:"opts,omitempty"`
LogURI string `json:"-"`
Driver string `json:"driver"`
Opts map[string]string `json:"opts,omitempty"`
LogURI string `json:"-"`
Address string `json:"address"`
}

// LogConfigFilePath returns the path of log-config.json
Expand All @@ -149,7 +150,7 @@ func LoadLogConfig(dataStore, ns, id string) (LogConfig, error) {
}

func loggingProcessAdapter(ctx context.Context, driver Driver, dataStore string, config *logging.Config) error {
if err := driver.PreProcess(dataStore, config); err != nil {
if err := driver.PreProcess(ctx, dataStore, config); err != nil {
return err
}

Expand Down Expand Up @@ -215,7 +216,7 @@ func loggerFunc(dataStore string) (logging.LoggerFunc, error) {
if err != nil {
return err
}
driver, err := GetDriver(logConfig.Driver, logConfig.Opts)
driver, err := GetDriver(logConfig.Driver, logConfig.Opts, logConfig.Address)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/logging/logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (m *MockDriver) Init(dataStore, ns, id string) error {
return nil
}

func (m *MockDriver) PreProcess(dataStore string, config *logging.Config) error {
func (m *MockDriver) PreProcess(ctx context.Context, dataStore string, config *logging.Config) error {
return nil
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/logging/none_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package logging

import (
"context"

"github.com/containerd/containerd/v2/core/runtime/v2/logging"
)

Expand All @@ -28,7 +30,7 @@ func (n *NoneLogger) Init(dataStore, ns, id string) error {
return nil
}

func (n *NoneLogger) PreProcess(dataStore string, config *logging.Config) error {
func (n *NoneLogger) PreProcess(ctx context.Context, dataStore string, config *logging.Config) error {
return nil
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/logging/none_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package logging

import (
"context"
"os"
"testing"
"time"
Expand All @@ -29,6 +30,7 @@ import (
func TestNoneLogger(t *testing.T) {
// Create a temporary directory for potential log files
tmpDir := t.TempDir()
ctx := context.Background()

logger := &NoneLogger{
Opts: map[string]string{},
Expand All @@ -40,7 +42,7 @@ func TestNoneLogger(t *testing.T) {

// Run all logger methods
logger.Init(tmpDir, "namespace", "id")
logger.PreProcess(tmpDir, &logging.Config{})
logger.PreProcess(ctx, tmpDir, &logging.Config{})

stdout := make(chan string)
stderr := make(chan string)
Expand Down
3 changes: 2 additions & 1 deletion pkg/logging/syslog_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package logging

import (
"context"
"crypto/tls"
"errors"
"fmt"
Expand Down Expand Up @@ -122,7 +123,7 @@ func (sy *SyslogLogger) Init(dataStore string, ns string, id string) error {
return nil
}

func (sy *SyslogLogger) PreProcess(dataStore string, config *logging.Config) error {
func (sy *SyslogLogger) PreProcess(ctx context.Context, dataStore string, config *logging.Config) error {
logger, err := parseSyslog(config.ID, sy.Opts)
if err != nil {
return err
Expand Down

0 comments on commit 3c41efe

Please sign in to comment.