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

Switch from Progrock to OpenTelemetry #6835

Merged
merged 104 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from 101 commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
9aefcf2
progrock -> otel
vito Feb 24, 2024
abde5d4
Merge remote-tracking branch 'upstream/main' into otel-tui
vito Mar 6, 2024
e0b39b0
fix log draining, again, ish
vito Mar 7, 2024
2899340
don't set up logs if not configured
vito Mar 7, 2024
2b8c0fd
respect configured level
vito Mar 7, 2024
b7c921b
clean up shim early tracing remnants
vito Mar 7, 2024
bf34d84
synchronously detach services on main client exit
vito Mar 7, 2024
4036ce0
flush telemetry before closing server clients
vito Mar 7, 2024
b3fe350
switch from errgroup to conc for panic handling
vito Mar 7, 2024
29e82ee
nest 'starting session' beneath 'connect'
vito Mar 7, 2024
0f856e4
send logs out from engine to log exporter too
vito Mar 7, 2024
c3ad64b
bump midterm
vito Mar 7, 2024
e7b370d
switch to server-side telemetry pub/sub
vito Mar 9, 2024
008fae1
Merge remote-tracking branch 'upstream/main' into otel-tui
vito Mar 9, 2024
79008a0
show newer traces first
vito Mar 7, 2024
a62f251
cleanup
vito Mar 7, 2024
0c3e216
send individual Calls over telemetry instead of IDs
vito Mar 9, 2024
641e3a6
idtui Base was correct in returning bool
vito Mar 9, 2024
0f2ea03
handle case where calls haven't been seen yet
vito Mar 9, 2024
91c9192
idtui: add space between progress and primary output
vito Mar 9, 2024
7cd1bf6
swap -vvv and -vv, -vv now breaks encapsulation
vito Mar 9, 2024
2399d94
cleanups
vito Mar 9, 2024
5219fb2
tidy mage
vito Mar 9, 2024
0fbfd9c
tidy
vito Mar 9, 2024
fd6b22e
loosen go.mod constraints
vito Mar 9, 2024
152f767
revive labels tests
vito Mar 9, 2024
322df5f
fix cachemap tests
vito Mar 9, 2024
7070093
nuclear option: wait for all spans to complete
vito Mar 9, 2024
a34d10f
pass-through all gRPC stuff
vito Mar 10, 2024
c812028
dagviz: tweaks to support visualizing a live trace
vito Mar 10, 2024
8882ef5
better 'docker tag' parsing
vito Mar 12, 2024
1a508fb
Merge remote-tracking branch 'upstream/main' into otel-tui
vito Mar 12, 2024
f607ce4
fixup docker tag check
vito Mar 12, 2024
2bab220
pass auth headers to OTLP logs too
vito Mar 12, 2024
a7f4b46
fix stdio not making it out of gateway containers
vito Mar 12, 2024
5e07c62
fix terminal support
vito Mar 13, 2024
8c90f2a
drain immediately when interrupted
vito Mar 13, 2024
ed31975
fix unintentionally HTTP-ifying gRPC otlp enpoint
vito Mar 13, 2024
f31846d
give up retrying connection if outer ctx canceled
vito Mar 13, 2024
c4b563a
initiate draining only when main client goes away
vito Mar 13, 2024
f53f239
appease linter
vito Mar 13, 2024
d610561
remove unnecessary wait
vito Mar 14, 2024
a1c442a
fix panic if no telemetry
vito Mar 14, 2024
cd899de
remove debug log
vito Mar 14, 2024
1078f30
print final progress tree in plain mode
vito Mar 14, 2024
23e3f04
fix Windows build
vito Mar 14, 2024
8e824fc
propagate spans through dagger-in-dagger
vito Mar 15, 2024
cb4c314
retry connecting to telemetry
vito Mar 15, 2024
cd09ab9
propagate span context through dagger run
vito Mar 15, 2024
65ab328
install default labels as otel resource attrs
vito Mar 15, 2024
02e57de
Merge remote-tracking branch 'upstream/main' into otel-tui
vito Mar 15, 2024
2eee30d
tidy
vito Mar 15, 2024
bf366ba
remove pipeline tests
vito Mar 15, 2024
76b3874
fail root span when command fails
vito Mar 16, 2024
2b85aa4
Container.import: add span for streaming image
vito Mar 18, 2024
45f8ad5
idtui: break encapsulation in case of errors
vito Mar 18, 2024
58c50c6
fix schema-level logging not exporting
vito Mar 18, 2024
50baada
update TestDaggerRun assertion
vito Mar 18, 2024
ae0aea5
fix test not syncing on progress completion
vito Mar 18, 2024
c20d65a
add verbose debug log
vito Mar 18, 2024
97efa54
respect $DAGGER_CLOUD_URL and $DAGGER_CLOUD_TOKEN
vito Mar 18, 2024
08cf4d3
port 'processor: support span keepalive'
vito Mar 18, 2024
206de8b
add 'watch' command
vito Mar 19, 2024
c630bf1
set a reasonable window size in plain mode
vito Mar 19, 2024
33b53d1
manually revert container.import change
vito Mar 19, 2024
bf1319f
Merge remote-tracking branch 'upstream/main' into otel-tui
vito Mar 19, 2024
132e74e
fix race
vito Mar 19, 2024
459ed03
mark watch command experimental
vito Mar 19, 2024
808a676
fixup lock, more logging
vito Mar 19, 2024
4d69d1b
tidy
vito Mar 19, 2024
2f40276
fix data race in tests
vito Mar 19, 2024
09f7de7
fix java SDK hang once again
vito Mar 19, 2024
125b4c5
retire dagger.io/ui.primary, use root span instead
vito Mar 19, 2024
47ff78c
take 2: just manually mark the 'primary' span
vito Mar 19, 2024
fb88848
merge tracing and telemetry packages
vito Mar 19, 2024
d0f12bf
cleanups
vito Mar 19, 2024
c564e89
roll back sync detach change
vito Mar 19, 2024
2447156
cleanups
vito Mar 19, 2024
c3c429a
update comment
vito Mar 20, 2024
2705bd4
remove dead code
vito Mar 20, 2024
35af790
default primary span to root span
vito Mar 20, 2024
1ca7e73
remove unused module arg
vito Mar 20, 2024
29c1b67
send engine traces/logs to cloud
vito Mar 20, 2024
8a055cd
implement sub metrics pub/sub
vito Mar 25, 2024
65f07f3
sdk/go runtime: implement otel propagation
vito Mar 25, 2024
93da881
tidy
vito Mar 26, 2024
c59a2c2
add scary comment
vito Mar 27, 2024
ddd17d1
batch events that are sent from the engine
vito Mar 27, 2024
dbc13df
fix spans being deduped within single batch
vito Mar 27, 2024
08aa92c
Add Python support
helderco Mar 28, 2024
c8b06ff
shim: proxy otel to 127.0.0.1:0
vito Mar 28, 2024
5fc4ecb
remove unnecesssary fn
vito Mar 28, 2024
4cc5b56
attributes: add passthrough, bikeshed + document
vito Mar 28, 2024
d95d664
fix janky flag parsing
vito Mar 28, 2024
cbffddc
discard Buildkit progress
vito Mar 29, 2024
31b303b
sdk/go: somewhat gross support for opentelemetry
vito Mar 29, 2024
64a98a9
send logs to function call span, not exec /runtime
vito Mar 29, 2024
b161258
tui: respect dagger.io/ui.mask
vito Mar 29, 2024
a8fec0d
silence linter
vito Mar 29, 2024
dce8443
ignore --help when parsing global flags
vito Apr 2, 2024
b40c411
Pin python requirements
helderco Apr 2, 2024
ff205b5
revert Python SDK changes for now
vito Apr 2, 2024
1216ae8
Merge remote-tracking branch 'upstream/main' into otel-tui
vito Apr 2, 2024
17d17f2
fix race conditions in python SDK runtime
vito Apr 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,3 @@ go.work.sum

