Summary: API and stub implementation for real-time logging capabilities. Low-level implementation requires interaction with FB-specific deployment of Scribe, hence it is stubbed out. Reviewed By: jberdine Differential Revision: D15259559 fbshipit-source-id: 712cb99e1master
parent
e7ad99eed0
commit
03927af1d0
@ -0,0 +1,12 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2019-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
type category = InferEvents
|
||||||
|
|
||||||
|
let log _ _ = ()
|
@ -0,0 +1,12 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2019-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
type category = InferEvents
|
||||||
|
|
||||||
|
val log : category -> string list -> unit
|
@ -0,0 +1,59 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2019-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*)
|
||||||
|
|
||||||
|
(* FB-ONLY *)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
module SMap = Map.Make (String)
|
||||||
|
|
||||||
|
type table = InferEvents
|
||||||
|
|
||||||
|
type sample =
|
||||||
|
{ int_section: int SMap.t (** All integer type fields and their values *)
|
||||||
|
; normal_section: string SMap.t
|
||||||
|
(** All string (normal in Scuba terminology) type fields and their values *) }
|
||||||
|
|
||||||
|
let new_sample ~time =
|
||||||
|
let time = match time with Some time -> time | None -> int_of_float (Unix.time ()) in
|
||||||
|
{ (* time is a single mandatory field in scuba. without it,
|
||||||
|
scuba disregards all samples *)
|
||||||
|
int_section= SMap.singleton "time" time
|
||||||
|
; normal_section= SMap.empty }
|
||||||
|
|
||||||
|
|
||||||
|
let add_int ~name ~value sample =
|
||||||
|
let int_section = SMap.set sample.int_section ~key:name ~data:value in
|
||||||
|
{sample with int_section}
|
||||||
|
|
||||||
|
|
||||||
|
let add_normal ~name ~value sample =
|
||||||
|
let normal_section = SMap.set sample.normal_section ~key:name ~data:value in
|
||||||
|
{sample with normal_section}
|
||||||
|
|
||||||
|
|
||||||
|
let sample_to_json sample =
|
||||||
|
let map_to_assoc value_to_json key_value_map =
|
||||||
|
let pairs = SMap.to_alist key_value_map in
|
||||||
|
let assocs = List.map pairs ~f:(fun (name, data) -> (name, value_to_json data)) in
|
||||||
|
`Assoc assocs
|
||||||
|
in
|
||||||
|
let ints_to_assoc = map_to_assoc (fun data -> `Int data) in
|
||||||
|
let normals_to_assoc = map_to_assoc (fun data -> `String data) in
|
||||||
|
`Assoc
|
||||||
|
[("int", ints_to_assoc sample.int_section); ("normal", normals_to_assoc sample.normal_section)]
|
||||||
|
|
||||||
|
|
||||||
|
let sample_to_json_string sample = sample |> sample_to_json |> Yojson.Basic.to_string
|
||||||
|
|
||||||
|
let table_to_scribe_category = function InferEvents -> Scribe.InferEvents
|
||||||
|
|
||||||
|
let log table samples =
|
||||||
|
let category = table_to_scribe_category table in
|
||||||
|
Scribe.log category (List.map samples ~f:sample_to_json_string)
|
||||||
|
|
||||||
|
|
||||||
|
let log = if Config.scuba_logging then log else fun _ _ -> ()
|
@ -0,0 +1,45 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2019-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
(* FB-ONLY *)
|
||||||
|
|
||||||
|
(**
|
||||||
|
Low-level Scuba logging functionality.
|
||||||
|
Provides functionality to log anything to any scuba table.
|
||||||
|
(Note that Scuba is a schema-free storage, so it won't require any changes).
|
||||||
|
Don't use this module directly for logging to tables with known structure.
|
||||||
|
Use high-level functions that are aware of the table structure.
|
||||||
|
*)
|
||||||
|
|
||||||
|
(** A scuba table *)
|
||||||
|
type table = InferEvents
|
||||||
|
|
||||||
|
(** A sample to be added to Scuba *)
|
||||||
|
type sample
|
||||||
|
|
||||||
|
val new_sample : time:int option -> sample
|
||||||
|
(** Create an empty sample with given creation timestamp. If time is not specified,
|
||||||
|
corresponds to current timestamp. *)
|
||||||
|
|
||||||
|
val add_int : name:string -> value:int -> sample -> sample
|
||||||
|
(**
|
||||||
|
Set a new integer field and its value to the sample.
|
||||||
|
Overwrites if a field with this name was already set.
|
||||||
|
*)
|
||||||
|
|
||||||
|
val add_normal : name:string -> value:string -> sample -> sample
|
||||||
|
(**
|
||||||
|
Set a new string (normal in Scuba terminology) field and its value to the sample.
|
||||||
|
Overwrites if a field with this name was already set.
|
||||||
|
*)
|
||||||
|
|
||||||
|
val log : table -> sample list -> unit
|
||||||
|
(**
|
||||||
|
The main function. Log a collection of samples to the given table.
|
||||||
|
*)
|
@ -0,0 +1,59 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2019-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*)
|
||||||
|
(* FB-ONLY *)
|
||||||
|
|
||||||
|
(*
|
||||||
|
* Utilities to log in "infer_events" key-value scuba table.
|
||||||
|
*)
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
let hostname = Unix.gethostname ()
|
||||||
|
|
||||||
|
let maybe_add_normal ~name ~value sample =
|
||||||
|
match value with None -> sample | Some value -> Scuba.add_normal ~name ~value sample
|
||||||
|
|
||||||
|
|
||||||
|
let set_common_fields sample =
|
||||||
|
let open Scuba in
|
||||||
|
sample
|
||||||
|
|> add_int ~name:"pid" ~value:(ProcessPoolState.get_pid () |> Pid.to_int)
|
||||||
|
|> add_int ~name:"is_main_process" ~value:(Bool.to_int CommandLineOption.is_originator)
|
||||||
|
|> add_normal ~name:"hostname" ~value:hostname
|
||||||
|
|> maybe_add_normal ~name:"job_id" ~value:Config.job_id
|
||||||
|
|> add_normal ~name:"command" ~value:(InferCommand.to_string Config.command)
|
||||||
|
|> add_normal ~name:"infer_commit" ~value:Version.commit
|
||||||
|
|
||||||
|
|
||||||
|
let create_sample ~event_name ~value =
|
||||||
|
Scuba.new_sample ~time:None |> set_common_fields
|
||||||
|
|> Scuba.add_normal ~name:"event" ~value:event_name
|
||||||
|
|> Scuba.add_int ~name:"value" ~value
|
||||||
|
|
||||||
|
|
||||||
|
type event_type = Count | Time
|
||||||
|
|
||||||
|
let string_of_event_type = function Count -> "count" | Time -> "time"
|
||||||
|
|
||||||
|
let log event_type ~event_suffix ~value =
|
||||||
|
let event_name = string_of_event_type event_type ^ "." ^ event_suffix in
|
||||||
|
let sample = create_sample ~event_name ~value in
|
||||||
|
Scuba.log Scuba.InferEvents [sample]
|
||||||
|
|
||||||
|
|
||||||
|
(* If scuba logging is disabled, we would not log anyway, but let's not even try
|
||||||
|
to create samples to save perf *)
|
||||||
|
let log = if Config.scuba_logging then log else fun _ ~event_suffix:_ ~value:_ -> ()
|
||||||
|
|
||||||
|
let log_count ~name ~value = log Count ~event_suffix:name ~value
|
||||||
|
|
||||||
|
let log_time ~name ~duration_ms = log Time ~event_suffix:name ~value:duration_ms
|
||||||
|
|
||||||
|
let execute_with_time_logging name f =
|
||||||
|
let start_time = Mtime_clock.counter () in
|
||||||
|
let ret_val = f () in
|
||||||
|
let duration_ms = Mtime_clock.count start_time |> Mtime.Span.to_ms |> int_of_float in
|
||||||
|
log_time ~name ~duration_ms ; ret_val
|
@ -0,0 +1,32 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2019-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
(* FB-ONLY *)
|
||||||
|
|
||||||
|
(**
|
||||||
|
Functionality for logging into "infer_events" Scuba table.
|
||||||
|
The table is organized in form of key-value pairs.
|
||||||
|
Two most important fields are "event" and "value".
|
||||||
|
Other fields in the table correspond to things common for this particular
|
||||||
|
run of Infer.
|
||||||
|
*)
|
||||||
|
|
||||||
|
val log_count : name:string -> value:int -> unit
|
||||||
|
(** Log anything that can be counted. Events will be prefixed with "count." *)
|
||||||
|
|
||||||
|
val execute_with_time_logging : string -> (unit -> 'a) -> 'a
|
||||||
|
(**
|
||||||
|
A helper to log execution time of a particular function.
|
||||||
|
Use this to measure a performance of a given function.
|
||||||
|
Example:
|
||||||
|
{|
|
||||||
|
let f a b = <some code>
|
||||||
|
let f a b = ScubaEventLogging.execute_with_time_logging "f" (fun () -> f a b)
|
||||||
|
|}
|
||||||
|
*)
|
Loading…
Reference in new issue