k8s.io/apiserver@v0.31.1/pkg/server/options/audit.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package options 18 19 import ( 20 "fmt" 21 "io" 22 "os" 23 "path/filepath" 24 "strings" 25 "time" 26 27 "github.com/spf13/pflag" 28 "gopkg.in/natefinch/lumberjack.v2" 29 "k8s.io/klog/v2" 30 31 "k8s.io/apimachinery/pkg/runtime/schema" 32 utilnet "k8s.io/apimachinery/pkg/util/net" 33 "k8s.io/apimachinery/pkg/util/sets" 34 auditinternal "k8s.io/apiserver/pkg/apis/audit" 35 auditv1 "k8s.io/apiserver/pkg/apis/audit/v1" 36 "k8s.io/apiserver/pkg/audit" 37 "k8s.io/apiserver/pkg/audit/policy" 38 "k8s.io/apiserver/pkg/server" 39 "k8s.io/apiserver/pkg/server/egressselector" 40 "k8s.io/apiserver/pkg/util/webhook" 41 pluginbuffered "k8s.io/apiserver/plugin/pkg/audit/buffered" 42 pluginlog "k8s.io/apiserver/plugin/pkg/audit/log" 43 plugintruncate "k8s.io/apiserver/plugin/pkg/audit/truncate" 44 pluginwebhook "k8s.io/apiserver/plugin/pkg/audit/webhook" 45 ) 46 47 const ( 48 // Default configuration values for ModeBatch. 49 defaultBatchBufferSize = 10000 // Buffer up to 10000 events before starting discarding. 50 // These batch parameters are only used by the webhook backend. 51 defaultBatchMaxSize = 400 // Only send up to 400 events at a time. 52 defaultBatchMaxWait = 30 * time.Second // Send events at least twice a minute. 53 defaultBatchThrottleQPS = 10 // Limit the send rate by 10 QPS. 54 defaultBatchThrottleBurst = 15 // Allow up to 15 QPS burst. 55 ) 56 57 func appendBackend(existing, newBackend audit.Backend) audit.Backend { 58 if existing == nil { 59 return newBackend 60 } 61 if newBackend == nil { 62 return existing 63 } 64 return audit.Union(existing, newBackend) 65 } 66 67 type AuditOptions struct { 68 // Policy configuration file for filtering audit events that are captured. 69 // If unspecified, a default is provided. 70 PolicyFile string 71 72 // Plugin options 73 LogOptions AuditLogOptions 74 WebhookOptions AuditWebhookOptions 75 } 76 77 const ( 78 // ModeBatch indicates that the audit backend should buffer audit events 79 // internally, sending batch updates either once a certain number of 80 // events have been received or a certain amount of time has passed. 81 ModeBatch = "batch" 82 // ModeBlocking causes the audit backend to block on every attempt to process 83 // a set of events. This causes requests to the API server to wait for the 84 // flush before sending a response. 85 ModeBlocking = "blocking" 86 // ModeBlockingStrict is the same as ModeBlocking, except when there is 87 // a failure during audit logging at RequestReceived stage, the whole 88 // request to apiserver will fail. 89 ModeBlockingStrict = "blocking-strict" 90 ) 91 92 // AllowedModes is the modes known for audit backends. 93 var AllowedModes = []string{ 94 ModeBatch, 95 ModeBlocking, 96 ModeBlockingStrict, 97 } 98 99 type AuditBatchOptions struct { 100 // Should the backend asynchronous batch events to the webhook backend or 101 // should the backend block responses? 102 // 103 // Defaults to asynchronous batch events. 104 Mode string 105 // Configuration for batching backend. Only used in batch mode. 106 BatchConfig pluginbuffered.BatchConfig 107 } 108 109 type AuditTruncateOptions struct { 110 // Whether truncating is enabled or not. 111 Enabled bool 112 113 // Truncating configuration. 114 TruncateConfig plugintruncate.Config 115 } 116 117 // AuditLogOptions determines the output of the structured audit log by default. 118 type AuditLogOptions struct { 119 Path string 120 MaxAge int 121 MaxBackups int 122 MaxSize int 123 Format string 124 Compress bool 125 126 BatchOptions AuditBatchOptions 127 TruncateOptions AuditTruncateOptions 128 129 // API group version used for serializing audit events. 130 GroupVersionString string 131 } 132 133 // AuditWebhookOptions control the webhook configuration for audit events. 134 type AuditWebhookOptions struct { 135 ConfigFile string 136 InitialBackoff time.Duration 137 138 BatchOptions AuditBatchOptions 139 TruncateOptions AuditTruncateOptions 140 141 // API group version used for serializing audit events. 142 GroupVersionString string 143 } 144 145 func NewAuditOptions() *AuditOptions { 146 return &AuditOptions{ 147 WebhookOptions: AuditWebhookOptions{ 148 InitialBackoff: pluginwebhook.DefaultInitialBackoffDelay, 149 BatchOptions: AuditBatchOptions{ 150 Mode: ModeBatch, 151 BatchConfig: defaultWebhookBatchConfig(), 152 }, 153 TruncateOptions: NewAuditTruncateOptions(), 154 GroupVersionString: "audit.k8s.io/v1", 155 }, 156 LogOptions: AuditLogOptions{ 157 Format: pluginlog.FormatJson, 158 BatchOptions: AuditBatchOptions{ 159 Mode: ModeBlocking, 160 BatchConfig: defaultLogBatchConfig(), 161 }, 162 TruncateOptions: NewAuditTruncateOptions(), 163 GroupVersionString: "audit.k8s.io/v1", 164 }, 165 } 166 } 167 168 func NewAuditTruncateOptions() AuditTruncateOptions { 169 return AuditTruncateOptions{ 170 Enabled: false, 171 TruncateConfig: plugintruncate.Config{ 172 MaxBatchSize: 10 * 1024 * 1024, // 10MB 173 MaxEventSize: 100 * 1024, // 100KB 174 }, 175 } 176 } 177 178 // Validate checks invalid config combination 179 func (o *AuditOptions) Validate() []error { 180 if o == nil { 181 return nil 182 } 183 184 var allErrors []error 185 allErrors = append(allErrors, o.LogOptions.Validate()...) 186 allErrors = append(allErrors, o.WebhookOptions.Validate()...) 187 188 return allErrors 189 } 190 191 func validateBackendMode(pluginName string, mode string) error { 192 for _, m := range AllowedModes { 193 if m == mode { 194 return nil 195 } 196 } 197 return fmt.Errorf("invalid audit %s mode %s, allowed modes are %q", pluginName, mode, strings.Join(AllowedModes, ",")) 198 } 199 200 func validateBackendBatchOptions(pluginName string, options AuditBatchOptions) error { 201 if err := validateBackendMode(pluginName, options.Mode); err != nil { 202 return err 203 } 204 if options.Mode != ModeBatch { 205 // Don't validate the unused options. 206 return nil 207 } 208 config := options.BatchConfig 209 if config.BufferSize <= 0 { 210 return fmt.Errorf("invalid audit batch %s buffer size %v, must be a positive number", pluginName, config.BufferSize) 211 } 212 if config.MaxBatchSize <= 0 { 213 return fmt.Errorf("invalid audit batch %s max batch size %v, must be a positive number", pluginName, config.MaxBatchSize) 214 } 215 if config.ThrottleEnable { 216 if config.ThrottleQPS <= 0 { 217 return fmt.Errorf("invalid audit batch %s throttle QPS %v, must be a positive number", pluginName, config.ThrottleQPS) 218 } 219 if config.ThrottleBurst <= 0 { 220 return fmt.Errorf("invalid audit batch %s throttle burst %v, must be a positive number", pluginName, config.ThrottleBurst) 221 } 222 } 223 return nil 224 } 225 226 var knownGroupVersions = []schema.GroupVersion{ 227 auditv1.SchemeGroupVersion, 228 } 229 230 func validateGroupVersionString(groupVersion string) error { 231 gv, err := schema.ParseGroupVersion(groupVersion) 232 if err != nil { 233 return err 234 } 235 if !knownGroupVersion(gv) { 236 return fmt.Errorf("invalid group version, allowed versions are %q", knownGroupVersions) 237 } 238 if gv != auditv1.SchemeGroupVersion { 239 klog.Warningf("%q is deprecated and will be removed in a future release, use %q instead", gv, auditv1.SchemeGroupVersion) 240 } 241 return nil 242 } 243 244 func knownGroupVersion(gv schema.GroupVersion) bool { 245 for _, knownGv := range knownGroupVersions { 246 if gv == knownGv { 247 return true 248 } 249 } 250 return false 251 } 252 253 func (o *AuditOptions) AddFlags(fs *pflag.FlagSet) { 254 if o == nil { 255 return 256 } 257 258 fs.StringVar(&o.PolicyFile, "audit-policy-file", o.PolicyFile, 259 "Path to the file that defines the audit policy configuration.") 260 261 o.LogOptions.AddFlags(fs) 262 o.LogOptions.BatchOptions.AddFlags(pluginlog.PluginName, fs) 263 o.LogOptions.TruncateOptions.AddFlags(pluginlog.PluginName, fs) 264 o.WebhookOptions.AddFlags(fs) 265 o.WebhookOptions.BatchOptions.AddFlags(pluginwebhook.PluginName, fs) 266 o.WebhookOptions.TruncateOptions.AddFlags(pluginwebhook.PluginName, fs) 267 } 268 269 func (o *AuditOptions) ApplyTo( 270 c *server.Config, 271 ) error { 272 if o == nil { 273 return nil 274 } 275 if c == nil { 276 return fmt.Errorf("server config must be non-nil") 277 } 278 279 // 1. Build policy evaluator 280 evaluator, err := o.newPolicyRuleEvaluator() 281 if err != nil { 282 return err 283 } 284 285 // 2. Build log backend 286 var logBackend audit.Backend 287 w, err := o.LogOptions.getWriter() 288 if err != nil { 289 return err 290 } 291 if w != nil { 292 if evaluator == nil { 293 klog.V(2).Info("No audit policy file provided, no events will be recorded for log backend") 294 } else { 295 logBackend = o.LogOptions.newBackend(w) 296 } 297 } 298 299 // 3. Build webhook backend 300 var webhookBackend audit.Backend 301 if o.WebhookOptions.enabled() { 302 if evaluator == nil { 303 klog.V(2).Info("No audit policy file provided, no events will be recorded for webhook backend") 304 } else { 305 if c.EgressSelector != nil { 306 var egressDialer utilnet.DialFunc 307 egressDialer, err = c.EgressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext()) 308 if err != nil { 309 return err 310 } 311 webhookBackend, err = o.WebhookOptions.newUntruncatedBackend(egressDialer) 312 } else { 313 webhookBackend, err = o.WebhookOptions.newUntruncatedBackend(nil) 314 } 315 if err != nil { 316 return err 317 } 318 } 319 } 320 321 groupVersion, err := schema.ParseGroupVersion(o.WebhookOptions.GroupVersionString) 322 if err != nil { 323 return err 324 } 325 326 // 4. Apply dynamic options. 327 var dynamicBackend audit.Backend 328 if webhookBackend != nil { 329 // if only webhook is enabled wrap it in the truncate options 330 dynamicBackend = o.WebhookOptions.TruncateOptions.wrapBackend(webhookBackend, groupVersion) 331 } 332 333 // 5. Set the policy rule evaluator 334 c.AuditPolicyRuleEvaluator = evaluator 335 336 // 6. Join the log backend with the webhooks 337 c.AuditBackend = appendBackend(logBackend, dynamicBackend) 338 339 if c.AuditBackend != nil { 340 klog.V(2).Infof("Using audit backend: %s", c.AuditBackend) 341 } 342 return nil 343 } 344 345 func (o *AuditOptions) newPolicyRuleEvaluator() (audit.PolicyRuleEvaluator, error) { 346 if o.PolicyFile == "" { 347 return nil, nil 348 } 349 350 p, err := policy.LoadPolicyFromFile(o.PolicyFile) 351 if err != nil { 352 return nil, fmt.Errorf("loading audit policy file: %v", err) 353 } 354 return policy.NewPolicyRuleEvaluator(p), nil 355 } 356 357 func (o *AuditBatchOptions) AddFlags(pluginName string, fs *pflag.FlagSet) { 358 fs.StringVar(&o.Mode, fmt.Sprintf("audit-%s-mode", pluginName), o.Mode, 359 "Strategy for sending audit events. Blocking indicates sending events should block"+ 360 " server responses. Batch causes the backend to buffer and write events"+ 361 " asynchronously. Known modes are "+strings.Join(AllowedModes, ",")+".") 362 fs.IntVar(&o.BatchConfig.BufferSize, fmt.Sprintf("audit-%s-batch-buffer-size", pluginName), 363 o.BatchConfig.BufferSize, "The size of the buffer to store events before "+ 364 "batching and writing. Only used in batch mode.") 365 fs.IntVar(&o.BatchConfig.MaxBatchSize, fmt.Sprintf("audit-%s-batch-max-size", pluginName), 366 o.BatchConfig.MaxBatchSize, "The maximum size of a batch. Only used in batch mode.") 367 fs.DurationVar(&o.BatchConfig.MaxBatchWait, fmt.Sprintf("audit-%s-batch-max-wait", pluginName), 368 o.BatchConfig.MaxBatchWait, "The amount of time to wait before force writing the "+ 369 "batch that hadn't reached the max size. Only used in batch mode.") 370 fs.BoolVar(&o.BatchConfig.ThrottleEnable, fmt.Sprintf("audit-%s-batch-throttle-enable", pluginName), 371 o.BatchConfig.ThrottleEnable, "Whether batching throttling is enabled. Only used in batch mode.") 372 fs.Float32Var(&o.BatchConfig.ThrottleQPS, fmt.Sprintf("audit-%s-batch-throttle-qps", pluginName), 373 o.BatchConfig.ThrottleQPS, "Maximum average number of batches per second. "+ 374 "Only used in batch mode.") 375 fs.IntVar(&o.BatchConfig.ThrottleBurst, fmt.Sprintf("audit-%s-batch-throttle-burst", pluginName), 376 o.BatchConfig.ThrottleBurst, "Maximum number of requests sent at the same "+ 377 "moment if ThrottleQPS was not utilized before. Only used in batch mode.") 378 } 379 380 type ignoreErrorsBackend struct { 381 audit.Backend 382 } 383 384 func (i *ignoreErrorsBackend) ProcessEvents(ev ...*auditinternal.Event) bool { 385 i.Backend.ProcessEvents(ev...) 386 return true 387 } 388 389 func (i *ignoreErrorsBackend) String() string { 390 return fmt.Sprintf("ignoreErrors<%s>", i.Backend) 391 } 392 393 func (o *AuditBatchOptions) wrapBackend(delegate audit.Backend) audit.Backend { 394 if o.Mode == ModeBlockingStrict { 395 return delegate 396 } 397 if o.Mode == ModeBlocking { 398 return &ignoreErrorsBackend{Backend: delegate} 399 } 400 return pluginbuffered.NewBackend(delegate, o.BatchConfig) 401 } 402 403 func (o *AuditTruncateOptions) Validate(pluginName string) error { 404 config := o.TruncateConfig 405 if config.MaxEventSize <= 0 { 406 return fmt.Errorf("invalid audit truncate %s max event size %v, must be a positive number", pluginName, config.MaxEventSize) 407 } 408 if config.MaxBatchSize < config.MaxEventSize { 409 return fmt.Errorf("invalid audit truncate %s max batch size %v, must be greater than "+ 410 "max event size (%v)", pluginName, config.MaxBatchSize, config.MaxEventSize) 411 } 412 return nil 413 } 414 415 func (o *AuditTruncateOptions) AddFlags(pluginName string, fs *pflag.FlagSet) { 416 fs.BoolVar(&o.Enabled, fmt.Sprintf("audit-%s-truncate-enabled", pluginName), 417 o.Enabled, "Whether event and batch truncating is enabled.") 418 fs.Int64Var(&o.TruncateConfig.MaxBatchSize, fmt.Sprintf("audit-%s-truncate-max-batch-size", pluginName), 419 o.TruncateConfig.MaxBatchSize, "Maximum size of the batch sent to the underlying backend. "+ 420 "Actual serialized size can be several hundreds of bytes greater. If a batch exceeds this limit, "+ 421 "it is split into several batches of smaller size.") 422 fs.Int64Var(&o.TruncateConfig.MaxEventSize, fmt.Sprintf("audit-%s-truncate-max-event-size", pluginName), 423 o.TruncateConfig.MaxEventSize, "Maximum size of the audit event sent to the underlying backend. "+ 424 "If the size of an event is greater than this number, first request and response are removed, and "+ 425 "if this doesn't reduce the size enough, event is discarded.") 426 } 427 428 func (o *AuditTruncateOptions) wrapBackend(delegate audit.Backend, gv schema.GroupVersion) audit.Backend { 429 if !o.Enabled { 430 return delegate 431 } 432 return plugintruncate.NewBackend(delegate, o.TruncateConfig, gv) 433 } 434 435 func (o *AuditLogOptions) AddFlags(fs *pflag.FlagSet) { 436 fs.StringVar(&o.Path, "audit-log-path", o.Path, 437 "If set, all requests coming to the apiserver will be logged to this file. '-' means standard out.") 438 fs.IntVar(&o.MaxAge, "audit-log-maxage", o.MaxAge, 439 "The maximum number of days to retain old audit log files based on the timestamp encoded in their filename.") 440 fs.IntVar(&o.MaxBackups, "audit-log-maxbackup", o.MaxBackups, 441 "The maximum number of old audit log files to retain. Setting a value of 0 will mean there's no restriction on the number of files.") 442 fs.IntVar(&o.MaxSize, "audit-log-maxsize", o.MaxSize, 443 "The maximum size in megabytes of the audit log file before it gets rotated.") 444 fs.StringVar(&o.Format, "audit-log-format", o.Format, 445 "Format of saved audits. \"legacy\" indicates 1-line text format for each event."+ 446 " \"json\" indicates structured json format. Known formats are "+ 447 strings.Join(pluginlog.AllowedFormats, ",")+".") 448 fs.StringVar(&o.GroupVersionString, "audit-log-version", o.GroupVersionString, 449 "API group and version used for serializing audit events written to log.") 450 fs.BoolVar(&o.Compress, "audit-log-compress", o.Compress, "If set, the rotated log files will be compressed using gzip.") 451 } 452 453 func (o *AuditLogOptions) Validate() []error { 454 // Check whether the log backend is enabled based on the options. 455 if !o.enabled() { 456 return nil 457 } 458 459 var allErrors []error 460 461 if err := validateBackendBatchOptions(pluginlog.PluginName, o.BatchOptions); err != nil { 462 allErrors = append(allErrors, err) 463 } 464 if err := o.TruncateOptions.Validate(pluginlog.PluginName); err != nil { 465 allErrors = append(allErrors, err) 466 } 467 468 if err := validateGroupVersionString(o.GroupVersionString); err != nil { 469 allErrors = append(allErrors, err) 470 } 471 472 // Check log format 473 if !sets.NewString(pluginlog.AllowedFormats...).Has(o.Format) { 474 allErrors = append(allErrors, fmt.Errorf("invalid audit log format %s, allowed formats are %q", o.Format, strings.Join(pluginlog.AllowedFormats, ","))) 475 } 476 477 // Check validities of MaxAge, MaxBackups and MaxSize of log options, if file log backend is enabled. 478 if o.MaxAge < 0 { 479 allErrors = append(allErrors, fmt.Errorf("--audit-log-maxage %v can't be a negative number", o.MaxAge)) 480 } 481 if o.MaxBackups < 0 { 482 allErrors = append(allErrors, fmt.Errorf("--audit-log-maxbackup %v can't be a negative number", o.MaxBackups)) 483 } 484 if o.MaxSize < 0 { 485 allErrors = append(allErrors, fmt.Errorf("--audit-log-maxsize %v can't be a negative number", o.MaxSize)) 486 } 487 488 return allErrors 489 } 490 491 // Check whether the log backend is enabled based on the options. 492 func (o *AuditLogOptions) enabled() bool { 493 return o != nil && o.Path != "" 494 } 495 496 func (o *AuditLogOptions) getWriter() (io.Writer, error) { 497 if !o.enabled() { 498 return nil, nil 499 } 500 501 if o.Path == "-" { 502 return os.Stdout, nil 503 } 504 505 if err := o.ensureLogFile(); err != nil { 506 return nil, fmt.Errorf("ensureLogFile: %w", err) 507 } 508 509 return &lumberjack.Logger{ 510 Filename: o.Path, 511 MaxAge: o.MaxAge, 512 MaxBackups: o.MaxBackups, 513 MaxSize: o.MaxSize, 514 Compress: o.Compress, 515 }, nil 516 } 517 518 func (o *AuditLogOptions) ensureLogFile() error { 519 if err := os.MkdirAll(filepath.Dir(o.Path), 0700); err != nil { 520 return err 521 } 522 mode := os.FileMode(0600) 523 f, err := os.OpenFile(o.Path, os.O_CREATE|os.O_APPEND|os.O_RDWR, mode) 524 if err != nil { 525 return err 526 } 527 return f.Close() 528 } 529 530 func (o *AuditLogOptions) newBackend(w io.Writer) audit.Backend { 531 groupVersion, _ := schema.ParseGroupVersion(o.GroupVersionString) 532 log := pluginlog.NewBackend(w, o.Format, groupVersion) 533 log = o.BatchOptions.wrapBackend(log) 534 log = o.TruncateOptions.wrapBackend(log, groupVersion) 535 return log 536 } 537 538 func (o *AuditWebhookOptions) AddFlags(fs *pflag.FlagSet) { 539 fs.StringVar(&o.ConfigFile, "audit-webhook-config-file", o.ConfigFile, 540 "Path to a kubeconfig formatted file that defines the audit webhook configuration.") 541 fs.DurationVar(&o.InitialBackoff, "audit-webhook-initial-backoff", 542 o.InitialBackoff, "The amount of time to wait before retrying the first failed request.") 543 fs.DurationVar(&o.InitialBackoff, "audit-webhook-batch-initial-backoff", 544 o.InitialBackoff, "The amount of time to wait before retrying the first failed request.") 545 fs.MarkDeprecated("audit-webhook-batch-initial-backoff", 546 "Deprecated, use --audit-webhook-initial-backoff instead.") 547 fs.StringVar(&o.GroupVersionString, "audit-webhook-version", o.GroupVersionString, 548 "API group and version used for serializing audit events written to webhook.") 549 } 550 551 func (o *AuditWebhookOptions) Validate() []error { 552 if !o.enabled() { 553 return nil 554 } 555 556 var allErrors []error 557 if err := validateBackendBatchOptions(pluginwebhook.PluginName, o.BatchOptions); err != nil { 558 allErrors = append(allErrors, err) 559 } 560 if err := o.TruncateOptions.Validate(pluginwebhook.PluginName); err != nil { 561 allErrors = append(allErrors, err) 562 } 563 564 if err := validateGroupVersionString(o.GroupVersionString); err != nil { 565 allErrors = append(allErrors, err) 566 } 567 return allErrors 568 } 569 570 func (o *AuditWebhookOptions) enabled() bool { 571 return o != nil && o.ConfigFile != "" 572 } 573 574 // newUntruncatedBackend returns a webhook backend without the truncate options applied 575 // this is done so that the same trucate backend can wrap both the webhook and dynamic backends 576 func (o *AuditWebhookOptions) newUntruncatedBackend(customDial utilnet.DialFunc) (audit.Backend, error) { 577 groupVersion, _ := schema.ParseGroupVersion(o.GroupVersionString) 578 webhook, err := pluginwebhook.NewBackend(o.ConfigFile, groupVersion, webhook.DefaultRetryBackoffWithInitialDelay(o.InitialBackoff), customDial) 579 if err != nil { 580 return nil, fmt.Errorf("initializing audit webhook: %v", err) 581 } 582 webhook = o.BatchOptions.wrapBackend(webhook) 583 return webhook, nil 584 } 585 586 // defaultWebhookBatchConfig returns the default BatchConfig used by the Webhook backend. 587 func defaultWebhookBatchConfig() pluginbuffered.BatchConfig { 588 return pluginbuffered.BatchConfig{ 589 BufferSize: defaultBatchBufferSize, 590 MaxBatchSize: defaultBatchMaxSize, 591 MaxBatchWait: defaultBatchMaxWait, 592 593 ThrottleEnable: true, 594 ThrottleQPS: defaultBatchThrottleQPS, 595 ThrottleBurst: defaultBatchThrottleBurst, 596 597 AsyncDelegate: true, 598 } 599 } 600 601 // defaultLogBatchConfig returns the default BatchConfig used by the Log backend. 602 func defaultLogBatchConfig() pluginbuffered.BatchConfig { 603 return pluginbuffered.BatchConfig{ 604 BufferSize: defaultBatchBufferSize, 605 // Batching is not useful for the log-file backend. 606 // MaxBatchWait ignored. 607 MaxBatchSize: 1, 608 ThrottleEnable: false, 609 // Asynchronous log threads just create lock contention. 610 AsyncDelegate: false, 611 } 612 }