Skip to content

Commit e033bdf

Browse files
authored
[monitoring] Add telemetry in router (#1391)
1 parent 4422033 commit e033bdf

14 files changed

Lines changed: 219 additions & 83 deletions

File tree

cmds/core-service/main.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,11 +317,8 @@ func RunHTTPServer(ctx context.Context, ctxCanceler func(), address, locality st
317317
handler = authorizer.TokenMiddleware(handler)
318318

319319
if *enableOpenTelemetry {
320-
httpSpanName := func(operation string, req *http.Request) string {
321-
return fmt.Sprintf("%s %s", req.Method, req.URL.Path)
322-
}
323-
324-
handler = otelhttp.NewHandler(handler, "http", otelhttp.WithSpanNameFormatter(httpSpanName))
320+
// We use the default settings; the APIRouter handler will override the span value accordingly, as it has more information.
321+
handler = otelhttp.NewHandler(handler, "http")
325322
}
326323

327324
httpServer := &http.Server{

interfaces/openapi-to-go-server/example/api/common.gen.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ type Route struct {
6262
Method string
6363
Pattern *regexp.Regexp
6464
Handler Handler
65+
Name string
66+
Path string
6567
}
6668

6769
type PartialRouter interface {

interfaces/openapi-to-go-server/example/api/rid/server.gen.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import (
55
"context"
66
"encoding/json"
77
"example/api"
8+
"fmt"
9+
"go.opentelemetry.io/otel"
10+
"go.opentelemetry.io/otel/trace"
811
"net/http"
912
"regexp"
1013
)
@@ -15,10 +18,24 @@ type APIRouter struct {
1518
Authorizer api.Authorizer
1619
}
1720

21+
var tracer = otel.Tracer("rid.api")
22+
1823
// *rid.APIRouter (type defined above) implements the api.PartialRouter interface
1924
func (s *APIRouter) Handle(w http.ResponseWriter, r *http.Request) bool {
2025
for _, route := range s.Routes {
2126
if route.Method == r.Method && route.Pattern.MatchString(r.URL.Path) {
27+
28+
span := trace.SpanFromContext(r.Context())
29+
30+
if span.IsRecording() {
31+
// Current span is the one from the otelhttp handler
32+
span.SetName(fmt.Sprintf("%s %s", r.Method, route.Path))
33+
}
34+
35+
ctx, span := tracer.Start(r.Context(), route.Name)
36+
defer span.End()
37+
r = r.WithContext(ctx)
38+
2239
route.Handler(route.Pattern, w, r)
2340
return true
2441
}
@@ -522,34 +539,34 @@ func MakeAPIRouter(impl Implementation, auth api.Authorizer) APIRouter {
522539
router := APIRouter{Implementation: impl, Authorizer: auth, Routes: make([]*api.Route, 10)}
523540

524541
pattern := regexp.MustCompile("^/rid/v1/dss/identification_service_areas$")
525-
router.Routes[0] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.SearchIdentificationServiceAreas}
542+
router.Routes[0] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.SearchIdentificationServiceAreas, Name: "rid.SearchIdentificationServiceAreas", Path: "/rid/v1/dss/identification_service_areas"}
526543

527544
pattern = regexp.MustCompile("^/rid/v1/dss/identification_service_areas/(?P<id>[^/]*)$")
528-
router.Routes[1] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetIdentificationServiceArea}
545+
router.Routes[1] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetIdentificationServiceArea, Name: "rid.GetIdentificationServiceArea", Path: "/rid/v1/dss/identification_service_areas/{id}"}
529546

530547
pattern = regexp.MustCompile("^/rid/v1/dss/identification_service_areas/(?P<id>[^/]*)$")
531-
router.Routes[2] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateIdentificationServiceArea}
548+
router.Routes[2] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateIdentificationServiceArea, Name: "rid.CreateIdentificationServiceArea", Path: "/rid/v1/dss/identification_service_areas/{id}"}
532549

533550
pattern = regexp.MustCompile("^/rid/v1/dss/identification_service_areas/(?P<id>[^/]*)/(?P<version>[^/]*)$")
534-
router.Routes[3] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateIdentificationServiceArea}
551+
router.Routes[3] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateIdentificationServiceArea, Name: "rid.UpdateIdentificationServiceArea", Path: "/rid/v1/dss/identification_service_areas/{id}/{version}"}
535552

536553
pattern = regexp.MustCompile("^/rid/v1/dss/identification_service_areas/(?P<id>[^/]*)/(?P<version>[^/]*)$")
537-
router.Routes[4] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteIdentificationServiceArea}
554+
router.Routes[4] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteIdentificationServiceArea, Name: "rid.DeleteIdentificationServiceArea", Path: "/rid/v1/dss/identification_service_areas/{id}/{version}"}
538555

