mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +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**=*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)
|
||||
|
@ -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`)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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"})
|
||||
|
Reference in New Issue
Block a user