diff --git a/tests/bin/emit_logs_cohttp.ml b/tests/bin/emit_logs_cohttp.ml new file mode 100644 index 00000000..c5d694a2 --- /dev/null +++ b/tests/bin/emit_logs_cohttp.ml @@ -0,0 +1,118 @@ +(* Austin Theriault + * + * Copyright (C) Semgrep, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation, with the + * special exception on linking described in file LICENSE. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file + * LICENSE for more details. + *) + +module T = Opentelemetry_lwt +(*****************************************************************************) +(* Prelude *) +(*****************************************************************************) + +let string_tag = Logs.Tag.def "string_attr" Format.pp_print_string + +let int_tag = Logs.Tag.def "int_attr" Format.pp_print_int + +let float_tag = Logs.Tag.def "float_attr" Format.pp_print_float + +let bool_tag = Logs.Tag.def "bool_attr" Format.pp_print_bool + +let string_list_tag = + Logs.Tag.def "string_list_attr" (Format.pp_print_list Format.pp_print_string) + +let varied_tag_set = + Logs.Tag.( + empty + |> add string_tag "string_value" + |> add int_tag 42 |> add float_tag 3.14 |> add bool_tag true + |> add string_list_tag [ "foo"; "bar"; "baz" ]) + +let run () = + let otel_reporter = + Opentelemetry_logs.otel_reporter ~service_name:"emit_logs" + ~attributes:[ "my_reporter_attr", `String "foo" ] + () + in + Logs.set_reporter otel_reporter; + Logs.set_level (Some Logs.Debug); + Logs.debug (fun m -> m "emit_logs: starting"); + Logs.info (fun m -> m "emit_logs: info log"); + Logs.warn (fun m -> m "emit_logs: warn log"); + Logs.err (fun m -> m "emit_logs: error log"); + Logs.app (fun m -> m "emit_logs: app log"); + let%lwt () = + T.Trace.with_ ~kind:T.Span.Span_kind_producer "my_scope" (fun _scope -> + Logs.info (fun m -> + m ~tags:varied_tag_set + "emit_logs: this log is emitted with varied tags from a span"); + Lwt.return_unit) + in + let no_emit_tag = Opentelemetry_logs.emit_telemetry false in + Logs.info (fun m -> + m ~tags:no_emit_tag "emit_logs: this log will not be emitted"); + Logs.info (fun m -> + m ~tags:varied_tag_set + "emit_logs: this log will be emitted with varied tags"); + + let fmt_logger = Logs_fmt.reporter ~dst:Format.err_formatter () in + let combined_logger = + Opentelemetry_logs.attach_otel_reporter ~service_name:"emit_logs_fmt" + ~attributes:[ "my_fmt_attr", `String "bar" ] + fmt_logger + in + Logs.set_reporter combined_logger; + Logs.info (fun m -> + m "emit_logs: this log will be emitted from otel and fmt reporter"); + Logs.set_level None; + (* disable logging *) + Logs.debug (fun m -> + m "emit_logs: this log will not be emitted, logging disabled"); + Lwt.return_unit + +let () = + Sys.catch_break true; + T.Globals.service_name := "t1"; + T.Globals.service_namespace := Some "ocaml-otel.test"; + + let debug = ref false in + let batch_traces = ref 400 in + let batch_metrics = ref 3 in + let batch_logs = ref 400 in + let url = ref None in + let opts = + [ + "--debug", Arg.Bool (( := ) debug), " enable debug output"; + ( "--url", + Arg.String (fun s -> url := Some s), + " set the url for the OTel collector" ); + ] + |> Arg.align + in + + Arg.parse opts (fun _ -> ()) "emit1 [opt]*"; + + let some_if_nzero r = + if !r > 0 then + Some !r + else + None + in + let config = + Opentelemetry_client_cohttp_lwt.Config.make ~debug:!debug ?url:!url + ~batch_traces:(some_if_nzero batch_traces) + ~batch_metrics:(some_if_nzero batch_metrics) + ~batch_logs:(some_if_nzero batch_logs) () + in + Format.printf "@[@ config: %a@]@." Opentelemetry_client_cohttp_lwt.Config.pp + config; + + Opentelemetry_client_cohttp_lwt.with_setup ~config () run |> Lwt_main.run diff --git a/tests/logs/dune b/tests/logs/dune new file mode 100644 index 00000000..5702037c --- /dev/null +++ b/tests/logs/dune @@ -0,0 +1,18 @@ +(env + (_ + ; Make the binaries for the test emitters available on the path for the components defined in this dir. + ; See https://dune.readthedocs.io/en/stable/reference/dune/env.html + (binaries + (../bin/emit_logs_cohttp.exe as emit_logs_cohttp) + ))) + +(tests + (names test_logs_e2e) + (package opentelemetry-logs) + (libraries + opentelemetry + opentelemetry-logs + signal_gatherer + alcotest) + (deps %{bin:emit_logs_cohttp}) +) diff --git a/tests/logs/test_logs_e2e.expected b/tests/logs/test_logs_e2e.expected new file mode 100644 index 00000000..e408710e --- /dev/null +++ b/tests/logs/test_logs_e2e.expected @@ -0,0 +1,355 @@ +{ resource = + Some( + { attributes = + [{ key = "service.namespace"; + value = Some(String_value("ocaml-otel.test")); + }; + { key = "service.name"; value = Some(String_value("emit_logs")); }; + { key = "src"; value = Some(String_value("application")); }; + { key = "my_reporter_attr"; value = Some(String_value("foo")); } + ]; + dropped_attributes_count = 0; + }); + scope_logs = + [{ scope = + Some( + { name = "ocaml-otel"; + version = "%%VERSION_NUM%%"; + attributes = []; + dropped_attributes_count = 0; + }); + log_records = + [{ time_unix_nano = 0; + observed_time_unix_nano = 0; + severity_number = Severity_number_debug; + severity_text = "debug"; + body = Some(String_value("emit_logs: starting")); + attributes = []; + dropped_attributes_count = 0; + flags = 0; + trace_id = ; + span_id = ; + } + ]; + schema_url = ""; + } + ]; + schema_url = ""; +} +{ resource = + Some( + { attributes = + [{ key = "service.namespace"; + value = Some(String_value("ocaml-otel.test")); + }; + { key = "service.name"; value = Some(String_value("emit_logs")); }; + { key = "src"; value = Some(String_value("application")); }; + { key = "my_reporter_attr"; value = Some(String_value("foo")); } + ]; + dropped_attributes_count = 0; + }); + scope_logs = + [{ scope = + Some( + { name = "ocaml-otel"; + version = "%%VERSION_NUM%%"; + attributes = []; + dropped_attributes_count = 0; + }); + log_records = + [{ time_unix_nano = 0; + observed_time_unix_nano = 0; + severity_number = Severity_number_info2; + severity_text = "info"; + body = Some(String_value("emit_logs: info log")); + attributes = []; + dropped_attributes_count = 0; + flags = 0; + trace_id = ; + span_id = ; + } + ]; + schema_url = ""; + } + ]; + schema_url = ""; + } +{ resource = + Some( + { attributes = + [{ key = "service.namespace"; + value = Some(String_value("ocaml-otel.test")); + }; + { key = "service.name"; + value = Some(String_value("emit_logs")); + }; + { key = "src"; value = Some(String_value("application")); }; + { key = "my_reporter_attr"; value = Some(String_value("foo")); } + ]; + dropped_attributes_count = 0; + }); + scope_logs = + [{ scope = + Some( + { name = "ocaml-otel"; + version = "%%VERSION_NUM%%"; + attributes = []; + dropped_attributes_count = 0; + }); + log_records = + [{ time_unix_nano = 0; + observed_time_unix_nano = 0; + severity_number = Severity_number_warn; + severity_text = "warning"; + body = Some(String_value("emit_logs: warn log")); + attributes = []; + dropped_attributes_count = 0; + flags = 0; + trace_id = ; + span_id = ; + } + ]; + schema_url = ""; + } + ]; + schema_url = ""; + } +{ resource = + Some( + { attributes = + [{ key = "service.namespace"; + value = Some(String_value("ocaml-otel.test")); + }; + { key = "service.name"; + value = Some(String_value("emit_logs")); + }; + { key = "src"; value = Some(String_value("application")); }; + { key = "my_reporter_attr"; + value = Some(String_value("foo")); + } + ]; + dropped_attributes_count = 0; + }); + scope_logs = + [{ scope = + Some( + { name = "ocaml-otel"; + version = "%%VERSION_NUM%%"; + attributes = []; + dropped_attributes_count = 0; + }); + log_records = + [{ time_unix_nano = 0; + observed_time_unix_nano = 0; + severity_number = Severity_number_error; + severity_text = "error"; + body = Some(String_value("emit_logs: error log")); + attributes = []; + dropped_attributes_count = 0; + flags = 0; + trace_id = ; + span_id = ; + } + ]; + schema_url = ""; + } + ]; + schema_url = ""; + } +{ resource = + Some( + { attributes = + [{ key = "service.namespace"; + value = Some(String_value("ocaml-otel.test")); + }; + { key = "service.name"; + value = Some(String_value("emit_logs")); + }; + { key = "src"; value = Some(String_value("application")); }; + { key = "my_reporter_attr"; + value = Some(String_value("foo")); + } + ]; + dropped_attributes_count = 0; + }); + scope_logs = + [{ scope = + Some( + { name = "ocaml-otel"; + version = "%%VERSION_NUM%%"; + attributes = []; + dropped_attributes_count = 0; + }); + log_records = + [{ time_unix_nano = 0; + observed_time_unix_nano = 0; + severity_number = Severity_number_info; + severity_text = "app"; + body = Some(String_value("emit_logs: app log")); + attributes = []; + dropped_attributes_count = 0; + flags = 0; + trace_id = ; + span_id = ; + } + ]; + schema_url = ""; + } + ]; + schema_url = ""; + } +{ resource = + Some( + { attributes = + [{ key = "service.namespace"; + value = Some(String_value("ocaml-otel.test")); + }; + { key = "service.name"; + value = Some(String_value("emit_logs")); + }; + { key = "src"; + value = Some(String_value("application")); + }; + { key = "string_list_attr"; + value = Some(String_value("")); + }; + { key = "bool_attr"; value = Some(String_value("")); }; + { key = "float_attr"; value = Some(String_value("")); }; + { key = "int_attr"; value = Some(String_value("")); }; + { key = "string_attr"; value = Some(String_value("")); }; + { key = "my_reporter_attr"; + value = Some(String_value("foo")); + } + ]; + dropped_attributes_count = 0; + }); + scope_logs = + [{ scope = + Some( + { name = "ocaml-otel"; + version = "%%VERSION_NUM%%"; + attributes = []; + dropped_attributes_count = 0; + }); + log_records = + [{ time_unix_nano = 0; + observed_time_unix_nano = 0; + severity_number = Severity_number_info2; + severity_text = "info"; + body = + Some( + String_value( + "emit_logs: this log is emitted with varied tags from a span")); + attributes = []; + dropped_attributes_count = 0; + flags = 0; + trace_id = ; + span_id = ; + } + ]; + schema_url = ""; + } + ]; + schema_url = ""; + } +{ resource = + Some( + { attributes = + [{ key = "service.namespace"; + value = Some(String_value("ocaml-otel.test")); + }; + { key = "service.name"; + value = Some(String_value("emit_logs")); + }; + { key = "src"; + value = Some(String_value("application")); + }; + { key = "string_list_attr"; + value = Some(String_value("")); + }; + { key = "bool_attr"; value = Some(String_value("")); }; + { key = "float_attr"; value = Some(String_value("")); }; + { key = "int_attr"; value = Some(String_value("")); }; + { key = "string_attr"; value = Some(String_value("")); }; + { key = "my_reporter_attr"; + value = Some(String_value("foo")); + } + ]; + dropped_attributes_count = 0; + }); + scope_logs = + [{ scope = + Some( + { name = "ocaml-otel"; + version = "%%VERSION_NUM%%"; + attributes = []; + dropped_attributes_count = 0; + }); + log_records = + [{ time_unix_nano = 0; + observed_time_unix_nano = 0; + severity_number = Severity_number_info2; + severity_text = "info"; + body = + Some( + String_value( + "emit_logs: this log will be emitted with varied tags")); + attributes = []; + dropped_attributes_count = 0; + flags = 0; + trace_id = ; + span_id = ; + } + ]; + schema_url = ""; + } + ]; + schema_url = ""; + } +{ resource = + Some( + { attributes = + [{ key = "service.namespace"; + value = Some(String_value("ocaml-otel.test")); + }; + { key = "service.name"; + value = Some(String_value("emit_logs_fmt")); + }; + { key = "src"; + value = Some(String_value("application")); + }; + { key = "my_fmt_attr"; + value = Some(String_value("bar")); + } + ]; + dropped_attributes_count = 0; + }); + scope_logs = + [{ scope = + Some( + { name = "ocaml-otel"; + version = "%%VERSION_NUM%%"; + attributes = []; + dropped_attributes_count = 0; + }); + log_records = + [{ time_unix_nano = 0; + observed_time_unix_nano = 0; + severity_number = Severity_number_info2; + severity_text = "info"; + body = + Some( + String_value( + "emit_logs: this log will be emitted from otel and fmt reporter")); + attributes = []; + dropped_attributes_count = 0; + flags = 0; + trace_id = ; + span_id = ; + } + ]; + schema_url = ""; + } + ]; + schema_url = ""; + } diff --git a/tests/logs/test_logs_e2e.ml b/tests/logs/test_logs_e2e.ml new file mode 100644 index 00000000..51af2968 --- /dev/null +++ b/tests/logs/test_logs_e2e.ml @@ -0,0 +1,43 @@ +module Client = Opentelemetry_client +module L = Opentelemetry_proto.Logs + +(* NOTE: This port must be different from that used by other integration tests, + to prevent socket binding clashes. *) +let port = 4359 + +let url = Printf.sprintf "http://localhost:%d" port + +let cmd = [ "emit_logs_cohttp"; "--url"; url ] + +let tests (signal_batches : Client.Signal.t list) = + ignore signal_batches; + List.iter + (fun (signal_batch : Client.Signal.t) -> + match signal_batch with + | Logs ls -> + ls (* Mask out the times so tests don't change in between runs *) + |> List.map (fun (l : L.resource_logs) -> + let masked_scope_logs = + List.map + (fun (sl : L.scope_logs) -> + let masked_log_records = + List.map + (fun (lr : L.log_record) -> + { + lr with + time_unix_nano = 0L; + observed_time_unix_nano = 0L; + }) + sl.log_records + in + { sl with log_records = masked_log_records }) + l.scope_logs + in + { l with scope_logs = masked_scope_logs }) + |> List.iter (Format.printf "%a\n" L.pp_resource_logs) + | _ -> ()) + signal_batches + +let () = + let signal_batches = Signal_gatherer.gather_signals ~port cmd in + tests signal_batches