package trace

  1. Overview
  2. Docs
A lightweight stub for tracing/observability, agnostic in how data is collected

Install

dune-project
 Dependency

Authors

Maintainers

Sources

trace-0.11.tbz
sha256=a29329fcfb191a98bfed26c05c300ed9e1e915b73cc59f51e9d9cdc4d1f158bc
sha512=ff77a4ef19375f4ad3b1ddff7336657a4a5695924b679ac2c36a77b14b95c63d126539efd1590f83b415a1288bb843a5fb4308e53a807dcc22456cb40a8e0026

doc/src/trace.debug/track_spans.ml.html

Source file track_spans.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
module A = Trace_core.Internal_.Atomic_
open Trace_core

let ( let@ ) = ( @@ )

type span += Span_tracked of (* id *) int * span

type unclosed_spans = {
  num: int;
  by_name: (string * int) list;
}

type out =
  [ `Out of out_channel
  | `Call of unclosed_spans -> unit
  ]

open struct
  module Tbl = Hashtbl.Make (struct
    type t = int

    let equal = Stdlib.( = )
    let hash = Hashtbl.hash
  end)

  type 'state st = {
    mutex: Mutex.t;
    tbl_open_spans: string Tbl.t;
    gen_id: int A.t;
    state: 'state;
    cbs: 'state Collector.Callbacks.t;  (** underlying callbacks *)
    out: out;
  }

  let create_st ~state ~cbs ~out () : _ st =
    {
      mutex = Mutex.create ();
      tbl_open_spans = Tbl.create 32;
      gen_id = A.make 0;
      state;
      cbs;
      out;
    }

  let with_mutex mut f =
    Mutex.lock mut;
    Fun.protect f ~finally:(fun () -> Mutex.unlock mut)

  let enter_span (self : _ st) ~__FUNCTION__ ~__FILE__ ~__LINE__ ~level ~params
      ~data ~parent name : span =
    let span =
      self.cbs.enter_span self.state ~__FUNCTION__ ~__FILE__ ~__LINE__ ~level
        ~params ~data ~parent name
    in
    let id = A.fetch_and_add self.gen_id 1 in
    (let@ () = with_mutex self.mutex in
     Tbl.add self.tbl_open_spans id name);
    Span_tracked (id, span)

  let exit_span (self : _ st) span =
    match span with
    | Span_tracked (id, span) ->
      (let@ () = with_mutex self.mutex in
       Tbl.remove self.tbl_open_spans id);
      self.cbs.exit_span self.state span
    | _ -> self.cbs.exit_span self.state span

  let add_data_to_span (self : _ st) span data =
    match span with
    | Span_tracked (_, span) -> self.cbs.add_data_to_span self.state span data
    | _ -> self.cbs.add_data_to_span self.state span data

  let emit (self : _ st) (us : unclosed_spans) =
    assert (us.by_name <> []);
    match self.out with
    | `Call f -> f us
    | `Out out ->
      Printf.fprintf out "trace: warning: %d spans were not closed\n" us.num;
      List.iter
        (fun (name, n) ->
          Printf.fprintf out "  span %S was not closed (%d occurrences)\n" name
            n)
        us.by_name;
      flush out

  let print_non_closed_spans_warning (self : _ st) =
    let module Str_map = Map.Make (String) in
    let@ () = with_mutex self.mutex in

    let num = Tbl.length self.tbl_open_spans in
    if num > 0 then (
      let names_with_count =
        Tbl.fold
          (fun _id name m ->
            Str_map.add name
              (1 + try Str_map.find name m with Not_found -> 0)
              m)
          self.tbl_open_spans Str_map.empty
      in
      let unclosed_spans =
        {
          num;
          by_name =
            Str_map.fold (fun name id l -> (name, id) :: l) names_with_count []
            |> List.sort Stdlib.compare;
        }
      in
      emit self unclosed_spans
    )

  let message self ~level ~params ~data ~span msg =
    let span =
      match span with
      | Some (Span_tracked (_, sp)) -> Some sp
      | _ -> span
    in
    self.cbs.message self.state ~level ~params ~data ~span msg

  let metric self ~level ~params ~data name v =
    self.cbs.metric self.state ~level ~params ~data name v

  let enabled _ _ = true
  let init (self : _ st) = self.cbs.init self.state

  let shutdown (self : _ st) : unit =
    print_non_closed_spans_warning self;
    self.cbs.shutdown self.state

  let extension self ~level ev = self.cbs.extension self.state ~level ev

  let track_callbacks : _ st Collector.Callbacks.t =
    {
      enter_span;
      exit_span;
      add_data_to_span;
      enabled;
      message;
      metric;
      init;
      shutdown;
      extension;
    }
end

let track ?(on_lingering_spans = `Out stderr) (c : Collector.t) : Collector.t =
  match c with
  | C_none -> C_none
  | C_some (st, cbs) ->
    let st = create_st ~state:st ~cbs ~out:on_lingering_spans () in
    C_some (st, track_callbacks)