# merged from dagger/examples repository
**/node_modules
**/env
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "telemetry/opentelemetry-proto"]
sipsma marked this conversation as resolved.
Show resolved Hide resolved
path = telemetry/opentelemetry-proto
url = https://github.com/open-telemetry/opentelemetry-proto
54 changes: 18 additions & 36 deletions analytics/analytics.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"os"
"sync"
"time"

"github.com/dagger/dagger/core/pipeline"
"github.com/dagger/dagger/engine"
"github.com/vito/progrock"
"github.com/dagger/dagger/telemetry"
)

const (
Expand Down Expand Up @@ -78,30 +78,20 @@ func DoNotTrack() bool {

type Config struct {
DoNotTrack bool
Labels pipeline.Labels
Labels telemetry.Labels
CloudToken string
}

func DefaultConfig() Config {
func DefaultConfig(labels telemetry.Labels) Config {
cfg := Config{
DoNotTrack: DoNotTrack(),
CloudToken: os.Getenv("DAGGER_CLOUD_TOKEN"),
Labels: labels,
}
// Backward compatibility with the old environment variable.
if cfg.CloudToken == "" {
cfg.CloudToken = os.Getenv("_EXPERIMENTAL_DAGGER_CLOUD_TOKEN")
}

workdir, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to get cwd: %v\n", err)
return cfg
}

cfg.Labels.AppendCILabel()
cfg.Labels = append(cfg.Labels, pipeline.LoadVCSLabels(workdir)...)
cfg.Labels = append(cfg.Labels, pipeline.LoadClientLabels(engine.Version)...)

return cfg
}

Expand All @@ -111,8 +101,7 @@ type queuedEvent struct {
}

type CloudTracker struct {
cfg Config
labels map[string]string
cfg Config

closed bool
mu sync.Mutex
Expand All @@ -128,15 +117,10 @@ func New(cfg Config) Tracker {

t := &CloudTracker{
cfg: cfg,
labels: make(map[string]string),
stopCh: make(chan struct{}),
doneCh: make(chan struct{}),
}

for _, l := range cfg.Labels {
t.labels[l.Name] = l.Value
}

go t.start()

return t
Expand All @@ -155,19 +139,19 @@ func (t *CloudTracker) Capture(ctx context.Context, event string, properties map
Type: event,
Properties: properties,

DeviceID: t.labels["dagger.io/client.machine_id"],
DeviceID: t.cfg.Labels["dagger.io/client.machine_id"],

ClientVersion: t.labels["dagger.io/client.version"],
ClientOS: t.labels["dagger.io/client.os"],
ClientArch: t.labels["dagger.io/client.arch"],
ClientVersion: t.cfg.Labels["dagger.io/client.version"],
ClientOS: t.cfg.Labels["dagger.io/client.os"],
ClientArch: t.cfg.Labels["dagger.io/client.arch"],

CI: t.labels["dagger.io/ci"] == "true",
CIVendor: t.labels["dagger.io/ci.vendor"],
CI: t.cfg.Labels["dagger.io/ci"] == "true",
CIVendor: t.cfg.Labels["dagger.io/ci.vendor"],
}
if remote := t.labels["dagger.io/git.remote"]; remote != "" {
if remote := t.cfg.Labels["dagger.io/git.remote"]; remote != "" {
ev.GitRemoteEncoded = fmt.Sprintf("%x", base64.StdEncoding.EncodeToString([]byte(remote)))
}
if author := t.labels["dagger.io/git.author.email"]; author != "" {
if author := t.cfg.Labels["dagger.io/git.author.email"]; author != "" {
ev.GitAuthorHashed = fmt.Sprintf("%x", sha256.Sum256([]byte(author)))
}
if clientMetadata, err := engine.ClientMetadataFromContext(ctx); err == nil {
Expand Down Expand Up @@ -203,34 +187,32 @@ func (t *CloudTracker) send() {
}

// grab the progrock recorder from the last event in the queue
rec := progrock.FromContext(queue[len(queue)-1].ctx)

payload := bytes.NewBuffer([]byte{})
enc := json.NewEncoder(payload)
for _, q := range queue {
err := enc.Encode(q.event)
if err != nil {
rec.Debug("analytics: encode failed", progrock.ErrorLabel(err))
slog.Debug("analytics: encode failed", "error", err)
continue
}
}

req, err := http.NewRequest(http.MethodPost, trackURL, bytes.NewReader(payload.Bytes()))
if err != nil {
rec.Debug("analytics: new request failed", progrock.ErrorLabel(err))
slog.Debug("analytics: new request failed", "error", err)
return
}
if t.cfg.CloudToken != "" {
req.SetBasicAuth(t.cfg.CloudToken, "")
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
rec.Debug("analytics: do request failed", progrock.ErrorLabel(err))
slog.Debug("analytics: do request failed", "error", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
rec.Debug("analytics: unexpected response", progrock.Labelf("status", resp.Status))
slog.Debug("analytics: unexpected response", "status", resp.Status)
}
}

Expand Down
24 changes: 9 additions & 15 deletions cmd/codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"time"

"github.com/vito/progrock"

"dagger.io/dagger"
"github.com/dagger/dagger/cmd/codegen/generator"
Expand All @@ -17,18 +15,14 @@ import (
)

func Generate(ctx context.Context, cfg generator.Config, dag *dagger.Client) (err error) {
var vtxName string
logsW := os.Stdout

if cfg.ModuleName != "" {
vtxName = fmt.Sprintf("generating %s module: %s", cfg.Lang, cfg.ModuleName)
fmt.Fprintf(logsW, "generating %s module: %s\n", cfg.Lang, cfg.ModuleName)
} else {
vtxName = fmt.Sprintf("generating %s SDK client", cfg.Lang)
fmt.Fprintf(logsW, "generating %s SDK client\n", cfg.Lang)
}

ctx, vtx := progrock.Span(ctx, time.Now().String(), vtxName)
defer func() { vtx.Done(err) }()

logsW := vtx.Stdout()

var introspectionSchema *introspection.Schema
if cfg.IntrospectionJSON != "" {
var resp introspection.Response
Expand All @@ -55,12 +49,12 @@ func Generate(ctx context.Context, cfg generator.Config, dag *dagger.Client) (er

for _, cmd := range generated.PostCommands {
cmd.Dir = cfg.OutputDir
cmd.Stdout = vtx.Stdout()
cmd.Stderr = vtx.Stderr()
task := vtx.Task(strings.Join(cmd.Args, " "))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Fprintln(logsW, "running post-command:", strings.Join(cmd.Args, " "))
err := cmd.Run()
task.Done(err)
if err != nil {
fmt.Fprintln(logsW, "post-command failed:", err)
return err
}
}
Expand Down
6 changes: 5 additions & 1 deletion cmd/codegen/generator/go/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ func (g *GoGenerator) Generate(ctx context.Context, schema *introspection.Schema

var overlay fs.FS = mfs
if g.Config.ModuleName != "" {
overlay = layerfs.New(mfs, &MountedFS{FS: dagger.QueryBuilder, Name: "internal"})
overlay = layerfs.New(
mfs,
&MountedFS{FS: dagger.QueryBuilder, Name: "internal"},
&MountedFS{FS: dagger.Telemetry, Name: "internal"},
)
}

genSt := &generator.GeneratedState{
Expand Down
2 changes: 1 addition & 1 deletion cmd/codegen/generator/go/templates/module_interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ func (spec *parsedIfaceType) marshalJSONMethodCode() *Statement {
BlockFunc(func(g *Group) {
g.If(Id("r").Op("==").Nil()).Block(Return(Index().Byte().Parens(Lit(`""`)), Nil()))

g.List(Id("id"), Id("err")).Op(":=").Id("r").Dot("ID").Call(Qual("context", "Background").Call())
g.List(Id("id"), Id("err")).Op(":=").Id("r").Dot("ID").Call(Id("marshalCtx"))
g.If(Id("err").Op("!=").Nil()).Block(Return(Nil(), Id("err")))
g.Return(Id("json").Dot("Marshal").Call(Id("id")))
})
Expand Down
72 changes: 53 additions & 19 deletions cmd/codegen/generator/go/templates/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ from the Engine, calls the relevant function and returns the result. The generat
on the object+function name, with each case doing json deserialization of the input arguments and calling the actual
Go function.
*/
func (funcs goTemplateFuncs) moduleMainSrc() (string, error) {
func (funcs goTemplateFuncs) moduleMainSrc() (string, error) { //nolint: gocyclo
// HACK: the code in this func can be pretty flaky and tricky to debug -
// it's much easier to debug when we actually have stack traces, so we grab
// those on a panic
Expand Down Expand Up @@ -93,6 +93,12 @@ func (funcs goTemplateFuncs) moduleMainSrc() (string, error) {

tps := []types.Type{}
for _, obj := range objs {
// ignore any private definitions, they may be part of the runtime itself
// e.g. marshalCtx
if !obj.Exported() {
continue
}

// check if this is the constructor func, save it for later if so
if ok := ps.checkConstructor(obj); ok {
continue
Expand Down Expand Up @@ -230,58 +236,86 @@ const (
mainSrc = `func main() {
ctx := context.Background()

// Direct slog to the new stderr. This is only for dev time debugging, and
// runtime errors/warnings.
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Level: slog.LevelWarn,
})))

if err := dispatch(ctx); err != nil {
fmt.Println(err.Error())
os.Exit(2)
}
}

func dispatch(ctx context.Context) error {
ctx = telemetry.InitEmbedded(ctx, resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("dagger-go-sdk"),
// TODO version?
))
defer telemetry.Close()

ctx, span := Tracer().Start(ctx, "Go runtime",
trace.WithAttributes(
// In effect, the following two attributes hide the exec /runtime span.
//
// Replace the parent span,
attribute.Bool("dagger.io/ui.mask", true),
// and only show our children.
attribute.Bool("dagger.io/ui.passthrough", true),
))
defer span.End()

// A lot of the "work" actually happens when we're marshalling the return
// value, which entails getting object IDs, which happens in MarshalJSON,
// which has no ctx argument, so we use this lovely global variable.
setMarshalContext(ctx)

fnCall := dag.CurrentFunctionCall()
parentName, err := fnCall.ParentName(ctx)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("get parent name: %w", err)
}
fnName, err := fnCall.Name(ctx)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("get fn name: %w", err)
}
parentJson, err := fnCall.Parent(ctx)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("get fn parent: %w", err)
}
fnArgs, err := fnCall.InputArgs(ctx)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("get fn args: %w", err)
}

inputArgs := map[string][]byte{}
for _, fnArg := range fnArgs {
argName, err := fnArg.Name(ctx)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("get fn arg name: %w", err)
}
argValue, err := fnArg.Value(ctx)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("get fn arg value: %w", err)
}
inputArgs[argName] = []byte(argValue)
}

result, err := invoke(ctx, []byte(parentJson), parentName, fnName, inputArgs)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("invoke: %w", err)
}
resultBytes, err := json.Marshal(result)
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("marshal: %w", err)
}
_, err = fnCall.ReturnValue(ctx, JSON(resultBytes))
if err != nil {
fmt.Println(err.Error())
os.Exit(2)
return fmt.Errorf("store return value: %w", err)
}
return nil
}
`
parentJSONVar = "parentJSON"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
import (
"context"
"log/slog"

"{{.PackageImport}}/internal/dagger"

"go.opentelemetry.io/otel/trace"
)

var dag = dagger.Connect()

func Tracer() trace.Tracer {
return otel.Tracer("dagger.io/sdk.go")
}

// used for local MarshalJSON implementations
var marshalCtx = context.Background()

// called by main()
func setMarshalContext(ctx context.Context) {
marshalCtx = ctx
dagger.SetMarshalContext(ctx)
}

type DaggerObject = dagger.DaggerObject

type ExecError = dagger.ExecError

{{ range .Types }}
{{ $name := .Name | FormatName }}
{{ $name := .Name | FormatName }}

{{ .Description | Comment }}
type {{ $name }} = dagger.{{ $name }}
Expand Down