Skip to content

Commit aeeba55

Browse files
committed
CP-308811: Add an option to limit the span depth in tracing
Adds a new span.depth key to the trace context baggage, and a configurable max_span_depth. This defaults to 100 and so will not limit traces, but is useful when wanting to analyse large traces which can often become slow if all the traces are recorded. Signed-off-by: Steven Woods <[email protected]>
1 parent dd75195 commit aeeba55

File tree

5 files changed

+95
-14
lines changed

5 files changed

+95
-14
lines changed

ocaml/libs/tracing/tracing.ml

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ module TraceContext = struct
222222

223223
let empty = {traceparent= None; baggage= None}
224224

225+
let depth_key = "span.depth"
226+
225227
let with_traceparent traceparent ctx = {ctx with traceparent}
226228

227229
let with_baggage baggage ctx = {ctx with baggage}
@@ -230,6 +232,20 @@ module TraceContext = struct
230232

231233
let baggage_of ctx = ctx.baggage
232234

235+
let baggage_depth_of ctx =
236+
Option.bind (baggage_of ctx) (List.assoc_opt depth_key)
237+
|> Option.value ~default:"1"
238+
|> int_of_string
239+
240+
let update_with_baggage k v ctx =
241+
let new_baggage =
242+
baggage_of ctx
243+
|> Option.value ~default:[]
244+
|> List.remove_assoc k
245+
|> List.cons (k, v)
246+
in
247+
with_baggage (Some new_baggage) ctx
248+
233249
let parse input =
234250
let open Astring.String in
235251
let trim_pair (key, value) = (trim key, trim value) in
@@ -322,22 +338,36 @@ module Span = struct
322338

323339
let start ?(attributes = Attributes.empty)
324340
?(trace_context : TraceContext.t option) ~name ~parent ~span_kind () =
325-
let trace_id, extra_context =
341+
let trace_id, extra_context, depth =
326342
match parent with
327343
| None ->
328-
(Trace_id.make (), TraceContext.empty)
344+
(Trace_id.make (), TraceContext.empty, 1)
329345
| Some span_parent ->
330-
(span_parent.context.trace_id, span_parent.context.trace_context)
346+
( span_parent.context.trace_id
347+
, span_parent.context.trace_context
348+
, TraceContext.baggage_depth_of span_parent.context.trace_context + 1
349+
)
331350
in
332351
let span_id = Span_id.make () in
352+
let extra_context_with_depth =
353+
TraceContext.(
354+
with_added_baggage depth_key (string_of_int depth) extra_context
355+
)
356+
in
333357
let context : SpanContext.t =
334-
{trace_id; span_id; trace_context= extra_context}
358+
{trace_id; span_id; trace_context= extra_context_with_depth}
335359
in
336360
let context =
337-
(* If trace_context is provided to the call, override any inherited trace context. *)
338-
trace_context
339-
|> Option.fold ~none:context
340-
~some:(Fun.flip SpanContext.with_trace_context context)
361+
(* If trace_context is provided to the call, override any inherited trace
362+
context except span.depth which should still be maintained. *)
363+
match trace_context with
364+
| Some tc ->
365+
let tc_with_depth =
366+
TraceContext.(with_added_baggage depth_key (string_of_int depth) tc)
367+
in
368+
SpanContext.with_trace_context tc_with_depth context
369+
| None ->
370+
context
341371
in
342372
(* Using gettimeofday over Mtime as it is better for sharing timestamps between the systems *)
343373
let begin_time = Unix.gettimeofday () in
@@ -473,6 +503,11 @@ module Spans = struct
473503

474504
let set_max_traces x = Atomic.set max_traces x
475505

506+
(* Default is much larger than the largest current traces, so effectively off *)
507+
let max_depth = Atomic.make 100
508+
509+
let set_max_depth x = Atomic.set max_depth x
510+
476511
let finished_spans = Atomic.make ([], 0)
477512

478513
let span_hashtbl_is_empty () = TraceMap.is_empty (Atomic.get spans)
@@ -713,12 +748,18 @@ module Tracer = struct
713748
let get_tracer ~name:_ = TracerProvider.get_current ()
714749

