aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiuseppe Scrivano <gscrivan@redhat.com>2020-01-07 13:41:56 +0100
committerGiuseppe Scrivano <gscrivan@redhat.com>2020-01-10 10:35:19 +0100
commit71341a194843fadf836b5460d09f685bb6c465b4 (patch)
tree115b8394df383e81b40d322cabc864f576ed6478
parentf3fc10feb42930def6922fc050096ea38bafed7a (diff)
downloadpodman-71341a194843fadf836b5460d09f685bb6c465b4.tar.gz
podman-71341a194843fadf836b5460d09f685bb6c465b4.tar.bz2
podman-71341a194843fadf836b5460d09f685bb6c465b4.zip
log: support --log-opt tag=
support a custom tag to add to each log for the container. It is currently supported only by the journald backend. Closes: https://github.com/containers/libpod/issues/3653 Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
-rw-r--r--docs/source/markdown/podman-create.1.md11
-rw-r--r--docs/source/markdown/podman-run.1.md13
-rw-r--r--libpod/container.go7
-rw-r--r--libpod/container_inspect.go30
-rw-r--r--libpod/oci_conmon_linux.go37
-rw-r--r--libpod/options.go17
-rw-r--r--pkg/spec/createconfig.go5
-rw-r--r--pkg/spec/parse.go13
-rw-r--r--test/e2e/logs_test.go19
9 files changed, 133 insertions, 19 deletions
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 85aa81553..2bbe4f70a 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -419,6 +419,17 @@ Logging driver specific options. Used to set the path to the container log file
`--log-opt path=/var/log/container/mycontainer.json`
+**--log-opt**=*tag*
+
+Set custom logging configuration. Presently supports the `tag` option
+which specified a custom log tag for the container. For example:
+
+`--log-opt tag="{{.ImageName}}"`
+
+It supports the same keys as `podman inspect --format`.
+
+It is currently supported only by the journald log driver.
+
**--mac-address**=*address*
Container MAC address (e.g. 92:d0:c6:0a:29:33)
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index e8744de35..3f6effb75 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -426,10 +426,21 @@ Logging driver for the container. Currently available options are *k8s-file* an
**--log-opt**=*path*
-Logging driver specific options. Used to set the path to the container log file. For example:
+Set custom logging configuration. Presently supports the `tag` option
+which specified a custom log tag for the container. For example:
`--log-opt path=/var/log/container/mycontainer.json`
+**--log-opt**=*tag*
+
+Specify a custom log tag for the container. For example:
+
+`--log-opt tag="{{.ImageName}}"`
+
+It supports the same keys as `podman inspect --format`.
+
+It is currently supported only by the journald log driver.
+
**--mac-address**=*address*
Container MAC address (e.g. `92:d0:c6:0a:29:33`)
diff --git a/libpod/container.go b/libpod/container.go
index edf72f4ee..a046dcafc 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -379,6 +379,8 @@ type ContainerConfig struct {
CgroupParent string `json:"cgroupParent"`
// LogPath log location
LogPath string `json:"logPath"`
+ // LogTag is the tag used for logging
+ LogTag string `json:"logTag"`
// LogDriver driver for logs
LogDriver string `json:"logDriver"`
// File containing the conmon PID
@@ -726,6 +728,11 @@ func (c *Container) LogPath() string {
return c.config.LogPath
}
+// LogTag returns the tag to the container's log file
+func (c *Container) LogTag() string {
+ return c.config.LogTag
+}
+
// RestartPolicy returns the container's restart policy.
func (c *Container) RestartPolicy() string {
return c.config.RestartPolicy
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 639dd6e91..3f4ab394f 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -107,6 +107,7 @@ type InspectContainerData struct {
OCIConfigPath string `json:"OCIConfigPath,omitempty"`
OCIRuntime string `json:"OCIRuntime,omitempty"`
LogPath string `json:"LogPath"`
+ LogTag string `json:"LogTag"`
ConmonPidFile string `json:"ConmonPidFile"`
Name string `json:"Name"`
RestartCount int32 `json:"RestartCount"`
@@ -629,17 +630,9 @@ type InspectNetworkSettings struct {
MacAddress string `json:"MacAddress"`
}
-// Inspect a container for low-level information
-func (c *Container) Inspect(size bool) (*InspectContainerData, error) {
- if !c.batched {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- if err := c.syncContainer(); err != nil {
- return nil, err
- }
- }
-
+// inspectLocked inspects a container for low-level information.
+// The caller must held c.lock.
+func (c *Container) inspectLocked(size bool) (*InspectContainerData, error) {
storeCtr, err := c.runtime.store.Container(c.ID())
if err != nil {
return nil, errors.Wrapf(err, "error getting container from store %q", c.ID())
@@ -655,6 +648,20 @@ func (c *Container) Inspect(size bool) (*InspectContainerData, error) {
return c.getContainerInspectData(size, driverData)
}
+// Inspect a container for low-level information
+func (c *Container) Inspect(size bool) (*InspectContainerData, error) {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return nil, err
+ }
+ }
+
+ return c.inspectLocked(size)
+}
+
func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) (*InspectContainerData, error) {
config := c.config
runtimeInfo := c.state
@@ -732,6 +739,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
HostsPath: hostsPath,
StaticDir: config.StaticDir,
LogPath: config.LogPath,
+ LogTag: config.LogTag,
OCIRuntime: config.OCIRuntime,
ConmonPidFile: config.ConmonPidFile,
Name: config.Name,
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 0312f0ba2..d4bdabaf5 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"syscall"
+ "text/template"
"time"
"github.com/containers/libpod/libpod/config"
@@ -532,7 +533,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options
if logrus.GetLevel() != logrus.DebugLevel && r.supportsJSON {
ociLog = c.execOCILog(sessionID)
}
- args := r.sharedConmonArgs(c, sessionID, c.execBundlePath(sessionID), c.execPidPath(sessionID), c.execLogPath(sessionID), c.execExitFileDir(sessionID), ociLog)
+ args := r.sharedConmonArgs(c, sessionID, c.execBundlePath(sessionID), c.execPidPath(sessionID), c.execLogPath(sessionID), c.execExitFileDir(sessionID), ociLog, "")
if options.PreserveFDs > 0 {
args = append(args, formatRuntimeOpts("--preserve-fds", fmt.Sprintf("%d", options.PreserveFDs))...)
@@ -887,6 +888,27 @@ func waitPidStop(pid int, timeout time.Duration) error {
}
}
+func (r *ConmonOCIRuntime) getLogTag(ctr *Container) (string, error) {
+ logTag := ctr.LogTag()
+ if logTag == "" {
+ return "", nil
+ }
+ data, err := ctr.inspectLocked(false)
+ if err != nil {
+ return "", nil
+ }
+ tmpl, err := template.New("container").Parse(logTag)
+ if err != nil {
+ return "", errors.Wrapf(err, "template parsing error %s", logTag)
+ }
+ var b bytes.Buffer
+ err = tmpl.Execute(&b, data)
+ if err != nil {
+ return "", err
+ }
+ return b.String(), nil
+}
+
// createOCIContainer generates this container's main conmon instance and prepares it for starting
func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) {
var stderrBuf bytes.Buffer
@@ -913,7 +935,13 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
if logrus.GetLevel() != logrus.DebugLevel && r.supportsJSON {
ociLog = filepath.Join(ctr.state.RunDir, "oci-log")
}
- args := r.sharedConmonArgs(ctr, ctr.ID(), ctr.bundlePath(), filepath.Join(ctr.state.RunDir, "pidfile"), ctr.LogPath(), r.exitsDir, ociLog)
+
+ logTag, err := r.getLogTag(ctr)
+ if err != nil {
+ return err
+ }
+
+ args := r.sharedConmonArgs(ctr, ctr.ID(), ctr.bundlePath(), filepath.Join(ctr.state.RunDir, "pidfile"), ctr.LogPath(), r.exitsDir, ociLog, logTag)
if ctr.config.Spec.Process.Terminal {
args = append(args, "-t")
@@ -1147,7 +1175,7 @@ func (r *ConmonOCIRuntime) configureConmonEnv(runtimeDir string) ([]string, []*o
}
// sharedConmonArgs takes common arguments for exec and create/restore and formats them for the conmon CLI
-func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, pidPath, logPath, exitDir, ociLogPath string) []string {
+func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, pidPath, logPath, exitDir, ociLogPath, logTag string) []string {
// set the conmon API version to be able to use the correct sync struct keys
args := []string{"--api-version", "1"}
if r.cgroupManager == define.SystemdCgroupsManager && !ctr.config.NoCgroups {
@@ -1194,6 +1222,9 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p
if ociLogPath != "" {
args = append(args, "--runtime-arg", "--log-format=json", "--runtime-arg", "--log", fmt.Sprintf("--runtime-arg=%s", ociLogPath))
}
+ if logTag != "" {
+ args = append(args, "--log-tag", logTag)
+ }
if ctr.config.NoCgroups {
logrus.Debugf("Running with no CGroups")
args = append(args, "--runtime-arg", "--cgroup-manager", "--runtime-arg", "disabled")
diff --git a/libpod/options.go b/libpod/options.go
index 031f4f705..1d6863e7b 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1059,6 +1059,23 @@ func WithLogPath(path string) CtrCreateOption {
}
}
+// WithLogTag sets the tag to the log file.
+func WithLogTag(tag string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ if tag == "" {
+ return errors.Wrapf(define.ErrInvalidArg, "log tag must be set")
+ }
+
+ ctr.config.LogTag = tag
+
+ return nil
+ }
+
+}
+
// WithNoCgroups disables the creation of CGroups for the new container.
func WithNoCgroups() CtrCreateOption {
return func(ctr *Container) error {
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 244a8d1cd..6d058229b 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -282,10 +282,13 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, libpod.WithStopSignal(c.StopSignal))
options = append(options, libpod.WithStopTimeout(c.StopTimeout))
- logPath := getLoggingPath(c.LogDriverOpt)
+ logPath, logTag := getLoggingOpts(c.LogDriverOpt)
if logPath != "" {
options = append(options, libpod.WithLogPath(logPath))
}
+ if logTag != "" {
+ options = append(options, libpod.WithLogTag(logTag))
+ }
if c.LogDriver != "" {
options = append(options, libpod.WithLogDriver(c.LogDriver))
diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go
index c2572a033..6fa0b0636 100644
--- a/pkg/spec/parse.go
+++ b/pkg/spec/parse.go
@@ -132,16 +132,23 @@ func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint
}, nil
}
-func getLoggingPath(opts []string) string {
+// getLoggingOpts splits the path= and tag= options provided to --log-opt.
+func getLoggingOpts(opts []string) (string, string) {
+ var path, tag string
for _, opt := range opts {
arr := strings.SplitN(opt, "=", 2)
if len(arr) == 2 {
if strings.TrimSpace(arr[0]) == "path" {
- return strings.TrimSpace(arr[1])
+ path = strings.TrimSpace(arr[1])
+ } else if strings.TrimSpace(arr[0]) == "tag" {
+ tag = strings.TrimSpace(arr[1])
}
}
+ if path != "" && tag != "" {
+ break
+ }
}
- return ""
+ return path, tag
}
// ParseDevice parses device mapping string to a src, dest & permissions string
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index e25364695..0438a31cb 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -1,7 +1,9 @@
package integration
import (
+ "fmt"
"os"
+ "os/exec"
"strings"
. "github.com/containers/libpod/test/utils"
@@ -153,6 +155,23 @@ var _ = Describe("Podman logs", func() {
Expect(results.ExitCode()).To(BeZero())
})
+ It("podman journald logs for container with container tag", func() {
+ Skip("need to verify images have correct packages for journald")
+ logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "--log-opt=tag={{.ImageName}}", "-d", ALPINE, "sh", "-c", "echo podman; sleep 0.1; echo podman; sleep 0.1; echo podman"})
+ logc.WaitWithDefaultTimeout()
+ Expect(logc.ExitCode()).To(Equal(0))
+ cid := logc.OutputToString()
+
+ wait := podmanTest.Podman([]string{"wait", "-l"})
+ wait.WaitWithDefaultTimeout()
+ Expect(wait.ExitCode()).To(BeZero())
+
+ cmd := exec.Command("journalctl", "--no-pager", "-o", "json", "--output-fields=CONTAINER_TAG", "-u", fmt.Sprintf("libpod-conmon-%s.scope", cid))
+ out, err := cmd.CombinedOutput()
+ Expect(err).To(BeNil())
+ Expect(string(out)).To(ContainSubstring("alpine"))
+ })
+
It("podman journald logs for container", func() {
Skip("need to verify images have correct packages for journald")
logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})