Skip to content

Commit a7e347f

Browse files
authored
feat(server): Enable Cloud Trace [VIZ-2305] (#1856)
1 parent 1f11570 commit a7e347f

File tree

6 files changed

+73
-15
lines changed

6 files changed

+73
-15
lines changed

server/.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ REEARTH_VISUALIZER_INTERNALAPI_ACTIVE=true
3333
REEARTH_VISUALIZER_INTERNALAPI_PORT=50052
3434
REEARTH_VISUALIZER_INTERNALAPI_TOKEN=token
3535

36+
# This value is for local use; in Cloud Run, the environment variable "GOOGLE_CLOUD_PROJECT" is referenced.
37+
REEARTH_GCP_PROJECT=
38+
39+
# Trace API
40+
REEARTH_TRACER=gcp
41+
REEARTH_TRACER_SAMPLE=1.0
42+
3643
# Auth client
3744
#REEARTH_AUTH_ISS=https://hoge.com
3845
#REEARTH_AUTH_AUD=https://api.reearth.example.com

server/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ require (
4242
github.com/vektah/gqlparser/v2 v2.5.27
4343
github.com/zitadel/oidc v1.13.5
4444
go.mongodb.org/mongo-driver v1.17.3
45+
go.opentelemetry.io/contrib/detectors/gcp v1.34.0
4546
go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.59.0
4647
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.59.0
48+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0
4749
go.opentelemetry.io/otel v1.35.0
4850
go.opentelemetry.io/otel/sdk v1.35.0
4951
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b
@@ -168,8 +170,6 @@ require (
168170
github.com/zitadel/logging v0.6.1 // indirect
169171
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
170172
go.opentelemetry.io/contrib v1.35.0 // indirect
171-
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
172-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
173173
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
174174
go.opentelemetry.io/otel/metric v1.35.0 // indirect
175175
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect

server/internal/adapter/gql/resolver_user.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package gql
22

33
import (
44
"context"
5+
"errors"
6+
"fmt"
57

68
"github.com/reearth/reearth/server/internal/adapter/gql/gqlmodel"
9+
"github.com/reearth/reearth/server/pkg/visualizer"
710
)
811

912
func (r *Resolver) Me() MeResolver {
@@ -13,6 +16,10 @@ func (r *Resolver) Me() MeResolver {
1316
type meResolver struct{ *Resolver }
1417

1518
func (r *meResolver) MyWorkspace(ctx context.Context, obj *gqlmodel.Me) (*gqlmodel.Workspace, error) {
19+
if obj.MyWorkspaceID == "" {
20+
errMsg := fmt.Sprintf("MyWorkspaceID not found Me.ID: %s", obj.ID)
21+
return nil, visualizer.ErrorWithCallerLogging(ctx, errMsg, errors.New(errMsg))
22+
}
1623
return dataloaders(ctx).Workspace.Load(obj.MyWorkspaceID)
1724
}
1825

server/internal/app/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type Config struct {
3737
GCPProject string `envconfig:"GOOGLE_CLOUD_PROJECT" pp:",omitempty"`
3838
Profiler string `pp:",omitempty"`
3939
Tracer string `pp:",omitempty"`
40-
TracerSample float64 `pp:",omitempty"`
40+
TracerSample float64 `default:"0.01" pp:",omitempty"`
4141
Marketplace MarketplaceConfig `pp:",omitempty"`
4242
AssetBaseURL string `default:"http://localhost:8080/assets"`
4343
Origins []string `pp:",omitempty"`

server/internal/app/grpc.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/reearth/reearthx/account/accountdomain"
1515
"github.com/reearth/reearthx/log"
1616
"github.com/reearth/reearthx/rerror"
17+
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
1718
"google.golang.org/grpc"
1819
"google.golang.org/grpc/metadata"
1920
)
@@ -29,7 +30,10 @@ func initGrpc(cfg *ServerConfig) *grpc.Server {
2930
unaryAttachOperatorInterceptor(cfg),
3031
unaryAttachUsecaseInterceptor(cfg),
3132
)
32-
s := grpc.NewServer(ui)
33+
s := grpc.NewServer(
34+
ui,
35+
grpc.StatsHandler(otelgrpc.NewServerHandler()),
36+
)
3337
pb.RegisterReEarthVisualizerServer(s, internalapi.NewServer())
3438

3539
return s

server/internal/app/tracer.go

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,73 @@ import (
1111
jaegercfg "github.com/uber/jaeger-client-go/config"
1212
jaegerlog "github.com/uber/jaeger-client-go/log"
1313
"github.com/uber/jaeger-lib/metrics"
14+
"go.opentelemetry.io/contrib/detectors/gcp"
1415
"go.opentelemetry.io/otel"
16+
"go.opentelemetry.io/otel/sdk/resource"
1517
sdktrace "go.opentelemetry.io/otel/sdk/trace"
18+
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
1619
)
1720

21+
type gcpTracerCloser struct {
22+
tp *sdktrace.TracerProvider
23+
}
24+
25+
func (c *gcpTracerCloser) Close() error {
26+
if c.tp != nil {
27+
ctx := context.Background()
28+
return c.tp.Shutdown(ctx)
29+
}
30+
return nil
31+
}
32+
1833
func initTracer(ctx context.Context, conf *config.Config) io.Closer {
19-
if conf.Tracer == "gcp" {
20-
initGCPTracer(ctx, conf)
21-
} else if conf.Tracer == "jaeger" {
34+
switch conf.Tracer {
35+
case "gcp":
36+
return initGCPTracer(ctx, conf)
37+
case "jaeger":
2238
return initJaegerTracer(conf)
39+
default:
40+
return nil
2341
}
24-
return nil
2542
}
2643

27-
func initGCPTracer(ctx context.Context, conf *config.Config) {
44+
func initGCPTracer(ctx context.Context, conf *config.Config) io.Closer {
45+
// Create GCP trace exporter
2846
exporter, err := texporter.New(texporter.WithProjectID(conf.GCPProject))
2947
if err != nil {
30-
log.Fatalf("failed to init GCP tracer: %v", err)
48+
log.Fatalf("failed to create GCP trace exporter: %v", err)
3149
}
3250

33-
tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter), sdktrace.WithSampler(sdktrace.TraceIDRatioBased(conf.TracerSample)))
34-
defer func() {
35-
_ = tp.ForceFlush(ctx)
36-
}()
51+
// Detect GCP resources (project, instance, etc.)
52+
res, err := resource.New(ctx,
53+
resource.WithDetectors(gcp.NewDetector()),
54+
resource.WithTelemetrySDK(),
55+
resource.WithAttributes(
56+
semconv.ServiceNameKey.String("reearth-visualizer"),
57+
),
58+
)
59+
if err != nil {
60+
log.Warnf("failed to detect GCP resources: %v, using default resource", err)
61+
res, _ = resource.New(ctx,
62+
resource.WithTelemetrySDK(),
63+
resource.WithAttributes(
64+
semconv.ServiceNameKey.String("reearth-visualizer"),
65+
),
66+
)
67+
}
68+
69+
// Create tracer provider with batch span processor for better performance
70+
tp := sdktrace.NewTracerProvider(
71+
sdktrace.WithBatcher(exporter),
72+
sdktrace.WithResource(res),
73+
sdktrace.WithSampler(sdktrace.TraceIDRatioBased(conf.TracerSample)),
74+
)
3775

3876
otel.SetTracerProvider(tp)
3977

40-
log.Infof("tracer: initialized cloud trace with sample fraction: %g", conf.TracerSample)
78+
log.Infof("tracer: initialized GCP Cloud Trace (project=%s, sample=%g)", conf.GCPProject, conf.TracerSample)
79+
80+
return &gcpTracerCloser{tp: tp}
4181
}
4282

4383
func initJaegerTracer(conf *config.Config) io.Closer {

0 commit comments

Comments
 (0)