715750
let span_of_span_context context name : Span.t =
751+
let tc = SpanContext.context_of_span_context context in
752+
let new_depth = TraceContext.baggage_depth_of tc in
753+
let new_tc =
754+
TraceContext.(with_added_baggage depth_key (string_of_int new_depth) tc)
755+
in
756+
let context = SpanContext.with_trace_context new_tc context in
716757
{
717758
context
718759
; status= {status_code= Status.Unset; _description= None}
719760
; name
720761
; parent= None
721-
; span_kind= SpanKind.Client (* This will be the span of the client call*)
762+
; span_kind= SpanKind.Client (* This will be the span of the client call *)
722763
; begin_time= Unix.gettimeofday ()
723764
; end_time= None
724765
; links= []
@@ -730,10 +771,23 @@ module Tracer = struct
730771
?(span_kind = SpanKind.Internal) ~name ~parent () :
731772
(Span.t option, exn) result =
732773
let open TracerProvider in
733-
(* Do not start span if the TracerProvider is disabled*)
774+
let parent_depth =
775+
Option.fold ~none:1
776+
~some:(fun parent ->
777+
parent.Span.context
778+
|> SpanContext.context_of_span_context
779+
|> TraceContext.baggage_depth_of
780+
)
781+
parent
782+
in
783+
(* Do not start span if the TracerProvider is disabled *)
734784
if not t.enabled then
785+
ok_none (* Do not start span if the max depth has been reached *)
786+
else if parent_depth >= Atomic.get Spans.max_depth then (
787+
let parent_trace_id = Option.fold ~none:"None" ~some:(fun p -> p.Span.context |> SpanContext.span_id_of_span_context |> Span_id.to_string) parent in
788+
debug "Max_span_depth limit reached, not creating span %s (parent %s)" name parent_trace_id ;
735789
ok_none
736-
else
790+
) else
737791
let attributes = Attributes.merge_into t.attributes attributes in
738792
let span =
739793
Span.start ~attributes ?trace_context ~name ~parent ~span_kind ()
@@ -750,16 +804,24 @@ module Tracer = struct
750804
|> Spans.remove_from_spans
751805
|> Option.map (fun existing_span ->
752806
let old_context = Span.get_context existing_span in
807+
let parent_trace_context = Span.get_trace_context parent in
808+
let new_depth =
809+
TraceContext.baggage_depth_of parent_trace_context + 1
810+
in
753811
let new_context : SpanContext.t =
754-
let trace_context = span.Span.context.trace_context in
812+
let trace_context =
813+
TraceContext.(
814+
with_added_baggage depth_key (string_of_int new_depth)
815+
span.Span.context.trace_context
816+
)
817+
in
755818
SpanContext.context
756819
(SpanContext.trace_id_of_span_context parent.context)
757820
old_context.span_id
758821
|> SpanContext.with_trace_context trace_context
759822
in
760823
let updated_span = {existing_span with parent= Some parent} in
761824
let updated_span = {updated_span with context= new_context} in
762-
763825
let () = Spans.add_to_spans ~span:updated_span in
764826
updated_span
765827
)
@@ -926,7 +988,15 @@ module Propagator = struct
926988
let trace_context' =
927989
TraceContext.with_traceparent (Some traceparent) trace_context
928990
in
929-
let carrier' = P.inject_into trace_context' carrier in
991+
let new_depth =
992+
TraceContext.baggage_depth_of trace_context' + 1 |> string_of_int
993+
in
994+
let trace_context'' =
995+
TraceContext.(
996+
with_added_baggage depth_key new_depth trace_context'
997+
)
998+
in
999+
let carrier' = P.inject_into trace_context'' carrier in
9301000
f carrier'
9311001
| _ ->
9321002
f carrier

ocaml/libs/tracing/tracing.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ module Spans : sig
165165

166166
val set_max_traces : int -> unit
167167

168+
val set_max_depth : int -> unit
169+
168170
val span_count : unit -> int
169171

170172
val since : unit -> Span.t list * int

ocaml/tests/test_observer.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ let verify_json_fields_and_values ~json =
305305
; ("xs.host.uuid", `String _)
306306
; ("xs.host.name", `String _)
307307
; ("service.name", `String _)
308+
; ("span.depth", `String _)
308309
]
309310
)
310311
; ("annotations", `List _)

ocaml/xapi/xapi_globs.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,8 @@ let max_spans = ref 10000
10551055

10561056
let max_traces = ref 10000
10571057

1058+
let max_span_depth = ref 100
1059+
10581060
let use_xmlrpc = ref true
10591061

10601062
let compress_tracing_files = ref true
@@ -1778,6 +1780,11 @@ let other_options =
17781780
, (fun () -> string_of_float !vm_sysprep_wait)
17791781
, "Time in seconds to wait for VM to recognise inserted CD"
17801782
)
1783+
; ( "max-span-depth"
1784+
, Arg.Set_int max_span_depth
1785+
, (fun () -> string_of_int !max_span_depth)
1786+
, "The maximum depth to which spans are recorded in a trace in Tracing"
1787+
)
17811788
]
17821789

17831790
(* The options can be set with the variable xapiflags in /etc/sysconfig/xapi.

ocaml/xapi/xapi_observer.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ let initialise_observer ~__context component =
599599
initialise_observer_component ~__context component
600600

601601
let initialise ~__context =
602+
Tracing.Spans.set_max_depth !Xapi_globs.max_span_depth ;
602603
List.iter (initialise_observer_meta ~__context) (startup_components ()) ;
603604
Db.Observer.get_all ~__context
604605
|> List.iter (fun self ->

0 commit comments

Comments
 (0)