custom-span-for-trace
Overview
This guide outlines how to implement user-defined trace spans within Extend app, enabling then creation of custom spans and attributes that reflect specific application logic, workflows, or business processes. This allows for fine-grained visibility into Extend app's behavior.
By default, Extend app is using OpenTelemetry SDK to facilitates tracing capability.
How to add user defined span
- C#
- Go
- Inside the grpc service implementation class (eg.
Services/MyService.cs
in Extend Service Extension), add a private field for tracer component and initialize it using dependency injection.
...
using OpenTelemetry.Trace;
...
public public class MyService : Service.ServiceBase
{
...
private readonly Tracer _Tracer;
...
public MyService(
ILogger<MyService> logger,
IAccelByteServiceProvider abProvider,
Tracer tracer)
{
_Logger = logger;
_ABProvider = abProvider;
_Tracer = tracer;
}
...
}
- Then inside one of the method implementation (eg.
CreateOrUpdateGuildProgress
in Extend Service Extension), create an active span object. After adding some application logic, do not forget to end the span.
...
public override Task<CreateOrUpdateGuildProgressResponse> CreateOrUpdateGuildProgress(CreateOrUpdateGuildProgressRequest request, ServerCallContext context)
{
...
var mySpan = _Tracer.StartActiveSpan("<span name>");
// <write some application logic here>
mySpan.End();
...
}
...
- Inside the gRPC service implementation (e.g.
pkg/service/myService.go
in Extend Service Extension), add a new field for the tracer component.
type MyServiceServerImpl struct {
pb.UnimplementedServiceServer
tokenRepo repository.TokenRepository
configRepo repository.ConfigRepository
refreshRepo repository.RefreshTokenRepository
storage storage.Storage
+ tracer trace.Tracer
}
func NewMyServiceServer(
tokenRepo repository.TokenRepository,
configRepo repository.ConfigRepository,
refreshRepo repository.RefreshTokenRepository,
storage storage.Storage,
+ tracer trace.Tracer,
) *MyServiceServerImpl {
return &MyServiceServerImpl{
tokenRepo: tokenRepo,
configRepo: configRepo,
refreshRepo: refreshRepo,
storage: storage,
+ tracer: tracer,
}
}
- You'll also need to move the instantiation of the gRPC service implementation further down in your
main.go
file to get access to thetracerProvider
created a bit much after.
func main() {
...
cloudSaveStorage := ...
- // Register Guild Service
- myServiceServer := service.NewMyServiceServer(tokenRepo, configRepo, refreshRepo, cloudSaveStorage)
- pb.RegisterServiceServer(s, myServiceServer)
...
// Set Tracer Provider
tracerProvider, err := common.NewTracerProvider(serviceName)
if err != nil {
logrus.Fatalf("Failed to create tracer provider: %v", err)
return
}
otel.SetTracerProvider(tracerProvider)
defer func(ctx context.Context) {
if err := tracerProvider.Shutdown(ctx); err != nil {
logrus.Fatal(err)
}
}(ctx)
+ // Register Guild Service
+ tracer := tracerProvider.Tracer(fmt.Sprintf("%s/MyServiceServer", serviceName))
+ myServiceServer := service.NewMyServiceServer(tokenRepo, configRepo, refreshRepo, cloudSaveStorage, tracer)
+ pb.RegisterServiceServer(s, myServiceServer)
...
}
- Then inside one of the method implementation (e.g.
CreateOrUpdateGuildProgress
in Extend Service Extension), create a new span using thetracerProvider
.
func (g MyServiceServerImpl) CreateOrUpdateGuildProgress(
ctx context.Context, req *pb.CreateOrUpdateGuildProgressRequest,
) (*pb.CreateOrUpdateGuildProgressResponse, error) {
+ ctx, span := g.tracer.Start(ctx, "<span name>")
+ defer span.End()
...
}
How to add nested span
- C#
- Go
To add a child span under current active span, just start new active span before current active span ended. It is recommended to start a child span inside its own method together with the sub application logic.
private void MySubLogic(Tracer tracer)
{
var childSpan = tracer.StartActiveSpan("<child span name>");
// <write some application logic here>
childSpan.End();
}
// then in parent span, call the method
...
var mySpan = _Tracer.StartActiveSpan("<span name>");
// <write some application logic here>
MySubLogic(_Tracer);
mySpan.End();
...
If separate method is not desired, wrap child span inside an using
block.
...
var mySpan = _Tracer.StartActiveSpan("<span name>");
// <write some application logic here>
using (var childSpan = tracer.StartActiveSpan("<child span name>"))
{
// <write some other application logic here>
childSpan.End();
}
mySpan.End();
...
To add a child span under current span, just start new span before current span ends. It is recommended to start a child span inside its own function together with the sub application logic.
import (
+ "go.opentelemetry.io/otel/trace"
)
func (g MyServiceServerImpl) GetGuildProgress(
ctx context.Context, req *pb.GetGuildProgressRequest,
) (*pb.GetGuildProgressResponse, error) {
+ ctx, span := g.tracer.Start(ctx, "<span name>")
+ defer span.End()
// <some application logic here>
+ g.MySubLogic(ctx)
// <some application logic here>
return ...
}
+ func (g MyServiceServerImpl) MySubLogic(ctx context.Context) {
+ ctx, span := g.tracer.Start(ctx, "<child span name>")
+ defer span.End()
// <some other application logic here>
+ }
If separate method is not desired, create the child span using the new context (ctx
) from the parent span.
func (g MyServiceServerImpl) GetGuildProgress(
ctx context.Context, req *pb.GetGuildProgressRequest,
) (*pb.GetGuildProgressResponse, error) {
+ ctx, span := g.tracer.Start(ctx, "<span name>")
+ defer span.End()
// <some application logic here>
+ _, span1 := g.tracer.Start(ctx, "<child span name>")
// <some other application logic here>
+ span1.End()
// <some application logic here>
return ...
}
How to add span's event
- C#
- Go
An event inside a span can be added by using AddEvent
method in span object. This method must be called before End
method is called.
var mySpan = _Tracer.StartActiveSpan("<span name>");
//add event
mySpan.AddEvent("<event description>");
An event inside a span can be added by using AddEvent
function in the span instance. This function must be called before the End
function is called on the span instance.
ctx, span := g.tracer.Start(ctx, "<span name>")
defer span.End()
span.AddEvent("<event description>")
How to set status for a span
- C#
- Go
A span status can be specified before ending the span itself.
var mySpan = _Tracer.StartActiveSpan("<span name>");
//set span status to error
mySpan.SetStatus(OpenTelemetry.Trace.Status.Error);
import "go.opentelemetry.io/otel/codes"
...
ctx, span := g.tracer.Start(ctx, "<span name>")
defer span.End()
span.SetStatus(codes.Error, "<error description>") // The description is only included in a status when the code is for an error.
Resources
- For more advanced usage of trace in C# please read Dotnet OpenTelemetry Tracing Shim.