539556
pattern = regexp.MustCompile("^/rid/v1/dss/subscriptions$")
540-
router.Routes[5] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.SearchSubscriptions}
557+
router.Routes[5] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.SearchSubscriptions, Name: "rid.SearchSubscriptions", Path: "/rid/v1/dss/subscriptions"}
541558

542559
pattern = regexp.MustCompile("^/rid/v1/dss/subscriptions/(?P<id>[^/]*)$")
543-
router.Routes[6] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetSubscription}
560+
router.Routes[6] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetSubscription, Name: "rid.GetSubscription", Path: "/rid/v1/dss/subscriptions/{id}"}
544561

545562
pattern = regexp.MustCompile("^/rid/v1/dss/subscriptions/(?P<id>[^/]*)$")
546-
router.Routes[7] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateSubscription}
563+
router.Routes[7] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateSubscription, Name: "rid.CreateSubscription", Path: "/rid/v1/dss/subscriptions/{id}"}
547564

548565
pattern = regexp.MustCompile("^/rid/v1/dss/subscriptions/(?P<id>[^/]*)/(?P<version>[^/]*)$")
549-
router.Routes[8] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateSubscription}
566+
router.Routes[8] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateSubscription, Name: "rid.UpdateSubscription", Path: "/rid/v1/dss/subscriptions/{id}/{version}"}
550567

551568
pattern = regexp.MustCompile("^/rid/v1/dss/subscriptions/(?P<id>[^/]*)/(?P<version>[^/]*)$")
552-
router.Routes[9] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteSubscription}
569+
router.Routes[9] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteSubscription, Name: "rid.DeleteSubscription", Path: "/rid/v1/dss/subscriptions/{id}/{version}"}
553570

554571
return router
555572
}

interfaces/openapi-to-go-server/example/api/scd/server.gen.go

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import (
55
"context"
66
"encoding/json"
77
"example/api"
8+
"fmt"
9+
"go.opentelemetry.io/otel"
10+
"go.opentelemetry.io/otel/trace"
811
"net/http"
912
"regexp"
1013
)
@@ -15,10 +18,24 @@ type APIRouter struct {
1518
Authorizer api.Authorizer
1619
}
1720

21+
var tracer = otel.Tracer("scd.api")
22+
1823
// *scd.APIRouter (type defined above) implements the api.PartialRouter interface
1924
func (s *APIRouter) Handle(w http.ResponseWriter, r *http.Request) bool {
2025
for _, route := range s.Routes {
2126
if route.Method == r.Method && route.Pattern.MatchString(r.URL.Path) {
27+
28+
span := trace.SpanFromContext(r.Context())
29+
30+
if span.IsRecording() {
31+
// Current span is the one from the otelhttp handler
32+
span.SetName(fmt.Sprintf("%s %s", r.Method, route.Path))
33+
}
34+
35+
ctx, span := tracer.Start(r.Context(), route.Name)
36+
defer span.End()
37+
r = r.WithContext(ctx)
38+
2239
route.Handler(route.Pattern, w, r)
2340
return true
2441
}
@@ -953,58 +970,58 @@ func MakeAPIRouter(impl Implementation, auth api.Authorizer) APIRouter {
953970
router := APIRouter{Implementation: impl, Authorizer: auth, Routes: make([]*api.Route, 18)}
954971

955972
pattern := regexp.MustCompile("^/scd/dss/v1/operational_intent_references/query$")
956-
router.Routes[0] = &api.Route{Method: http.MethodPost, Pattern: pattern, Handler: router.QueryOperationalIntentReferences}
973+
router.Routes[0] = &api.Route{Method: http.MethodPost, Pattern: pattern, Handler: router.QueryOperationalIntentReferences, Name: "scd.QueryOperationalIntentReferences", Path: "/scd/dss/v1/operational_intent_references/query"}
957974

958975
pattern = regexp.MustCompile("^/scd/dss/v1/operational_intent_references/(?P<entityid>[^/]*)$")
959-
router.Routes[1] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetOperationalIntentReference}
976+
router.Routes[1] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetOperationalIntentReference, Name: "scd.GetOperationalIntentReference", Path: "/scd/dss/v1/operational_intent_references/{entityid}"}
960977

961978
pattern = regexp.MustCompile("^/scd/dss/v1/operational_intent_references/(?P<entityid>[^/]*)$")
962-
router.Routes[2] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateOperationalIntentReference}
979+
router.Routes[2] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateOperationalIntentReference, Name: "scd.CreateOperationalIntentReference", Path: "/scd/dss/v1/operational_intent_references/{entityid}"}
963980

964981
pattern = regexp.MustCompile("^/scd/dss/v1/operational_intent_references/(?P<entityid>[^/]*)/(?P<ovn>[^/]*)$")
965-
router.Routes[3] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateOperationalIntentReference}
982+
router.Routes[3] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateOperationalIntentReference, Name: "scd.UpdateOperationalIntentReference", Path: "/scd/dss/v1/operational_intent_references/{entityid}/{ovn}"}
966983

