logs: fix missing allocation logs after update to Nomad 1.5.4 (#17087)

When the server restarts for the upgrade, it loads the `structs.Job` from the
Raft snapshot/logs. The jobspec has long since been parsed, so none of the
guards around the default value are in play. The empty field value for `Enabled`
is the zero value, which is false.

This doesn't impact any running allocation because we don't replace running
allocations when either the client or server restart. But as soon as any
allocation gets rescheduled (ex. you drain all your clients during upgrades),
it'll be using the `structs.Job` that the server has, which has `Enabled =
false`, and logs will not be collected.

This changeset fixes the bug by adding a new field `Disabled` which defaults to
false (so that the zero value works), and deprecates the old field.

Fixes #17076
This commit is contained in:
Tim Gross 2023-05-04 16:01:18 -04:00 committed by GitHub
parent b4c9f3bbc2
commit 17bd930ca9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 108 additions and 72 deletions

3
.changelog/17087.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
logging: Fixed a bug where alloc logs would not be collected after an upgrade to 1.5.4
```

View File

@ -639,16 +639,21 @@ func (g *TaskGroup) AddSpread(s *Spread) *TaskGroup {
// LogConfig provides configuration for log rotation // LogConfig provides configuration for log rotation
type LogConfig struct { type LogConfig struct {
MaxFiles *int `mapstructure:"max_files" hcl:"max_files,optional"` MaxFiles *int `mapstructure:"max_files" hcl:"max_files,optional"`
MaxFileSizeMB *int `mapstructure:"max_file_size" hcl:"max_file_size,optional"` MaxFileSizeMB *int `mapstructure:"max_file_size" hcl:"max_file_size,optional"`
Enabled *bool `mapstructure:"enabled" hcl:"enabled,optional"`
// COMPAT(1.6.0): Enabled had to be swapped for Disabled to fix a backwards
// compatibility bug when restoring pre-1.5.4 jobs. Remove in 1.6.0
Enabled *bool `mapstructure:"enabled" hcl:"enabled,optional"`
Disabled *bool `mapstructure:"disabled" hcl:"disabled,optional"`
} }
func DefaultLogConfig() *LogConfig { func DefaultLogConfig() *LogConfig {
return &LogConfig{ return &LogConfig{
MaxFiles: pointerOf(10), MaxFiles: pointerOf(10),
MaxFileSizeMB: pointerOf(10), MaxFileSizeMB: pointerOf(10),
Enabled: pointerOf(true), Disabled: pointerOf(false),
} }
} }
@ -659,8 +664,8 @@ func (l *LogConfig) Canonicalize() {
if l.MaxFileSizeMB == nil { if l.MaxFileSizeMB == nil {
l.MaxFileSizeMB = pointerOf(10) l.MaxFileSizeMB = pointerOf(10)
} }
if l.Enabled == nil { if l.Disabled == nil {
l.Enabled = pointerOf(true) l.Disabled = pointerOf(false)
} }
} }

View File

@ -46,7 +46,7 @@ type logmonHook struct {
type logmonHookConfig struct { type logmonHookConfig struct {
logDir string logDir string
enabled bool disabled bool
stdoutFifo string stdoutFifo string
stderrFifo string stderrFifo string
} }
@ -63,12 +63,12 @@ func newLogMonHook(tr *TaskRunner, logger hclog.Logger) *logmonHook {
func newLogMonHookConfig(taskName string, logCfg *structs.LogConfig, logDir string) *logmonHookConfig { func newLogMonHookConfig(taskName string, logCfg *structs.LogConfig, logDir string) *logmonHookConfig {
cfg := &logmonHookConfig{ cfg := &logmonHookConfig{
logDir: logDir, logDir: logDir,
enabled: logCfg.Enabled, disabled: logCfg.Disabled,
} }
// If logging is disabled configure task's stdout/err to point to devnull // If logging is disabled configure task's stdout/err to point to devnull
if !logCfg.Enabled { if logCfg.Disabled {
cfg.stdoutFifo = os.DevNull cfg.stdoutFifo = os.DevNull
cfg.stderrFifo = os.DevNull cfg.stderrFifo = os.DevNull
return cfg return cfg
@ -116,7 +116,7 @@ func reattachConfigFromHookData(data map[string]string) (*plugin.ReattachConfig,
func (h *logmonHook) Prestart(ctx context.Context, func (h *logmonHook) Prestart(ctx context.Context,
req *interfaces.TaskPrestartRequest, resp *interfaces.TaskPrestartResponse) error { req *interfaces.TaskPrestartRequest, resp *interfaces.TaskPrestartResponse) error {
if !h.isLoggingEnabled() { if h.isLoggingDisabled() {
return nil return nil
} }
@ -151,10 +151,10 @@ func (h *logmonHook) Prestart(ctx context.Context,
} }
} }
func (h *logmonHook) isLoggingEnabled() bool { func (h *logmonHook) isLoggingDisabled() bool {
if !h.config.enabled { if h.config.disabled {
h.logger.Debug("log collection is disabled by task") h.logger.Debug("log collection is disabled by task")
return false return true
} }
// internal plugins have access to a capability to disable logging and // internal plugins have access to a capability to disable logging and
@ -162,16 +162,16 @@ func (h *logmonHook) isLoggingEnabled() bool {
// currently only the docker driver exposes this to users. // currently only the docker driver exposes this to users.
ic, ok := h.runner.driver.(drivers.InternalCapabilitiesDriver) ic, ok := h.runner.driver.(drivers.InternalCapabilitiesDriver)
if !ok { if !ok {
return true return false
} }
caps := ic.InternalCapabilities() caps := ic.InternalCapabilities()
if caps.DisableLogCollection { if caps.DisableLogCollection {
h.logger.Debug("log collection is disabled by driver") h.logger.Debug("log collection is disabled by driver")
return false return true
} }
return true return false
} }
func (h *logmonHook) prestartOneLoop(ctx context.Context, req *interfaces.TaskPrestartRequest) error { func (h *logmonHook) prestartOneLoop(ctx context.Context, req *interfaces.TaskPrestartRequest) error {
@ -219,7 +219,7 @@ func (h *logmonHook) prestartOneLoop(ctx context.Context, req *interfaces.TaskPr
} }
func (h *logmonHook) Stop(_ context.Context, req *interfaces.TaskStopRequest, _ *interfaces.TaskStopResponse) error { func (h *logmonHook) Stop(_ context.Context, req *interfaces.TaskStopRequest, _ *interfaces.TaskStopResponse) error {
if !h.isLoggingEnabled() { if h.isLoggingDisabled() {
return nil return nil
} }

View File

@ -112,7 +112,7 @@ func TestTaskRunner_LogmonHook_Disabled(t *testing.T) {
alloc := mock.BatchAlloc() alloc := mock.BatchAlloc()
task := alloc.Job.TaskGroups[0].Tasks[0] task := alloc.Job.TaskGroups[0].Tasks[0]
task.LogConfig.Enabled = false task.LogConfig.Disabled = true
dir := t.TempDir() dir := t.TempDir()

View File

@ -1242,11 +1242,7 @@ func ApiTaskToStructsTask(job *structs.Job, group *structs.TaskGroup,
structsTask.Resources = ApiResourcesToStructs(apiTask.Resources) structsTask.Resources = ApiResourcesToStructs(apiTask.Resources)
structsTask.LogConfig = &structs.LogConfig{ structsTask.LogConfig = apiLogConfigToStructs(apiTask.LogConfig)
MaxFiles: *apiTask.LogConfig.MaxFiles,
MaxFileSizeMB: *apiTask.LogConfig.MaxFileSizeMB,
Enabled: *apiTask.LogConfig.Enabled,
}
if len(apiTask.Artifacts) > 0 { if len(apiTask.Artifacts) > 0 {
structsTask.Artifacts = []*structs.TaskArtifact{} structsTask.Artifacts = []*structs.TaskArtifact{}
@ -1809,13 +1805,21 @@ func apiLogConfigToStructs(in *api.LogConfig) *structs.LogConfig {
if in == nil { if in == nil {
return nil return nil
} }
return &structs.LogConfig{ return &structs.LogConfig{
Enabled: *in.Enabled, Disabled: dereferenceBool(in.Disabled),
MaxFiles: dereferenceInt(in.MaxFiles), MaxFiles: dereferenceInt(in.MaxFiles),
MaxFileSizeMB: dereferenceInt(in.MaxFileSizeMB), MaxFileSizeMB: dereferenceInt(in.MaxFileSizeMB),
} }
} }
func dereferenceBool(in *bool) bool {
if in == nil {
return false
}
return *in
}
func dereferenceInt(in *int) int { func dereferenceInt(in *int) int {
if in == nil { if in == nil {
return 0 return 0

View File

@ -2767,7 +2767,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
KillTimeout: pointer.Of(10 * time.Second), KillTimeout: pointer.Of(10 * time.Second),
KillSignal: "SIGQUIT", KillSignal: "SIGQUIT",
LogConfig: &api.LogConfig{ LogConfig: &api.LogConfig{
Enabled: pointer.Of(true), Disabled: pointer.Of(true),
MaxFiles: pointer.Of(10), MaxFiles: pointer.Of(10),
MaxFileSizeMB: pointer.Of(100), MaxFileSizeMB: pointer.Of(100),
}, },
@ -3185,7 +3185,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
KillTimeout: 10 * time.Second, KillTimeout: 10 * time.Second,
KillSignal: "SIGQUIT", KillSignal: "SIGQUIT",
LogConfig: &structs.LogConfig{ LogConfig: &structs.LogConfig{
Enabled: true, Disabled: true,
MaxFiles: 10, MaxFiles: 10,
MaxFileSizeMB: 100, MaxFileSizeMB: 100,
}, },
@ -3342,7 +3342,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
KillTimeout: pointer.Of(10 * time.Second), KillTimeout: pointer.Of(10 * time.Second),
KillSignal: "SIGQUIT", KillSignal: "SIGQUIT",
LogConfig: &api.LogConfig{ LogConfig: &api.LogConfig{
Enabled: pointer.Of(true), Disabled: pointer.Of(true),
MaxFiles: pointer.Of(10), MaxFiles: pointer.Of(10),
MaxFileSizeMB: pointer.Of(100), MaxFileSizeMB: pointer.Of(100),
}, },
@ -3468,7 +3468,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
KillTimeout: 10 * time.Second, KillTimeout: 10 * time.Second,
KillSignal: "SIGQUIT", KillSignal: "SIGQUIT",
LogConfig: &structs.LogConfig{ LogConfig: &structs.LogConfig{
Enabled: true, Disabled: true,
MaxFiles: 10, MaxFiles: 10,
MaxFileSizeMB: 100, MaxFileSizeMB: 100,
}, },
@ -3639,16 +3639,39 @@ func TestConversion_dereferenceInt(t *testing.T) {
func TestConversion_apiLogConfigToStructs(t *testing.T) { func TestConversion_apiLogConfigToStructs(t *testing.T) {
ci.Parallel(t) ci.Parallel(t)
require.Nil(t, apiLogConfigToStructs(nil)) must.Nil(t, apiLogConfigToStructs(nil))
require.Equal(t, &structs.LogConfig{ must.Eq(t, &structs.LogConfig{
Enabled: true, Disabled: true,
MaxFiles: 2, MaxFiles: 2,
MaxFileSizeMB: 8, MaxFileSizeMB: 8,
}, apiLogConfigToStructs(&api.LogConfig{ }, apiLogConfigToStructs(&api.LogConfig{
Enabled: pointer.Of(true), Disabled: pointer.Of(true),
MaxFiles: pointer.Of(2), MaxFiles: pointer.Of(2),
MaxFileSizeMB: pointer.Of(8), MaxFileSizeMB: pointer.Of(8),
})) }))
// COMPAT(1.6.0): verify backwards compatibility fixes
// Note: we're intentionally ignoring the Enabled: false case
must.Eq(t, &structs.LogConfig{Disabled: false},
apiLogConfigToStructs(&api.LogConfig{
Enabled: pointer.Of(false),
}))
must.Eq(t, &structs.LogConfig{Disabled: false},
apiLogConfigToStructs(&api.LogConfig{
Enabled: pointer.Of(true),
}))
must.Eq(t, &structs.LogConfig{Disabled: false},
apiLogConfigToStructs(&api.LogConfig{}))
must.Eq(t, &structs.LogConfig{Disabled: false},
apiLogConfigToStructs(&api.LogConfig{
Disabled: pointer.Of(false),
}))
must.Eq(t, &structs.LogConfig{Disabled: false},
apiLogConfigToStructs(&api.LogConfig{
Enabled: pointer.Of(false),
Disabled: pointer.Of(false),
}))
} }
func TestConversion_apiResourcesToStructs(t *testing.T) { func TestConversion_apiResourcesToStructs(t *testing.T) {
@ -3743,7 +3766,7 @@ func TestConversion_apiConnectSidecarTaskToStructs(t *testing.T) {
Meta: meta, Meta: meta,
KillTimeout: &timeout, KillTimeout: &timeout,
LogConfig: &structs.LogConfig{ LogConfig: &structs.LogConfig{
Enabled: true, Disabled: true,
MaxFiles: 2, MaxFiles: 2,
MaxFileSizeMB: 8, MaxFileSizeMB: 8,
}, },
@ -3762,7 +3785,7 @@ func TestConversion_apiConnectSidecarTaskToStructs(t *testing.T) {
Meta: meta, Meta: meta,
KillTimeout: &timeout, KillTimeout: &timeout,
LogConfig: &api.LogConfig{ LogConfig: &api.LogConfig{
Enabled: pointer.Of(true), Disabled: pointer.Of(true),
MaxFiles: pointer.Of(2), MaxFiles: pointer.Of(2),
MaxFileSizeMB: pointer.Of(8), MaxFileSizeMB: pointer.Of(8),
}, },

View File

@ -262,7 +262,8 @@ func parseTask(item *ast.ObjectItem, keys []string) (*api.Task, error) {
valid := []string{ valid := []string{
"max_files", "max_files",
"max_file_size", "max_file_size",
"enabled", "enabled", // COMPAT(1.6.0): remove in favor of disabled
"disabled",
} }
if err := checkHCLKeys(logsBlock.Val, valid); err != nil { if err := checkHCLKeys(logsBlock.Val, valid); err != nil {
return nil, multierror.Prefix(err, "logs ->") return nil, multierror.Prefix(err, "logs ->")

View File

@ -350,7 +350,7 @@ func TestParse(t *testing.T) {
LogConfig: &api.LogConfig{ LogConfig: &api.LogConfig{
MaxFiles: intToPtr(14), MaxFiles: intToPtr(14),
MaxFileSizeMB: intToPtr(101), MaxFileSizeMB: intToPtr(101),
Enabled: boolToPtr(true), Disabled: boolToPtr(false),
}, },
Artifacts: []*api.TaskArtifact{ Artifacts: []*api.TaskArtifact{
{ {

View File

@ -194,7 +194,7 @@ job "binstore-storagelocker" {
} }
logs { logs {
enabled = true disabled = false
max_files = 14 max_files = 14
max_file_size = 101 max_file_size = 101
} }

View File

@ -4420,7 +4420,7 @@ func TestTaskDiff(t *testing.T) {
LogConfig: &LogConfig{ LogConfig: &LogConfig{
MaxFiles: 1, MaxFiles: 1,
MaxFileSizeMB: 10, MaxFileSizeMB: 10,
Enabled: false, Disabled: true,
}, },
}, },
Expected: &TaskDiff{ Expected: &TaskDiff{
@ -4432,9 +4432,9 @@ func TestTaskDiff(t *testing.T) {
Fields: []*FieldDiff{ Fields: []*FieldDiff{
{ {
Type: DiffTypeAdded, Type: DiffTypeAdded,
Name: "Enabled", Name: "Disabled",
Old: "", Old: "",
New: "false", New: "true",
}, },
{ {
Type: DiffTypeAdded, Type: DiffTypeAdded,
@ -4459,7 +4459,7 @@ func TestTaskDiff(t *testing.T) {
LogConfig: &LogConfig{ LogConfig: &LogConfig{
MaxFiles: 1, MaxFiles: 1,
MaxFileSizeMB: 10, MaxFileSizeMB: 10,
Enabled: false, Disabled: true,
}, },
}, },
New: &Task{}, New: &Task{},
@ -4472,8 +4472,8 @@ func TestTaskDiff(t *testing.T) {
Fields: []*FieldDiff{ Fields: []*FieldDiff{
{ {
Type: DiffTypeDeleted, Type: DiffTypeDeleted,
Name: "Enabled", Name: "Disabled",
Old: "false", Old: "true",
New: "", New: "",
}, },
{ {
@ -4499,14 +4499,14 @@ func TestTaskDiff(t *testing.T) {
LogConfig: &LogConfig{ LogConfig: &LogConfig{
MaxFiles: 1, MaxFiles: 1,
MaxFileSizeMB: 10, MaxFileSizeMB: 10,
Enabled: false, Disabled: false,
}, },
}, },
New: &Task{ New: &Task{
LogConfig: &LogConfig{ LogConfig: &LogConfig{
MaxFiles: 2, MaxFiles: 2,
MaxFileSizeMB: 20, MaxFileSizeMB: 20,
Enabled: true, Disabled: true,
}, },
}, },
Expected: &TaskDiff{ Expected: &TaskDiff{
@ -4518,7 +4518,7 @@ func TestTaskDiff(t *testing.T) {
Fields: []*FieldDiff{ Fields: []*FieldDiff{
{ {
Type: DiffTypeEdited, Type: DiffTypeEdited,
Name: "Enabled", Name: "Disabled",
Old: "false", Old: "false",
New: "true", New: "true",
}, },
@ -4546,14 +4546,14 @@ func TestTaskDiff(t *testing.T) {
LogConfig: &LogConfig{ LogConfig: &LogConfig{
MaxFiles: 1, MaxFiles: 1,
MaxFileSizeMB: 10, MaxFileSizeMB: 10,
Enabled: false, Disabled: false,
}, },
}, },
New: &Task{ New: &Task{
LogConfig: &LogConfig{ LogConfig: &LogConfig{
MaxFiles: 1, MaxFiles: 1,
MaxFileSizeMB: 20, MaxFileSizeMB: 20,
Enabled: true, Disabled: true,
}, },
}, },
Expected: &TaskDiff{ Expected: &TaskDiff{
@ -4565,7 +4565,7 @@ func TestTaskDiff(t *testing.T) {
Fields: []*FieldDiff{ Fields: []*FieldDiff{
{ {
Type: DiffTypeEdited, Type: DiffTypeEdited,
Name: "Enabled", Name: "Disabled",
Old: "false", Old: "false",
New: "true", New: "true",
}, },

View File

@ -7225,7 +7225,7 @@ const (
type LogConfig struct { type LogConfig struct {
MaxFiles int MaxFiles int
MaxFileSizeMB int MaxFileSizeMB int
Enabled bool Disabled bool
} }
func (l *LogConfig) Equal(o *LogConfig) bool { func (l *LogConfig) Equal(o *LogConfig) bool {
@ -7241,7 +7241,7 @@ func (l *LogConfig) Equal(o *LogConfig) bool {
return false return false
} }
if l.Enabled != o.Enabled { if l.Disabled != o.Disabled {
return false return false
} }
@ -7255,7 +7255,7 @@ func (l *LogConfig) Copy() *LogConfig {
return &LogConfig{ return &LogConfig{
MaxFiles: l.MaxFiles, MaxFiles: l.MaxFiles,
MaxFileSizeMB: l.MaxFileSizeMB, MaxFileSizeMB: l.MaxFileSizeMB,
Enabled: l.Enabled, Disabled: l.Disabled,
} }
} }
@ -7264,13 +7264,13 @@ func DefaultLogConfig() *LogConfig {
return &LogConfig{ return &LogConfig{
MaxFiles: 10, MaxFiles: 10,
MaxFileSizeMB: 10, MaxFileSizeMB: 10,
Enabled: true, Disabled: false,
} }
} }
// Validate returns an error if the log config specified are less than the // Validate returns an error if the log config specified are less than the
// minimum allowed. Note that because we have a non-zero default MaxFiles and // minimum allowed. Note that because we have a non-zero default MaxFiles and
// MaxFileSizeMB, we can't validate that they're unset if Enabled=false // MaxFileSizeMB, we can't validate that they're unset if Disabled=true
func (l *LogConfig) Validate() error { func (l *LogConfig) Validate() error {
var mErr multierror.Error var mErr multierror.Error
if l.MaxFiles < 1 { if l.MaxFiles < 1 {

View File

@ -194,7 +194,7 @@ FieldsLoop:
for _, fDiff := range oDiff.Fields { for _, fDiff := range oDiff.Fields {
switch fDiff.Name { switch fDiff.Name {
// force a destructive update if logger was enabled or disabled // force a destructive update if logger was enabled or disabled
case "Enabled": case "Disabled":
destructive = true destructive = true
break ObjectsLoop break ObjectsLoop
} }

View File

@ -346,7 +346,7 @@ func TestAnnotateTask(t *testing.T) {
Fields: []*structs.FieldDiff{ Fields: []*structs.FieldDiff{
{ {
Type: structs.DiffTypeAdded, Type: structs.DiffTypeAdded,
Name: "Enabled", Name: "Disabled",
Old: "true", Old: "true",
New: "false", New: "false",
}, },

View File

@ -305,11 +305,11 @@ func tasksUpdated(jobA, jobB *structs.Job, taskGroup string) comparison {
return difference("task identity", at.Identity, bt.Identity) return difference("task identity", at.Identity, bt.Identity)
} }
// Most LogConfig updates are in-place but if we change Enabled we need // Most LogConfig updates are in-place but if we change Disabled we need
// to recreate the task to stop/start log collection and change the // to recreate the task to stop/start log collection and change the
// stdout/stderr of the task // stdout/stderr of the task
if at.LogConfig.Enabled != bt.LogConfig.Enabled { if at.LogConfig.Disabled != bt.LogConfig.Disabled {
return difference("task log enabled", at.LogConfig.Enabled, bt.LogConfig.Enabled) return difference("task log disabled", at.LogConfig.Disabled, bt.LogConfig.Disabled)
} }
} }

View File

@ -604,7 +604,7 @@ fields:
## Stream Logs ## Stream Logs
This endpoint streams a task's stderr/stdout logs. Note that if logging is set This endpoint streams a task's stderr/stdout logs. Note that if logging is set
to [enabled=false][] for the task, this endpoint will return a 404 error. to [disabled=true][] for the task, this endpoint will return a 404 error.
| Method | Path | Produces | | Method | Path | Produces |
| ------ | ------------------------------ | ------------ | | ------ | ------------------------------ | ------------ |
@ -833,4 +833,4 @@ $ nomad operator api /v1/client/gc
``` ```
[api-node-read]: /nomad/api-docs/nodes [api-node-read]: /nomad/api-docs/nodes
[enabled=false]: /nomad/docs/job-specification/logs#enabled [disabled=true]: /nomad/docs/job-specification/logs#disabled

View File

@ -491,7 +491,7 @@ $ curl \
}, },
"KillTimeout": 5000000000, "KillTimeout": 5000000000,
"LogConfig": { "LogConfig": {
"Enabled": true, "Disabled": false,
"MaxFiles": 10, "MaxFiles": 10,
"MaxFileSizeMB": 10 "MaxFileSizeMB": 10
}, },
@ -763,7 +763,7 @@ $ curl \
"Leader": false, "Leader": false,
"Lifecycle": null, "Lifecycle": null,
"LogConfig": { "LogConfig": {
"Enabled": true, "Disabled": false,
"MaxFileSizeMB": 10, "MaxFileSizeMB": 10,
"MaxFiles": 10 "MaxFiles": 10
}, },
@ -981,7 +981,7 @@ $ curl \
"Leader": false, "Leader": false,
"Lifecycle": null, "Lifecycle": null,
"LogConfig": { "LogConfig": {
"Enabled": true, "Disabled": false,
"MaxFileSizeMB": 10, "MaxFileSizeMB": 10,
"MaxFiles": 10 "MaxFiles": 10
}, },
@ -1148,7 +1148,7 @@ $ curl \
"Leader": false, "Leader": false,
"Lifecycle": null, "Lifecycle": null,
"LogConfig": { "LogConfig": {
"Enabled": true, "Disabled": false,
"MaxFileSizeMB": 10, "MaxFileSizeMB": 10,
"MaxFiles": 10 "MaxFiles": 10
}, },

View File

@ -927,7 +927,7 @@ The `Affinity` object supports the following keys:
The `LogConfig` object configures the log rotation policy for a task's `stdout` and The `LogConfig` object configures the log rotation policy for a task's `stdout` and
`stderr`. The `LogConfig` object supports the following attributes: `stderr`. The `LogConfig` object supports the following attributes:
- `Enabled` - Enables log collection. - `Disabled` - Disables log collection.
- `MaxFiles` - The maximum number of rotated files Nomad will retain for - `MaxFiles` - The maximum number of rotated files Nomad will retain for
`stdout` and `stderr`, each tracked individually. `stdout` and `stderr`, each tracked individually.
@ -942,7 +942,7 @@ a validation error when a job is submitted.
```json ```json
{ {
"LogConfig": { "LogConfig": {
"Enabled": true, "Disabled": false,
"MaxFiles": 3, "MaxFiles": 3,
"MaxFileSizeMB": 10 "MaxFileSizeMB": 10
} }

View File

@ -31,7 +31,7 @@ job "docs" {
logs { logs {
max_files = 10 max_files = 10
max_file_size = 10 max_file_size = 10
enabled = true disabled = false
} }
} }
} }
@ -52,8 +52,8 @@ please see the [`nomad alloc logs`][logs-command] command.
the total amount of disk space needed to retain the rotated set of files, the total amount of disk space needed to retain the rotated set of files,
Nomad will return a validation error when a job is submitted. Nomad will return a validation error when a job is submitted.
- `enabled` `(bool: true)` - Specifies that log collection should be enabled for - `disabled` `(bool: false)` - Specifies that log collection should be enabled for
this task. If set to `false`, the task driver will attach stdout/stderr of the this task. If set to `true`, the task driver will attach stdout/stderr of the
task to `/dev/null` (or `NUL` on Windows). You should only disable log task to `/dev/null` (or `NUL` on Windows). You should only disable log
collection if your application has some other way of emitting logs, such as collection if your application has some other way of emitting logs, such as
writing to a remote syslog server. Note that the `nomad alloc logs` command writing to a remote syslog server. Note that the `nomad alloc logs` command
@ -61,7 +61,7 @@ please see the [`nomad alloc logs`][logs-command] command.
Some task drivers such as `docker` support a [`disable_log_collection`][] Some task drivers such as `docker` support a [`disable_log_collection`][]
option. If the task driver's `disable_log_collection` option is set to `true`, option. If the task driver's `disable_log_collection` option is set to `true`,
it will override `enabled=true` in the task's `logs` block. it will override `disabled=false` in the task's `logs` block.
## `logs` Examples ## `logs` Examples