Skip to content
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

Implement an initial version of the support bundle in Alloy #2009

Merged
merged 31 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fb413ab
Implement an initial version of the support bundle in Alloy
dehaansa Oct 31, 2024
be6e95b
Add documentation for support bundle
dehaansa Oct 31, 2024
479291e
Update changelog
dehaansa Oct 31, 2024
efec44a
Merge branch 'main' into feat/support-bundle
dehaansa Oct 31, 2024
131b90b
Merge branch 'main' into feat/support-bundle
dehaansa Nov 1, 2024
8576be3
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 4, 2024
402b477
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 4, 2024
f22f663
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 4, 2024
b95a248
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 4, 2024
c04efb3
Initial PR feedback
dehaansa Nov 5, 2024
91c2fb0
Merge branch 'feat/support-bundle' of github.com:dehaansa/alloy into …
dehaansa Nov 5, 2024
e4d8deb
Rewrite http service to use logging library internal to alloy
dehaansa Nov 5, 2024
657bd04
Revert accidental commit of e2e test changes
dehaansa Nov 5, 2024
7307d16
Fix comment on exported function
dehaansa Nov 5, 2024
3740c27
Clean up added host variable that is no longer used
dehaansa Nov 5, 2024
93ada94
Merge branch 'main' into feat/support-bundle
dehaansa Nov 5, 2024
ff6412f
Refactor usage of logger in http service
dehaansa Nov 6, 2024
e8a30d5
Update internal/service/http/http.go
dehaansa Nov 7, 2024
8ff98a0
Merge branch 'main' into feat/support-bundle
dehaansa Nov 7, 2024
a1cd13a
implement PR feedback
dehaansa Nov 7, 2024
f4fc921
Hide support bundle behind public preview stability level
dehaansa Nov 7, 2024
3d5f9af
Update docs based on feedback
dehaansa Nov 7, 2024
88c4224
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 7, 2024
f23481f
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 7, 2024
1755e9d
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 7, 2024
f10ac8e
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 7, 2024
034265c
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 7, 2024
f5fd0d0
Update docs/sources/troubleshoot/support_bundle.md
dehaansa Nov 7, 2024
a8e01da
More PR feedback in docs
dehaansa Nov 7, 2024
62b2736
Fix race condition in logger
thampiotr Nov 11, 2024
3599ef9
Add a note about backward-compatibility exception
thampiotr Nov 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ v1.5.0-rc.0

### Features

- Add support bundle generation via the API endpoint /-/support (@dehaansa)
thampiotr marked this conversation as resolved.
Show resolved Hide resolved

- Add the function `path_join` to the stdlib. (@wildum)
- Add `pyroscope.receive_http` component to receive and forward Pyroscope profiles (@marcsanmi)

