mirror of
https://github.com/containers/podman.git
synced 2025-05-22 01:27:07 +08:00
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>
This commit is contained in:
@ -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 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*
|
**--mac-address**=*address*
|
||||||
|
|
||||||
Container MAC address (e.g. 92:d0:c6:0a:29:33)
|
Container MAC address (e.g. 92:d0:c6:0a:29:33)
|
||||||
|
@ -426,10 +426,21 @@ Logging driver for the container. Currently available options are *k8s-file* an
|
|||||||
|
|
||||||
**--log-opt**=*path*
|
**--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 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*
|
**--mac-address**=*address*
|
||||||
|
|
||||||
Container MAC address (e.g. `92:d0:c6:0a:29:33`)
|
Container MAC address (e.g. `92:d0:c6:0a:29:33`)
|
||||||
|
@ -379,6 +379,8 @@ type ContainerConfig struct {
|
|||||||
CgroupParent string `json:"cgroupParent"`
|
CgroupParent string `json:"cgroupParent"`
|
||||||
// LogPath log location
|
// LogPath log location
|
||||||
LogPath string `json:"logPath"`
|
LogPath string `json:"logPath"`
|
||||||
|
// LogTag is the tag used for logging
|
||||||
|
LogTag string `json:"logTag"`
|
||||||
// LogDriver driver for logs
|
// LogDriver driver for logs
|
||||||
LogDriver string `json:"logDriver"`
|
LogDriver string `json:"logDriver"`
|
||||||
// File containing the conmon PID
|
// File containing the conmon PID
|
||||||
@ -726,6 +728,11 @@ func (c *Container) LogPath() string {
|
|||||||
return c.config.LogPath
|
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.
|
// RestartPolicy returns the container's restart policy.
|
||||||
func (c *Container) RestartPolicy() string {
|
func (c *Container) RestartPolicy() string {
|
||||||
return c.config.RestartPolicy
|
return c.config.RestartPolicy
|
||||||
|
@ -107,6 +107,7 @@ type InspectContainerData struct {
|
|||||||
OCIConfigPath string `json:"OCIConfigPath,omitempty"`
|
OCIConfigPath string `json:"OCIConfigPath,omitempty"`
|
||||||
OCIRuntime string `json:"OCIRuntime,omitempty"`
|
OCIRuntime string `json:"OCIRuntime,omitempty"`
|
||||||
LogPath string `json:"LogPath"`
|
LogPath string `json:"LogPath"`
|
||||||
|
LogTag string `json:"LogTag"`
|
||||||
ConmonPidFile string `json:"ConmonPidFile"`
|
ConmonPidFile string `json:"ConmonPidFile"`
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
RestartCount int32 `json:"RestartCount"`
|
RestartCount int32 `json:"RestartCount"`
|
||||||
@ -629,17 +630,9 @@ type InspectNetworkSettings struct {
|
|||||||
MacAddress string `json:"MacAddress"`
|
MacAddress string `json:"MacAddress"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inspect a container for low-level information
|
// inspectLocked inspects a container for low-level information.
|
||||||
func (c *Container) Inspect(size bool) (*InspectContainerData, error) {
|
// The caller must held c.lock.
|
||||||
if !c.batched {
|
func (c *Container) inspectLocked(size bool) (*InspectContainerData, error) {
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
if err := c.syncContainer(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
storeCtr, err := c.runtime.store.Container(c.ID())
|
storeCtr, err := c.runtime.store.Container(c.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error getting container from store %q", c.ID())
|
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)
|
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) {
|
func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) (*InspectContainerData, error) {
|
||||||
config := c.config
|
config := c.config
|
||||||
runtimeInfo := c.state
|
runtimeInfo := c.state
|
||||||
@ -732,6 +739,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data)
|
|||||||
HostsPath: hostsPath,
|
HostsPath: hostsPath,
|
||||||
StaticDir: config.StaticDir,
|
StaticDir: config.StaticDir,
|
||||||
LogPath: config.LogPath,
|
LogPath: config.LogPath,
|
||||||
|
LogTag: config.LogTag,
|
||||||
OCIRuntime: config.OCIRuntime,
|
OCIRuntime: config.OCIRuntime,
|
||||||
ConmonPidFile: config.ConmonPidFile,
|
ConmonPidFile: config.ConmonPidFile,
|
||||||
Name: config.Name,
|
Name: config.Name,
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod/config"
|
"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 {
|
if logrus.GetLevel() != logrus.DebugLevel && r.supportsJSON {
|
||||||
ociLog = c.execOCILog(sessionID)
|
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 {
|
if options.PreserveFDs > 0 {
|
||||||
args = append(args, formatRuntimeOpts("--preserve-fds", fmt.Sprintf("%d", options.PreserveFDs))...)
|
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
|
// createOCIContainer generates this container's main conmon instance and prepares it for starting
|
||||||
func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) {
|
func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) {
|
||||||
var stderrBuf bytes.Buffer
|
var stderrBuf bytes.Buffer
|
||||||
@ -913,7 +935,13 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
|
|||||||
if logrus.GetLevel() != logrus.DebugLevel && r.supportsJSON {
|
if logrus.GetLevel() != logrus.DebugLevel && r.supportsJSON {
|
||||||
ociLog = filepath.Join(ctr.state.RunDir, "oci-log")
|
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 {
|
if ctr.config.Spec.Process.Terminal {
|
||||||
args = append(args, "-t")
|
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
|
// 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
|
// set the conmon API version to be able to use the correct sync struct keys
|
||||||
args := []string{"--api-version", "1"}
|
args := []string{"--api-version", "1"}
|
||||||
if r.cgroupManager == define.SystemdCgroupsManager && !ctr.config.NoCgroups {
|
if r.cgroupManager == define.SystemdCgroupsManager && !ctr.config.NoCgroups {
|
||||||
@ -1194,6 +1222,9 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p
|
|||||||
if ociLogPath != "" {
|
if ociLogPath != "" {
|
||||||
args = append(args, "--runtime-arg", "--log-format=json", "--runtime-arg", "--log", fmt.Sprintf("--runtime-arg=%s", 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 {
|
if ctr.config.NoCgroups {
|
||||||
logrus.Debugf("Running with no CGroups")
|
logrus.Debugf("Running with no CGroups")
|
||||||
args = append(args, "--runtime-arg", "--cgroup-manager", "--runtime-arg", "disabled")
|
args = append(args, "--runtime-arg", "--cgroup-manager", "--runtime-arg", "disabled")
|
||||||
|
@ -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.
|
// WithNoCgroups disables the creation of CGroups for the new container.
|
||||||
func WithNoCgroups() CtrCreateOption {
|
func WithNoCgroups() CtrCreateOption {
|
||||||
return func(ctr *Container) error {
|
return func(ctr *Container) error {
|
||||||
|
@ -282,10 +282,13 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
|
|||||||
options = append(options, libpod.WithStopSignal(c.StopSignal))
|
options = append(options, libpod.WithStopSignal(c.StopSignal))
|
||||||
options = append(options, libpod.WithStopTimeout(c.StopTimeout))
|
options = append(options, libpod.WithStopTimeout(c.StopTimeout))
|
||||||
|
|
||||||
logPath := getLoggingPath(c.LogDriverOpt)
|
logPath, logTag := getLoggingOpts(c.LogDriverOpt)
|
||||||
if logPath != "" {
|
if logPath != "" {
|
||||||
options = append(options, libpod.WithLogPath(logPath))
|
options = append(options, libpod.WithLogPath(logPath))
|
||||||
}
|
}
|
||||||
|
if logTag != "" {
|
||||||
|
options = append(options, libpod.WithLogTag(logTag))
|
||||||
|
}
|
||||||
|
|
||||||
if c.LogDriver != "" {
|
if c.LogDriver != "" {
|
||||||
options = append(options, libpod.WithLogDriver(c.LogDriver))
|
options = append(options, libpod.WithLogDriver(c.LogDriver))
|
||||||
|
@ -132,16 +132,23 @@ func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint
|
|||||||
}, nil
|
}, 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 {
|
for _, opt := range opts {
|
||||||
arr := strings.SplitN(opt, "=", 2)
|
arr := strings.SplitN(opt, "=", 2)
|
||||||
if len(arr) == 2 {
|
if len(arr) == 2 {
|
||||||
if strings.TrimSpace(arr[0]) == "path" {
|
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
|
// ParseDevice parses device mapping string to a src, dest & permissions string
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
. "github.com/containers/libpod/test/utils"
|
. "github.com/containers/libpod/test/utils"
|
||||||
@ -153,6 +155,23 @@ var _ = Describe("Podman logs", func() {
|
|||||||
Expect(results.ExitCode()).To(BeZero())
|
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() {
|
It("podman journald logs for container", func() {
|
||||||
Skip("need to verify images have correct packages for journald")
|
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"})
|
logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
|
||||||
|
Reference in New Issue
Block a user