967984
pattern = regexp.MustCompile("^/scd/dss/v1/operational_intent_references/(?P<entityid>[^/]*)/(?P<ovn>[^/]*)$")
968-
router.Routes[4] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteOperationalIntentReference}
985+
router.Routes[4] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteOperationalIntentReference, Name: "scd.DeleteOperationalIntentReference", Path: "/scd/dss/v1/operational_intent_references/{entityid}/{ovn}"}
969986

970987
pattern = regexp.MustCompile("^/scd/dss/v1/constraint_references/query$")
971-
router.Routes[5] = &api.Route{Method: http.MethodPost, Pattern: pattern, Handler: router.QueryConstraintReferences}
988+
router.Routes[5] = &api.Route{Method: http.MethodPost, Pattern: pattern, Handler: router.QueryConstraintReferences, Name: "scd.QueryConstraintReferences", Path: "/scd/dss/v1/constraint_references/query"}
972989

973990
pattern = regexp.MustCompile("^/scd/dss/v1/constraint_references/(?P<entityid>[^/]*)$")
974-
router.Routes[6] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetConstraintReference}
991+
router.Routes[6] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetConstraintReference, Name: "scd.GetConstraintReference", Path: "/scd/dss/v1/constraint_references/{entityid}"}
975992

976993
pattern = regexp.MustCompile("^/scd/dss/v1/constraint_references/(?P<entityid>[^/]*)$")
977-
router.Routes[7] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateConstraintReference}
994+
router.Routes[7] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateConstraintReference, Name: "scd.CreateConstraintReference", Path: "/scd/dss/v1/constraint_references/{entityid}"}
978995

979996
pattern = regexp.MustCompile("^/scd/dss/v1/constraint_references/(?P<entityid>[^/]*)/(?P<ovn>[^/]*)$")
980-
router.Routes[8] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateConstraintReference}
997+
router.Routes[8] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateConstraintReference, Name: "scd.UpdateConstraintReference", Path: "/scd/dss/v1/constraint_references/{entityid}/{ovn}"}
981998

982999
pattern = regexp.MustCompile("^/scd/dss/v1/constraint_references/(?P<entityid>[^/]*)/(?P<ovn>[^/]*)$")
983-
router.Routes[9] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteConstraintReference}
1000+
router.Routes[9] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteConstraintReference, Name: "scd.DeleteConstraintReference", Path: "/scd/dss/v1/constraint_references/{entityid}/{ovn}"}
9841001

9851002
pattern = regexp.MustCompile("^/scd/dss/v1/subscriptions/query$")
986-
router.Routes[10] = &api.Route{Method: http.MethodPost, Pattern: pattern, Handler: router.QuerySubscriptions}
1003+
router.Routes[10] = &api.Route{Method: http.MethodPost, Pattern: pattern, Handler: router.QuerySubscriptions, Name: "scd.QuerySubscriptions", Path: "/scd/dss/v1/subscriptions/query"}
9871004

9881005
pattern = regexp.MustCompile("^/scd/dss/v1/subscriptions/(?P<subscriptionid>[^/]*)$")
989-
router.Routes[11] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetSubscription}
1006+
router.Routes[11] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetSubscription, Name: "scd.GetSubscription", Path: "/scd/dss/v1/subscriptions/{subscriptionid}"}
9901007

9911008
pattern = regexp.MustCompile("^/scd/dss/v1/subscriptions/(?P<subscriptionid>[^/]*)$")
992-
router.Routes[12] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateSubscription}
1009+
router.Routes[12] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.CreateSubscription, Name: "scd.CreateSubscription", Path: "/scd/dss/v1/subscriptions/{subscriptionid}"}
9931010

9941011
pattern = regexp.MustCompile("^/scd/dss/v1/subscriptions/(?P<subscriptionid>[^/]*)/(?P<version>[^/]*)$")
995-
router.Routes[13] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateSubscription}
1012+
router.Routes[13] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.UpdateSubscription, Name: "scd.UpdateSubscription", Path: "/scd/dss/v1/subscriptions/{subscriptionid}/{version}"}
9961013

9971014
pattern = regexp.MustCompile("^/scd/dss/v1/subscriptions/(?P<subscriptionid>[^/]*)/(?P<version>[^/]*)$")
998-
router.Routes[14] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteSubscription}
1015+
router.Routes[14] = &api.Route{Method: http.MethodDelete, Pattern: pattern, Handler: router.DeleteSubscription, Name: "scd.DeleteSubscription", Path: "/scd/dss/v1/subscriptions/{subscriptionid}/{version}"}
9991016

