|
| 1 | +(* |
| 2 | + * Copyright (c) Cloud Software Group, Inc |
| 3 | + * |
| 4 | + * This program is free software; you can redistribute it and/or modify |
| 5 | + * it under the terms of the GNU Lesser General Public License as published |
| 6 | + * by the Free Software Foundation; version 2.1 only. with the special |
| 7 | + * exception on linking described in file LICENSE. |
| 8 | + * |
| 9 | + * This program is distributed in the hope that it will be useful, |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | + * GNU Lesser General Public License for more details. |
| 13 | + *) |
| 14 | +open Quicktest_trace |
| 15 | +open Conventions |
| 16 | + |
| 17 | +let id_sent = Atomic.make 1 |
| 18 | + |
| 19 | +let id_received = Atomic.make 1 |
| 20 | + |
| 21 | +let rpc_event ~id kind = |
| 22 | + let id = Atomic.fetch_and_add id 1 in |
| 23 | + Opentelemetry.Event.make |
| 24 | + ~attrs:[(rpc_message_id, `Int id); (rpc_message_type, `String kind)] |
| 25 | + rpc_message |
| 26 | + |
| 27 | +let code_of_response response = |
| 28 | + match response.Rpc.contents with |
| 29 | + | Rpc.Enum (Rpc.String code :: _) -> |
| 30 | + code |
| 31 | + | _ -> |
| 32 | + other |
| 33 | + |
| 34 | +let attrs_of_error response = |
| 35 | + [(error_type, `String (code_of_response response))] |
| 36 | + |
| 37 | +let rec any_value_of_rpc = |
| 38 | + let open Opentelemetry_proto.Common in |
| 39 | + function |
| 40 | + | Rpc.Null -> |
| 41 | + None |
| 42 | + | Rpc.Bool b -> |
| 43 | + Some (Bool_value b) |
| 44 | + | Rpc.Int32 i32 -> |
| 45 | + Some (Int_value (Int64.of_int32 i32)) |
| 46 | + | Rpc.Int i -> |
| 47 | + Some (Int_value i) |
| 48 | + | Rpc.Float f -> |
| 49 | + Some (Double_value f) |
| 50 | + | Rpc.Base64 s -> |
| 51 | + (* don't log the full Base64 entry, the UEFI NVRAM is huge *) |
| 52 | + Some (String_value (Printf.sprintf "base64(len=%d)" (String.length s))) |
| 53 | + | Rpc.DateTime s | Rpc.String s -> |
| 54 | + Some (String_value s) |
| 55 | + | Rpc.Enum lst -> |
| 56 | + let values = lst |> List.filter_map any_value_of_rpc in |
| 57 | + Some (Array_value (make_array_value ~values ())) |
| 58 | + | Rpc.Dict dict -> |
| 59 | + let values = |
| 60 | + dict |
| 61 | + |> List.map (fun (key, v) -> |
| 62 | + make_key_value ~key ~value:(any_value_of_rpc v) () |
| 63 | + ) |
| 64 | + in |
| 65 | + Some (Kvlist_value (make_key_value_list ~values ())) |
| 66 | + |
| 67 | +let log_rpc ?(time_unix_nano = Opentelemetry.Timestamp_ns.now_unix_ns ()) scope |
| 68 | + key rpc = |
| 69 | + let trace_id = Scope.trace_id scope |> Opentelemetry.Trace_id.to_bytes |
| 70 | + and span_id = Scope.span_id scope |> Opentelemetry.Span_id.to_bytes in |
| 71 | + let open Opentelemetry_proto in |
| 72 | + let body = |
| 73 | + Some |
| 74 | + Common.( |
| 75 | + Kvlist_value |
| 76 | + (make_key_value_list |
| 77 | + ~values:[make_key_value ~key ~value:(any_value_of_rpc rpc) ()] |
| 78 | + () |
| 79 | + ) |
| 80 | + ) |
| 81 | + in |
| 82 | + Logs.make_log_record ~time_unix_nano ~observed_time_unix_nano:time_unix_nano |
| 83 | + ~severity_number:Severity_number_trace ~severity_text:"TRACE" ~body |
| 84 | + ~attributes:[] ~dropped_attributes_count:0l ~flags:0l ~trace_id ~span_id () |
| 85 | + |
| 86 | +let wrap ?(log_body = false) rpc call = |
| 87 | + let attrs = |
| 88 | + [(rpc_system_name, `String "xmlrpc"); (rpc_method, `String call.Rpc.name)] |
| 89 | + in |
| 90 | + let () = |
| 91 | + if log_body then |
| 92 | + match Scope.get_ambient_scope () with |
| 93 | + | None -> |
| 94 | + () |
| 95 | + | Some scope -> |
| 96 | + (* log the actual bodies of the RPC, |
| 97 | + this is only for testing purposes, since they may contain secrets *) |
| 98 | + Scope.add_log scope (fun () -> |
| 99 | + log_rpc scope call.Rpc.name (Rpc.Enum call.Rpc.params) |
| 100 | + ) |
| 101 | + in |
| 102 | + Trace.with_ ~kind:Opentelemetry.Span.Span_kind_client ~attrs call.Rpc.name |
| 103 | + @@ fun scope -> |
| 104 | + Scope.add_event scope (fun () -> rpc_event ~id:id_sent sent) ; |
| 105 | + |
| 106 | + let (response : Rpc.response) = rpc call in |
| 107 | + |
| 108 | + Scope.add_event scope (fun () -> rpc_event ~id:id_received received) ; |
| 109 | + if log_body then |
| 110 | + Scope.add_delayed_log scope (fun () -> |
| 111 | + log_rpc scope "response" response.contents |
| 112 | + ) ; |
| 113 | + |
| 114 | + if not response.Rpc.success then begin |
| 115 | + Scope.set_status scope |
| 116 | + @@ Span_status.make ~code:Status_code_error |
| 117 | + ~message:(code_of_response response) ; |
| 118 | + Scope.add_attrs scope (fun () -> attrs_of_error response) |
| 119 | + end ; |
| 120 | + response |
| 121 | + |
| 122 | +let http_headers () = |
| 123 | + match Scope.get_ambient_scope () with |
| 124 | + | None -> |
| 125 | + [] |
| 126 | + | Some scope -> |
| 127 | + let open Opentelemetry.Trace_context.Traceparent in |
| 128 | + let traceparent = |
| 129 | + to_value ~trace_id:(Scope.trace_id scope) |
| 130 | + ~parent_id:(Scope.span_id scope) () |
| 131 | + in |
| 132 | + Scope.add_attrs scope (fun () -> [(name, `String traceparent)]) ; |
| 133 | + [(name, traceparent)] |
0 commit comments