-
Notifications
You must be signed in to change notification settings - Fork 366
Add OTLP job log export #3979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
catkins
wants to merge
13
commits into
main
Choose a base branch
from
catkins/codex/otlp-job-logs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,058
−34
Open
Add OTLP job log export #3979
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
54897fb
Spike OTLP job log export
catkins acc6ea3
Add OTLP job log trace fields
catkins 2e52895
Correlate OTLP job logs without backend propagation
catkins 9649d5c
Add missing context import in executor_test.go
catkins e30fc10
Use t.Context() in tests to satisfy usetesting linter
catkins 58eaa35
Consolidate OTLP job logs into bootstrap-only
catkins 890ee36
Redact secrets in OTLP job log records
catkins adaaefd
Mirror bootstrap control output into OTLP job logs
catkins a0a4224
Harden OTLP job log redaction and control tee lifecycle
catkins d7322f4
Address OTLP job log review feedback
catkins feca6a3
Share OTLP redaction through string search
catkins 0e5c4c9
Preserve OTLP redaction across commands
catkins 6dc004a
Align OTLP redaction flush boundaries
catkins File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -74,10 +74,54 @@ type Executor struct { | |||||||||||||||||||||||||||||||||||||||||||||||||
| // In order for the latter to happen, a reference is passed into the the Job API server as well | ||||||||||||||||||||||||||||||||||||||||||||||||||
| redactors *replacer.Mux | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // otlpJobLogger retains the OTLP redaction streams while job log export is | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // enabled. Executor redactor flush boundaries must flush these streams too | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // so OTLP and the customer-facing job log have identical stream semantics. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| otlpJobLogger *otlpJobLogger | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // jobAPI is the Job API server for this job. It's retained so the executor | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // can consume out-of-band requests made by hooks, such as a working | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // directory set by an unwrapped hook via the /workdir endpoint. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| jobAPI *jobapi.Server | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // loggerTee carries the redacted shell logger output (section headers, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // prompts, comments, warnings) to stderr and, when OTLP job logging is | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // enabled, mirrors it into the OTLP exporter so the exported records match | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // the customer-facing Buildkite job log. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| loggerTee *teeWriter | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // teeWriter writes to a primary writer and, when configured, also to a | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // secondary writer. The secondary sink can be attached after construction, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // which lets the executor mirror already-redacted shell logger output into the | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // OTLP exporter once it has been initialised. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| type teeWriter struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| mu sync.Mutex | ||||||||||||||||||||||||||||||||||||||||||||||||||
| primary io.Writer | ||||||||||||||||||||||||||||||||||||||||||||||||||
| secondary io.Writer | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func (w *teeWriter) Write(p []byte) (int, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Hold the lock for the whole write so that detaching the secondary sink | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // (setSecondary(nil), called before the OTLP exporter is flushed and shut | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // down) cannot complete while a write is still in flight. This guarantees | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // no control output lands on the OTLP emitter after it has been flushed. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| w.mu.Lock() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| defer w.mu.Unlock() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| n, err := w.primary.Write(p) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if w.secondary != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // The secondary sink is best-effort: failures must not affect the | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // primary (customer-facing) job log output. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| _, _ = w.secondary.Write(p) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return n, err | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func (w *teeWriter) setSecondary(secondary io.Writer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| w.mu.Lock() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| w.secondary = secondary | ||||||||||||||||||||||||||||||||||||||||||||||||||
| w.mu.Unlock() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // New returns a new executor instance | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -156,6 +200,36 @@ func (e *Executor) Run(ctx context.Context) (exitCode int) { | |||||||||||||||||||||||||||||||||||||||||||||||||
| // Create an empty env for us to keep track of our env changes in | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.shell.Env = env.FromSlice(os.Environ()) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // OTLP job log export lives entirely in the bootstrap process, which is the | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // single home for this feature. When OpenTelemetry tracing is enabled, the | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // per-command emit context carries the active hook/command span, so log | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // records are trace-correlated. With tracing disabled, records are still | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // emitted but remain uncorrelated. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if e.JobLogsOTLP { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| jobLogger, err := newOTLPJobLogger(ctx, e) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.shell.Warningf("Failed to initialize OTLP job log exporter: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.otlpJobLogger = jobLogger | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.shell.SetOutputInterceptor(jobLogger.Wrap) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Mirror the redacted shell logger control output (section headers, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // prompts, comments, warnings) into OTLP so the exported records | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // match the downloadable Buildkite job log and the UI stream. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if e.loggerTee != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.loggerTee.setSecondary(jobLogger.controlWriter()) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if e.loggerTee != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.loggerTee.setSecondary(nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := jobLogger.Close(); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.shell.Warningf("Failed to close OTLP job log exporter: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.otlpJobLogger = nil | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+209
to
+230
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The large func (e *Executor) setupOTLPJobLogger(ctx context.Context) func() {
jobLogger, err := newOTLPJobLogger(ctx, e)
if err != nil {
e.shell.Warningf("Failed to initialize OTLP job log exporter: %v", err)
return func() {}
}
e.otlpJobLogger = jobLogger
e.shell.SetOutputInterceptor(jobLogger.Wrap)
// Mirror the redacted shell logger control output (section headers,
// prompts, comments, warnings) into OTLP so the exported records
// match the downloadable Buildkite job log and the UI stream.
if e.loggerTee != nil {
e.loggerTee.setSecondary(jobLogger.controlWriter())
}
return func() {
if e.loggerTee != nil {
e.loggerTee.setSecondary(nil)
}
if err := jobLogger.Close(); err != nil {
e.shell.Warningf("Failed to close OTLP job log exporter: %v", err)
}
e.otlpJobLogger = nil
}
}
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Initialize the job API, iff the experiment is enabled. Noop otherwise | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if e.JobAPI { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cleanup, err := e.startJobAPI() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -361,6 +435,18 @@ type HookConfig struct { | |||||||||||||||||||||||||||||||||||||||||||||||||
| PluginName string | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func hookLogAttributes(hookCfg HookConfig) map[string]string { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| attrs := map[string]string{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "buildkite.phase": "hook", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "buildkite.hook.name": hookCfg.Name, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "buildkite.hook.scope": hookCfg.Scope, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if hookCfg.PluginName != "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| attrs["buildkite.hook.plugin"] = hookCfg.PluginName | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return attrs | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func (e *Executor) tracingImplementationSpecificHookScope(scope string) string { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if e.TracingBackend != tracetools.BackendDatadog { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return scope | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -463,7 +549,7 @@ func (e *Executor) runUnwrappedHook(ctx context.Context, _ string, hookCfg HookC | |||||||||||||||||||||||||||||||||||||||||||||||||
| environ.Set("BUILDKITE_HOOK_PATH", hookCfg.Path) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| environ.Set("BUILDKITE_HOOK_SCOPE", hookCfg.Scope) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := e.shell.Command(hookCfg.Path).Run(ctx, shell.WithExtraEnv(environ)); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := e.shell.Command(hookCfg.Path).Run(ctx, shell.WithExtraEnv(environ), shell.WithOutputAttributes(hookLogAttributes(hookCfg))); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Passing an empty env changes through because in polyglot hook we can't detect | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -537,7 +623,7 @@ func logMissingHookInfo(l shell.Logger, hookName, wrapperPath string) { | |||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func (e *Executor) runWrappedShellScriptHook(ctx context.Context, hookName string, hookCfg HookConfig) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := e.redactors.Flush(); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := e.flushOutputRedactors(); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.shell.Errorf("Error flushing redactors: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -572,7 +658,7 @@ func (e *Executor) runWrappedShellScriptHook(ctx context.Context, hookName strin | |||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return script.Run(ctx, shell.ShowPrompt(false), shell.WithExtraEnv(hookCfg.Env)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return script.Run(ctx, shell.ShowPrompt(false), shell.WithExtraEnv(hookCfg.Env), shell.WithOutputAttributes(hookLogAttributes(hookCfg))) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| exitCode := shell.ExitCode(err) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -730,6 +816,14 @@ func (e *Executor) addOutputRedactors() { | |||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func (e *Executor) flushOutputRedactors() error { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| err := e.redactors.Flush() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if e.otlpJobLogger != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.otlpJobLogger.FlushRedactors() | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| func (e *Executor) hasGlobalHook(name string) bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| _, err := hook.Find(nil, e.HooksPath, name) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1167,7 +1261,7 @@ func (e *Executor) CommandPhase(ctx context.Context) (hookErr, commandErr error) | |||||||||||||||||||||||||||||||||||||||||||||||||
| // defaultCommandPhase is executed if there is no global or plugin command hook | ||||||||||||||||||||||||||||||||||||||||||||||||||
| func (e *Executor) defaultCommandPhase(ctx context.Context) (retErr error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := e.redactors.Flush(); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := e.flushOutputRedactors(); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.shell.Errorf("Error flushing redactors: %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1299,7 +1393,11 @@ func (e *Executor) defaultCommandPhase(ctx context.Context) (retErr error) { | |||||||||||||||||||||||||||||||||||||||||||||||||
| e.shell.Promptf("%s", cmdToExec) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| err = e.shell.Command(cmd[0], cmd[1:]...).Run(ctx, shell.ShowPrompt(false)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| err = e.shell.Command(cmd[0], cmd[1:]...).Run(ctx, shell.ShowPrompt(false), shell.WithOutputAttributes(map[string]string{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "buildkite.phase": "command", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "buildkite.hook.name": "command", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "buildkite.hook.scope": "default", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| })) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return err | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1398,7 +1496,12 @@ func (e *Executor) setupRedactors(log shell.Logger, environ *env.Environment, st | |||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| stdoutRedactor := replacer.New(stdout, needles, redact.Redacted) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.redactors.Append(stdoutRedactor) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| loggerRedactor := replacer.New(stderr, needles, redact.Redacted) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // The shell logger writes through this redactor into loggerTee, whose | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // primary sink is stderr. When OTLP job logging is enabled, a secondary | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // sink is attached so the same already-redacted control output is mirrored | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // into the OTLP exporter. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.loggerTee = &teeWriter{primary: stderr} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| loggerRedactor := replacer.New(e.loggerTee, needles, redact.Redacted) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| e.redactors.Append(loggerRedactor) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| logger := shell.NewWriterLogger(loggerRedactor, true, e.DisabledWarnings) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the secondary sink considered best-effort? I can imagine a user totally ignoring the BK UI and instead expecting 100% of logs to always be present in their OTel system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thats reasonable