10001017
pattern = regexp.MustCompile("^/scd/dss/v1/reports$")
1001-
router.Routes[15] = &api.Route{Method: http.MethodPost, Pattern: pattern, Handler: router.MakeDssReport}
1018+
router.Routes[15] = &api.Route{Method: http.MethodPost, Pattern: pattern, Handler: router.MakeDssReport, Name: "scd.MakeDssReport", Path: "/scd/dss/v1/reports"}
10021019

10031020
pattern = regexp.MustCompile("^/scd/dss/v1/uss_availability/(?P<uss_id>[^/]*)$")
1004-
router.Routes[16] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetUssAvailability}
1021+
router.Routes[16] = &api.Route{Method: http.MethodGet, Pattern: pattern, Handler: router.GetUssAvailability, Name: "scd.GetUssAvailability", Path: "/scd/dss/v1/uss_availability/{uss_id}"}
10051022

10061023
pattern = regexp.MustCompile("^/scd/dss/v1/uss_availability/(?P<uss_id>[^/]*)$")
1007-
router.Routes[17] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.SetUssAvailability}
1024+
router.Routes[17] = &api.Route{Method: http.MethodPut, Pattern: pattern, Handler: router.SetUssAvailability, Name: "scd.SetUssAvailability", Path: "/scd/dss/v1/uss_availability/{uss_id}"}
10081025

10091026
return router
10101027
}

interfaces/openapi-to-go-server/example/run_example.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ cd "${BASEDIR}" || exit
1313
docker image build -t openapi-to-go-server-demo . || exit
1414

1515
echo "Running server..."
16-
docker container run -it -p 8080:8080 openapi-to-go-server-demo
16+
docker container run -it -p 8180:8080 openapi-to-go-server-demo

interfaces/openapi-to-go-server/generate.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,15 @@ def _generate_apis(
103103
routes, new_imports = rendering.routes(api, api_package, ensure_500)
104104
server_template_vars = {
105105
"<PACKAGE>": api.package,
106-
"<IMPORTS>": rendering.imports(list(new_imports) + [api_import]),
106+
"<IMPORTS>": rendering.imports(
107+
list(new_imports)
108+
+ [
109+
api_import,
110+
"fmt",
111+
"go.opentelemetry.io/otel",
112+
"go.opentelemetry.io/otel/trace",
113+
]
114+
),
107115
"<API_PACKAGE>": api_package,
108116
"<ROUTES>": "\n".join(routes),
109117
"<ROUTING>": "\n".join(rendering.routing(api, api_package)),

interfaces/openapi-to-go-server/rendering.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,17 @@ def routing(api: apis.API, api_package: str) -> List[str]:
458458
)
459459
)
460460
lines.append(
461-
"router.Routes[%d] = &%s.Route{Method: %s, Pattern: pattern, Handler: router.%s}"
462-
% (i, api_package, operation.verb_const_name, operation.interface_name)
461+
'router.Routes[%d] = &%s.Route{Method: %s, Pattern: pattern, Handler: router.%s, Name: "%s.%s", Path: "%s%s"}'
462+
% (
463+
i,
464+
api_package,
465+
operation.verb_const_name,
466+
operation.interface_name,
467+
api.package,
468+
operation.interface_name,
469+
prefix,
470+
operation.path,
471+
)
463472
)
464473
lines.append("")
465474
first_assignment = False

interfaces/openapi-to-go-server/templates/common.go.template

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ type Route struct {
5959
Method string
6060
Pattern *regexp.Regexp
6161
Handler Handler
62+
Name string
63+
Path string
6264
}
6365

6466
type PartialRouter interface {

interfaces/openapi-to-go-server/templates/server.go.template

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,24 @@ type APIRouter struct {
88
Authorizer <API_PACKAGE>.Authorizer
99
}
1010

11+
var tracer = otel.Tracer("<PACKAGE>.<API_PACKAGE>")
12+
1113
// *<PACKAGE>.APIRouter (type defined above) implements the <API_PACKAGE>.PartialRouter interface
1214
func (s *APIRouter) Handle(w http.ResponseWriter, r *http.Request) bool {
1315
for _, route := range s.Routes {
1416
if route.Method == r.Method && route.Pattern.MatchString(r.URL.Path) {
17+
18+
// We retrieve the current span from the otelhttp handler to set its name property.
19+
span := trace.SpanFromContext(r.Context())
20+
21+
if span.IsRecording() { // If the span is not recording, the name cannot be changed. This also likely means the otelhttp handler is not present (tracing disabled).
22+
span.SetName(fmt.Sprintf("%s %s", r.Method, route.Path))
23+
}
24+
25+
ctx, span := tracer.Start(r.Context(), route.Name)
26+
defer span.End()
27+
r = r.WithContext(ctx)
28+
1529
route.Handler(route.Pattern, w, r)
1630
return true
1731
}

0 commit comments

Comments
 (0)