Expand Down
2 changes: 2 additions & 0 deletions docs/sources/reference/cli/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The following flags are supported:
* `--server.http.ui-path-prefix`: Base path where the UI is exposed (default `/`).
* `--storage.path`: Base directory where components can store data (default `data-alloy/`).
* `--disable-reporting`: Disable [data collection][] (default `false`).
* `--disable-support-bundle`: Disable [support bundle][] endpoint (default `false`).
thampiotr marked this conversation as resolved.
Show resolved Hide resolved
* `--cluster.enabled`: Start {{< param "PRODUCT_NAME" >}} in clustered mode (default `false`).
* `--cluster.node-name`: The name to use for this node (defaults to the environment's hostname).
* `--cluster.join-addresses`: Comma-separated list of addresses to join the cluster at (default `""`). Mutually exclusive with `--cluster.discover-peers`.
Expand Down Expand Up @@ -178,6 +179,7 @@ Refer to [alloy convert][] for more details on how `extra-args` work.
[go-discover]: https://github.com/hashicorp/go-discover
[in-memory HTTP traffic]: ../../../get-started/component_controller/#in-memory-traffic
[data collection]: ../../../data-collection/
[support bundle]: ../../../troubleshoot/support_bundle
[components]: ../../get-started/components/
[component controller]: ../../../get-started/component_controller/
[UI]: ../../../troubleshoot/debug/#clustering-page
44 changes: 44 additions & 0 deletions docs/sources/troubleshoot/support_bundle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
canonical: https://grafana.com/docs/alloy/latest/troubleshoot/support_bundle/
description: Learn how to generate a support bundle
title: Generate a Support Bundle
dehaansa marked this conversation as resolved.
Show resolved Hide resolved
menuTitle: Generate Support Bundle
dehaansa marked this conversation as resolved.
Show resolved Hide resolved
weight: 300
---

# Generate Support Bundle
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be released as Public preview?

If yes, then we probably should add the Public preview bits at the top of the doc topic.

<span class="badge docs-labels__stage docs-labels__item">Public preview</span>

# Generate Support Bundle

{{< docs/shared lookup="stability/public_preview.md" source="alloy" version="<ALLOY_VERSION>" >}}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the shared component for public_preview? The language doesn't seem quite right for this feature

image

Copy link
Contributor

@clayton-cornell clayton-cornell Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh good point. Lets switch to the generic one instead. We could use this in its place:

{{< docs/public-preview product="Generate support bundle" >

https://grafana.com/docs/writers-toolkit/write/shortcodes/#docspublic-preview (if it's behind a feature flag, there's a block for that too)

This will create a Note block that says:

Generate support bundle is currently in public preview. Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available.

dehaansa marked this conversation as resolved.
Show resolved Hide resolved

The `/-/support?duration=N` endpoint returns a 'support bundle', a zip file that contains information
dehaansa marked this conversation as resolved.
Show resolved Hide resolved
about a running {{< param "PRODUCT_NAME" >}} instance, and can be used as a baseline of information when trying
to debug an issue.

{{< admonition type="note" >}}
This endpoint is enabled by default, but may be disabled using the `--disable-support-bundle` runtime flag.
{{< /admonition >}}

The duration parameter is optional, must be less than or equal to the
configured HTTP server write timeout, and if not provided, defaults to it.
The endpoint is only exposed to the {{< param "PRODUCT_NAME" >}} HTTP server listen address, which
defaults to `localhost:12345`.

The support bundle contains all information in plain text, so you can
inspect it before sharing to verify that no sensitive information has leaked.

In addition, you can inspect the [supportbundle implementation](https://github.com/grafana/alloy/tree/internal/service/http/supportbundle.go)
to verify the code used to generate these bundles.

A support bundle contains the following data:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will do to support questions what 🚒 does to 🔥
🎉

* `alloy-components.json` contains information about the [components][components] running on this {{< param "PRODUCT_NAME" >}} instance, generated by the
`/api/v0/web/components` endpoint.
* `alloy-logs.txt` contains the logs during the bundle generation.
* `alloy-metadata.yaml` contains the {{< param "PRODUCT_NAME" >}} build version and the installation's operating system, architecture, and uptime.
* `alloy-metrics.txt` contains a snapshot of {{< param "PRODUCT_NAME" >}}'s internal metrics.
dehaansa marked this conversation as resolved.
Show resolved Hide resolved
* `alloy-peers.json` contains information about the identified cluster peers of this {{< param "PRODUCT_NAME" >}} instance, generated by the
`/api/v0/web/peers` endpoint.
* `alloy-runtime-flags.txt` contains the values of the runtime flags available in {{< param "PRODUCT_NAME" >}}.
thampiotr marked this conversation as resolved.
Show resolved Hide resolved
* The `pprof/` directory contains Go runtime profiling data (CPU, heap, goroutine, mutex, block profiles) as exported by the pprof package. See the [profile][profile]
documentation for more details on how to use this information.
dehaansa marked this conversation as resolved.
Show resolved Hide resolved

wildum marked this conversation as resolved.
Show resolved Hide resolved
[profile]: ../profile
[components]: ../../get-started/components/
[alloy-repo]: https://github.com/grafana/alloy/issues
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,8 @@ require (
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.7.0 // indirect
)

require github.com/mackerelio/go-osstat v0.2.5

// NOTE: replace directives below must always be *temporary*.
//
// Adding a replace directive to change a module to a fork of a module will
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1702,6 +1702,8 @@ github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMn
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/lyft/protoc-gen-validate v0.0.0-20180911180927-64fcb82c878e/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o=
github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
Expand Down
28 changes: 22 additions & 6 deletions internal/alloycli/cmd_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/grafana/ckit/peer"
"github.com/prometheus/client_golang/prometheus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"go.opentelemetry.io/otel"
"golang.org/x/exp/maps"

Expand Down Expand Up @@ -64,6 +65,7 @@ func runCommand() *cobra.Command {
clusterAdvInterfaces: advertise.DefaultInterfaces,
clusterMaxJoinPeers: 5,
clusterRejoinInterval: 60 * time.Second,
disableSupportBundle: false,
}

cmd := &cobra.Command{
Expand Down Expand Up @@ -100,7 +102,7 @@ depending on the nature of the reload error.
SilenceUsage: true,

RunE: func(cmd *cobra.Command, args []string) error {
return r.Run(args[0])
return r.Run(cmd, args[0])
},
}

Expand All @@ -111,6 +113,8 @@ depending on the nature of the reload error.
cmd.Flags().StringVar(&r.uiPrefix, "server.http.ui-path-prefix", r.uiPrefix, "Prefix to serve the HTTP UI at")
cmd.Flags().
BoolVar(&r.enablePprof, "server.http.enable-pprof", r.enablePprof, "Enable /debug/pprof profiling endpoints.")
cmd.Flags().
BoolVar(&r.disableSupportBundle, "server.http.disable-support-bundle", r.disableSupportBundle, "Disable /-/support support bundle retrieval.")

// Cluster flags
cmd.Flags().
Expand Down Expand Up @@ -184,9 +188,10 @@ type alloyRun struct {
configBypassConversionErrors bool
configExtraArgs string
enableCommunityComps bool
disableSupportBundle bool
}

func (fr *alloyRun) Run(configPath string) error {
func (fr *alloyRun) Run(cmd *cobra.Command, configPath string) error {
var wg sync.WaitGroup
defer wg.Wait()

Expand Down Expand Up @@ -275,17 +280,28 @@ func (fr *alloyRun) Run(configPath string) error {
return err
}

var runtimeFlags []byte
thampiotr marked this conversation as resolved.
Show resolved Hide resolved
if !fr.disableSupportBundle {
b := strings.Builder{}
cmd.Flags().VisitAll(func(f *pflag.Flag) {
b.WriteString(fmt.Sprintf("%s=%s\n", f.Name, f.Value.String()))
})
runtimeFlags = []byte(b.String())
}

httpService := httpservice.New(httpservice.Options{
Logger: log.With(l, "service", "http"),
Logger: l,
Tracer: t,
Gatherer: prometheus.DefaultGatherer,

ReadyFunc: func() bool { return ready() },
ReloadFunc: func() (*alloy_runtime.Source, error) { return reload() },

HTTPListenAddr: fr.httpListenAddr,
MemoryListenAddr: fr.inMemoryAddr,
EnablePProf: fr.enablePprof,
HTTPListenAddr: fr.httpListenAddr,
MemoryListenAddr: fr.inMemoryAddr,
EnablePProf: fr.enablePprof,
DisableSupportBundle: fr.disableSupportBundle,
RuntimeFlags: runtimeFlags,
thampiotr marked this conversation as resolved.
Show resolved Hide resolved
})

remoteCfgService, err := remotecfgservice.New(remotecfgservice.Options{
Expand Down
59 changes: 49 additions & 10 deletions internal/runtime/logging/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ func New(w io.Writer, o Options) (*Logger, error) {
return l, nil
}

// NewNop returns a logger that does nothing
func NewNop() *Logger {
l, _ := NewDeferred(io.Discard)
return l
}

// NewDeferred creates a new logger with the default log level and format.
// The logger is not updated during initialization.
func NewDeferred(w io.Writer) (*Logger, error) {
Expand All @@ -63,7 +69,6 @@ func NewDeferred(w io.Writer) (*Logger, error) {
format formatVar
writer writerVar
)

l := &Logger{
inner: w,

Expand Down Expand Up @@ -104,11 +109,10 @@ func (l *Logger) Update(o Options) error {
l.level.Set(slogLevel(o.Level).Level())
l.format.Set(o.Format)

newWriter := l.inner
l.writer.innerWriter = l.inner
if len(o.WriteTo) > 0 {
newWriter = io.MultiWriter(l.inner, &lokiWriter{o.WriteTo})
l.writer.lokiWriter = &lokiWriter{o.WriteTo}
}
l.writer.Set(newWriter)

// Build all our deferred handlers
if l.deferredSlog != nil {
Expand All @@ -133,6 +137,14 @@ func (l *Logger) Update(o Options) error {
return nil
}

func (l *Logger) SetTemporaryWriter(w io.Writer) {
l.writer.SetTemporaryWriter(w)
}

func (l *Logger) RemoveTemporaryWriter() {
l.writer.RemoveTemporaryWriter()
}

// Log implements log.Logger.
func (l *Logger) Log(kvps ...interface{}) error {
// Buffer logs before confirming log format is configured in `logging` block
Expand Down Expand Up @@ -215,24 +227,51 @@ func (f *formatVar) Set(format Format) {

type writerVar struct {
mut sync.RWMutex
w io.Writer

lokiWriter *lokiWriter
innerWriter io.Writer
tmpWriter io.Writer
}

func (w *writerVar) Set(inner io.Writer) {
func (w *writerVar) SetTemporaryWriter(writer io.Writer) {
w.mut.Lock()
defer w.mut.Unlock()
w.w = inner
w.tmpWriter = writer
}

func (w *writerVar) Write(p []byte) (n int, err error) {
func (w *writerVar) RemoveTemporaryWriter() {
w.mut.Lock()
defer w.mut.Unlock()
w.tmpWriter = nil
}

func (w *writerVar) Write(p []byte) (int, error) {
w.mut.RLock()
defer w.mut.RUnlock()

if w.w == nil {
if w.innerWriter == nil {
return 0, fmt.Errorf("no writer available")
}

return w.w.Write(p)
// The following is effectively an io.Multiwriter, but without updating
// the Multiwriter each time tmpWriter is added or removed.
if _, err := w.innerWriter.Write(p); err != nil {
return 0, err
thampiotr marked this conversation as resolved.
Show resolved Hide resolved
}

if w.lokiWriter != nil {
if _, err := w.lokiWriter.Write(p); err != nil {
return 0, err
}
}

if w.tmpWriter != nil {
if _, err := w.tmpWriter.Write(p); err != nil {
return 0, err
}
}

return len(p), nil
}

type bufferedItem struct {
Expand Down
Loading