diff --git a/internal/component/pyroscope/java/java.go b/internal/component/pyroscope/java/java.go index 076bd254d8..487b1a1060 100644 --- a/internal/component/pyroscope/java/java.go +++ b/internal/component/pyroscope/java/java.go @@ -8,8 +8,10 @@ import ( "os" "strconv" "sync" + "time" "github.com/grafana/alloy/internal/component" + "github.com/grafana/alloy/internal/component/discovery" "github.com/grafana/alloy/internal/component/pyroscope" "github.com/grafana/alloy/internal/component/pyroscope/java/asprof" "github.com/grafana/alloy/internal/featuregate" @@ -51,6 +53,25 @@ func init() { }) } +type debugInfo struct { + ProfiledTargets []*debugInfoProfiledTarget `alloy:"profiled_targets,block"` +} + +type debugInfoProfiledTarget struct { + TotalBytes int64 `alloy:"total_bytes,attr,optional"` + TotalSamples int64 `alloy:"total_samples,attr,optional"` + LastProfiled time.Time `alloy:"last_profiled,attr,optional"` + LastError time.Time `alloy:"last_error,attr,optional"` + ErrorMsg string `alloy:"error_msg,attr,optional"` + PID int `alloy:"pid,attr"` + Target discovery.Target `alloy:"target,attr"` +} + +var ( + _ component.DebugComponent = (*javaComponent)(nil) + _ component.Component = (*javaComponent)(nil) +) + type javaComponent struct { opts component.Options args Arguments @@ -69,6 +90,17 @@ func (j *javaComponent) Run(ctx context.Context) error { return nil } +func (j *javaComponent) DebugInfo() interface{} { + j.mutex.Lock() + defer j.mutex.Unlock() + var di debugInfo + di.ProfiledTargets = make([]*debugInfoProfiledTarget, 0, len(j.pid2process)) + for _, proc := range j.pid2process { + di.ProfiledTargets = append(di.ProfiledTargets, proc.debugInfo()) + } + return &di +} + func (j *javaComponent) Update(args component.Arguments) error { newArgs := args.(Arguments) j.forwardTo.UpdateChildren(newArgs.ForwardTo) diff --git a/internal/component/pyroscope/java/loop.go b/internal/component/pyroscope/java/loop.go index 6a25e214bb..b01f0f31f7 100644 --- a/internal/component/pyroscope/java/loop.go +++ b/internal/component/pyroscope/java/loop.go @@ -34,12 +34,17 @@ type profilingLoop struct { pid int target discovery.Target cancel context.CancelFunc - error error dist *asprof.Distribution jfrFile string startTime time.Time profiler *asprof.Profiler sampleRate int + + error error + lastError time.Time + lastPush time.Time + totalBytes int64 + totalSamples int64 } func newProfilingLoop(pid int, target discovery.Target, logger log.Logger, profiler *asprof.Profiler, output *pyroscope.Fanout, cfg ProfilingConfig) *profilingLoop { @@ -144,6 +149,7 @@ func (p *profilingLoop) push(jfrBytes []byte, startTime time.Time, endTime time. return fmt.Errorf("failed to parse jfr: %w", err) } target := p.getTarget() + var totalSamples, totalBytes int64 for _, req := range profiles.Profiles { metric := req.Metric sz := req.Profile.SizeVT() @@ -156,6 +162,9 @@ func (p *profilingLoop) push(jfrBytes []byte, startTime time.Time, endTime time. ls.Set(labelServiceName, inferServiceName(target)) } + totalBytes += int64(sz) + totalSamples += int64(len(req.Profile.Sample)) + profile, err := req.Profile.MarshalVT() if err != nil { _ = l.Log("msg", "failed to marshal profile", "err", err) @@ -168,6 +177,12 @@ func (p *profilingLoop) push(jfrBytes []byte, startTime time.Time, endTime time. continue } _ = l.Log("msg", "pushed jfr-pprof") + + p.mutex.Lock() + p.lastPush = time.Now() + p.totalSamples += totalSamples + p.totalBytes += totalBytes + p.mutex.Unlock() } return nil } @@ -255,9 +270,28 @@ func (p *profilingLoop) onError(err error) bool { p.mutex.Lock() defer p.mutex.Unlock() p.error = err + p.lastError = time.Now() return alive } +func (p *profilingLoop) debugInfo() *debugInfoProfiledTarget { + p.mutex.Lock() + defer p.mutex.Unlock() + d := &debugInfoProfiledTarget{ + TotalBytes: p.totalBytes, + TotalSamples: p.totalSamples, + LastProfiled: p.lastPush, + LastError: p.lastError, + PID: p.pid, + Target: p.target, + } + if p.error != nil { + d.ErrorMsg = p.error.Error() + } + return d + +} + func (p *profilingLoop) interval() time.Duration { return p.getConfig().Interval }