From dbedce311223d6fefa2c2f912b44b6139d068154 Mon Sep 17 00:00:00 2001 From: Martin Wiso Date: Mon, 27 Jan 2020 20:33:24 +0100 Subject: [PATCH 1/5] Upgrade deps --- rebar.config | 8 ++++---- rebar.config.script | 6 +++--- rebar.lock | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/rebar.config b/rebar.config index 2a0ef75..555e3c8 100644 --- a/rebar.config +++ b/rebar.config @@ -17,10 +17,10 @@ {minimum_otp_vsn, "19"}. -{deps, [ {lager, "3.6.1"} - , {jiffy, "0.15.1"} - , {iso8601, "1.2.3"} - , {meck, "0.8.9"} +{deps, [ {lager, "3.6.4"} + , {jiffy, "0.15.2"} + , {iso8601, "1.3.1"} + , {meck, "0.8.11"} , {eunit_formatters, "0.5.0"} ]}. diff --git a/rebar.config.script b/rebar.config.script index d685284..b8b4253 100755 --- a/rebar.config.script +++ b/rebar.config.script @@ -6,13 +6,13 @@ case erlang:function_exported(rebar3, main, 1) of %% rebar 2.x or older NewConf = lists:keystore(deps_dir, 1, CONFIG, {deps_dir, "deps"}), Deps = [{lager, ".*", - {git, "https://github.com/erlang-lager/lager.git", {tag, "3.6.1"}}}, + {git, "https://github.com/erlang-lager/lager.git", {tag, "3.6.4"}}}, {jiffy, "0.*", - {git, "https://github.com/davisp/jiffy.git", {tag, "0.15.1"}}}, + {git, "https://github.com/davisp/jiffy.git", {tag, "1.0.1"}}}, {iso8601, ".*", {git, "https://github.com/erlsci/iso8601.git", {tag, "1.2.3"}}}, {meck, ".*", - {git, "https://github.com/eproxus/meck.git", {tag, "0.8.9"}}}, + {git, "https://github.com/eproxus/meck.git", {tag, "0.8.11"}}}, {eunit_formatters, ".*", {git, "https://github.com/seancribbs/eunit_formatters.git", {tag, "v0.5.0"}}} ], diff --git a/rebar.lock b/rebar.lock index bd1defe..04e6d2a 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,16 +1,16 @@ {"1.1.0", [{<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.5.0">>},0}, {<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.9">>},1}, - {<<"iso8601">>,{pkg,<<"iso8601">>,<<"1.2.3">>},0}, - {<<"jiffy">>,{pkg,<<"jiffy">>,<<"0.15.1">>},0}, - {<<"lager">>,{pkg,<<"lager">>,<<"3.6.1">>},0}, - {<<"meck">>,{pkg,<<"meck">>,<<"0.8.9">>},0}]}. + {<<"iso8601">>,{pkg,<<"iso8601">>,<<"1.3.1">>},0}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"0.15.2">>},0}, + {<<"lager">>,{pkg,<<"lager">>,<<"3.6.4">>},0}, + {<<"meck">>,{pkg,<<"meck">>,<<"0.8.11">>},0}]}. [ {pkg_hash,[ {<<"eunit_formatters">>, <<"6A9133943D36A465D804C1C5B6E6839030434B8879C5600D7DDB5B3BAD4CCB59">>}, {<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>}, - {<<"iso8601">>, <<"56D173EC568D42E95908797A9B7D27BCF0B377443277416733EB1A544A4A47C2">>}, - {<<"jiffy">>, <<"BE83B09388DA1A6C7E798207C9D6A1C4D71BB95FCC387D37D35861788F49AB97">>}, - {<<"lager">>, <<"9D29C5FF7F926D25ECD9899990867C9152DCF34EEE65BAC8EC0DFC0D16A26E0C">>}, - {<<"meck">>, <<"64C5C0BD8BCCA3A180B44196265C8ED7594E16BCC845D0698EC6B4E577F48188">>}]} + {<<"iso8601">>, <<"D1CEE73F56D71C35590C6B2DB2074873BF410BABAAB768F6EA566366D8CA4810">>}, + {<<"jiffy">>, <<"DE266C390111FD4EA28B9302F0BC3D7472468F3B8E0ACEABFBEFA26D08CD73B7">>}, + {<<"lager">>, <<"CED6E98070FB4E58EE93174D006D46479C79844DF7FC17FA4FEFC1049A320D88">>}, + {<<"meck">>, <<"2C39E15EC87D847DA6CF69B4A1C4AF3FD850AE2A272E719E0E8751A7FE54771F">>}]} ]. From 6f436a6d834edf4657057a10cad695443947019a Mon Sep 17 00:00:00 2001 From: Martin Wiso Date: Mon, 27 Jan 2020 20:33:44 +0100 Subject: [PATCH 2/5] Adjust Ingest API implementation --- src/lager_humio_backend.app.src | 2 +- src/lager_humio_backend.erl | 12 +++++++++--- test/lager_humio_backend_tests.erl | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/lager_humio_backend.app.src b/src/lager_humio_backend.app.src index cd80e46..6eceabd 100644 --- a/src/lager_humio_backend.app.src +++ b/src/lager_humio_backend.app.src @@ -1,6 +1,6 @@ {application, lager_humio_backend, [{description, "Lager logging backend for Humio.com"}, - {vsn, "1.2.4"}, + {vsn, "1.2.5"}, {registered, []}, {applications, [kernel, diff --git a/src/lager_humio_backend.erl b/src/lager_humio_backend.erl index d3e3171..5f4560a 100644 --- a/src/lager_humio_backend.erl +++ b/src/lager_humio_backend.erl @@ -66,6 +66,7 @@ %% @private init(Options) -> + error_logger:info_msg("HUMIO: init.options=~p", [Options]), case validate_options(Options) of ok -> {ok, get_configuration(Options)}; @@ -161,6 +162,10 @@ call_ingest_api(Request, Retries, Interval, Opts) -> case httpc:request(post, Request, Opts, []) of {ok, {{_, 200, _}, _H, _B}} -> ok; + {ok, {{_, 401, _}, _H, Response}} -> + error_logger:error_msg("HUMIO: ~s!~n", [Response]), + timer:sleep(Interval), + call_ingest_api(Request, Retries - 1, Interval, Opts); _Other -> timer:sleep(Interval), call_ingest_api(Request, Retries - 1, Interval, Opts) @@ -169,8 +174,9 @@ call_ingest_api(Request, Retries, Interval, Opts) -> create_httpc_request(Payload, Host, Token, DS) -> {get_uri(Host, DS), get_headers(Token), "application/json", Payload}. -get_uri(Host, DS) -> - "https://" ++ Host ++ "/api/v1/dataspaces/" ++ DS ++ "/ingest". +get_uri(Host, _DS) -> + %"https://" ++ Host ++ "/api/v1/dataspaces/" ++ DS ++ "/ingest". + "https://" ++ Host ++ "/api/v1/ingest/humio-structured". get_headers(Token) -> [{"Authorization", "Bearer " ++ Token}]. @@ -222,7 +228,7 @@ validate_options([H | _]) -> {error, {bad_config, H}}. get_configuration(Options) -> - #state{ host = get_option(host, Options, "go.humio.com") + #state{ host = get_option(host, Options, "cloud.humio.com") , token = get_option(token, Options, "") , dataspace = get_option(dataspace, Options, "") , source = get_option(source, Options, "unknown") diff --git a/test/lager_humio_backend_tests.erl b/test/lager_humio_backend_tests.erl index 4c775db..55a3931 100755 --- a/test/lager_humio_backend_tests.erl +++ b/test/lager_humio_backend_tests.erl @@ -66,7 +66,7 @@ test_integration() -> ok. assert_request({Url, Headers, ContentType, Payload}) -> - ?assertEqual("https://testhost/api/v1/dataspaces/bar/ingest", Url), + ?assertEqual("https://testhost/api/v1/dataspaces/humio-structured", Url), ?assertEqual([{"Authorization","Bearer foo"}], Headers), ?assertEqual("application/json", ContentType), From b42f855dd8760f25ba5a64f41fd97207a2cc51b9 Mon Sep 17 00:00:00 2001 From: Martin Wiso Date: Sun, 2 Feb 2020 11:53:36 +0100 Subject: [PATCH 3/5] Fix docs markup --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 958ae8b..0f4ed33 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ First you have to sign up with [Humio][1] service to get all required informatio | `dataspace` | Yes | Humio dataspace (from [Settings][2]) | | `source` | Yes | Humio log source for log grouping and filtering | | `level` | Yes | Minimal log level to use (defaults to `debug`) | -| `formatter` | No | The module to use when formatting log messages (defaults to `lager_default_formatter') | +| `formatter` | No | The module to use when formatting log messages (defaults to `lager_default_formatter`) | | `formatter_config` | No | The format configuration string (defaults to `time [ severity ] message`) | | `metadata_filter` | No | A list of excluded metadata keys | | `retry_interval` | No | Intervarl for retry in case endpoint is not available (defaults to 60 seconds) | From 6aee031d169a5ae5e051a8503572740d38ce2751 Mon Sep 17 00:00:00 2001 From: Martin Wiso Date: Sun, 2 Feb 2020 19:28:34 +0100 Subject: [PATCH 4/5] Remove dataspace configuration --- README.md | 10 ++++------ src/lager_humio_backend.app.src | 2 +- src/lager_humio_backend.erl | 16 +++++----------- test/lager_humio_backend_tests.erl | 10 +++------- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 0f4ed33..923d48c 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ First you have to sign up with [Humio][1] service to get all required informatio | ------------------ |:--------:| ---------------------------------------------- | | `host` | Yes | Hostname of the Humio server (e.g. go.humio.com) | | `token` | Yes | Humio Ingestion API token (from [Settings][2]) | -| `dataspace` | Yes | Humio dataspace (from [Settings][2]) | | `source` | Yes | Humio log source for log grouping and filtering | | `level` | Yes | Minimal log level to use (defaults to `debug`) | | `formatter` | No | The module to use when formatting log messages (defaults to `lager_default_formatter`) | @@ -49,11 +48,10 @@ Sample configuration: {lager, [ {handlers, [ {lager_console_backend, debug}, - {lager_humio_backend, [{host, "go.humio.com"}, - {token, "YOUR_INGESTION_API_TOKEN"}, - {dataspace, "YOUR_DATASPACE"}, - {source, "YOUR_APPLICATION"}, - {level, info} + {lager_humio_backend, [{host, "cloud.humio.com"}, + {token, "YOUR_INGESTION_API_TOKEN"}, + {source, "YOUR_APPLICATION"}, + {level, info} ]} ]} } diff --git a/src/lager_humio_backend.app.src b/src/lager_humio_backend.app.src index 6eceabd..ce957a5 100644 --- a/src/lager_humio_backend.app.src +++ b/src/lager_humio_backend.app.src @@ -1,6 +1,6 @@ {application, lager_humio_backend, [{description, "Lager logging backend for Humio.com"}, - {vsn, "1.2.5"}, + {vsn, "1.3.5"}, {registered, []}, {applications, [kernel, diff --git a/src/lager_humio_backend.erl b/src/lager_humio_backend.erl index 5f4560a..40a7ba8 100644 --- a/src/lager_humio_backend.erl +++ b/src/lager_humio_backend.erl @@ -6,7 +6,6 @@ %%%
    %%%
  • `host' - Hostname of the Humio server (e.g. go.humio.com)
  • %%%
  • `token' - Humio Ingestion API token (from Settings)
  • -%%%
  • `dataspace' - Humio dataspace (from Settings)
  • %%%
  • `source' - Humio log source for log grouping and filtering
  • %%%
  • `level' - log level to use
  • %%%
  • `formatter' - the module to use when formatting log messages. @@ -39,7 +38,6 @@ -record(state, { host :: string() , token :: string() - , dataspace :: string() , source :: string() , level :: integer() , formatter :: atom() @@ -55,7 +53,7 @@ %% Exported for testing -ifdef(TEST). -export([ call_ingest_api/4 - , create_httpc_request/4 + , create_httpc_request/3 , get_configuration/1 , validate_options/1 , create_event/4 @@ -66,7 +64,6 @@ %% @private init(Options) -> - error_logger:info_msg("HUMIO: init.options=~p", [Options]), case validate_options(Options) of ok -> {ok, get_configuration(Options)}; @@ -92,8 +89,7 @@ handle_event({log, Message}, #state{level = MinLevel} = State) -> case lager_util:is_loggable(Message, MinLevel, ?MODULE) of true -> Payload = jiffy:encode(create_payload(Message, State)), - Request = create_httpc_request(Payload, State#state.host, - State#state.token, State#state.dataspace), + Request = create_httpc_request(Payload, State#state.host, State#state.token), RetryInterval = State#state.retry_interval, MaxRetries = State#state.max_retries, Opts = State#state.httpc_opts, @@ -171,11 +167,10 @@ call_ingest_api(Request, Retries, Interval, Opts) -> call_ingest_api(Request, Retries - 1, Interval, Opts) end. -create_httpc_request(Payload, Host, Token, DS) -> - {get_uri(Host, DS), get_headers(Token), "application/json", Payload}. +create_httpc_request(Payload, Host, Token) -> + {get_uri(Host), get_headers(Token), "application/json", Payload}. -get_uri(Host, _DS) -> - %"https://" ++ Host ++ "/api/v1/dataspaces/" ++ DS ++ "/ingest". +get_uri(Host) -> "https://" ++ Host ++ "/api/v1/ingest/humio-structured". get_headers(Token) -> @@ -230,7 +225,6 @@ validate_options([H | _]) -> get_configuration(Options) -> #state{ host = get_option(host, Options, "cloud.humio.com") , token = get_option(token, Options, "") - , dataspace = get_option(dataspace, Options, "") , source = get_option(source, Options, "unknown") , level = lager_util:level_to_num( get_option(level, Options, debug)) diff --git a/test/lager_humio_backend_tests.erl b/test/lager_humio_backend_tests.erl index 55a3931..50c5567 100755 --- a/test/lager_humio_backend_tests.erl +++ b/test/lager_humio_backend_tests.erl @@ -34,7 +34,6 @@ test_integration() -> HumioConfig = {lager_humio_backend, [{host, "testhost"}, {token, "foo"}, - {dataspace, "bar"}, {source, "foobar"}, {level, info}] }, @@ -66,7 +65,7 @@ test_integration() -> ok. assert_request({Url, Headers, ContentType, Payload}) -> - ?assertEqual("https://testhost/api/v1/dataspaces/humio-structured", Url), + ?assertEqual("https://testhost/api/v1/ingest/humio-structured", Url), ?assertEqual([{"Authorization","Bearer foo"}], Headers), ?assertEqual("application/json", ContentType), @@ -90,7 +89,7 @@ assert_request({Url, Headers, ContentType, Payload}) -> ok. test_call_ingest_api_retry() -> - Request = lager_humio_backend:create_httpc_request(<<"{}">>, "foo", "bar", "baz"), + Request = lager_humio_backend:create_httpc_request(<<"{}">>, "foo", "bar"), ok = meck:expect( httpc, request, @@ -114,7 +113,6 @@ test_call_ingest_api_retry() -> test_get_configuration() -> Options = [ {host, ""} , {token, ""} - , {dataspace, ""} , {source, ""} , {level, debug} , {formatter, lager_default_formatter} @@ -126,14 +124,13 @@ test_get_configuration() -> ], ?assertEqual( - {state, [], [], [], [], 128, lager_default_formatter, [], [], 60*1000, 10, []}, + {state, [], [], [], 128, lager_default_formatter, [], [], 60*1000, 10, []}, lager_humio_backend:get_configuration(Options) ). test_validate_options() -> ValidOptions = [ {host, "test.com"} , {token, "foo"} - , {dataspace, "bar"} , {level, debug} , {formatter, lager_default_formatter} , {format_config, []} @@ -148,7 +145,6 @@ test_validate_options() -> Invalid = [ {{host, ""}, {error, missing_host}} , {{token, ""}, {error, missing_token}} - , {{dataspace, ""}, {error, missing_dataspace}} , {{source, ""}, {error, missing_source}} , {{level, unknown}, {error, {bad_level, unknown}}} , {{retry_interval, foo}, {error, {bad_config, {retry_interval, foo}}}} From fb8c5197dad0b884d9b1fcad5e5cdfa984b2e623 Mon Sep 17 00:00:00 2001 From: Martin Wiso Date: Sun, 2 Feb 2020 19:38:45 +0100 Subject: [PATCH 5/5] Upgrade `covertool` --- .circleci/config.yml | 6 +++--- bin/covertool | Bin 24679 -> 0 bytes rebar.config | 12 +++++++----- rebar.lock | 4 +++- 4 files changed, 13 insertions(+), 9 deletions(-) delete mode 100755 bin/covertool diff --git a/.circleci/config.yml b/.circleci/config.yml index 9943e9f..6030310 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,15 +26,15 @@ jobs: name: Display webapp coverage report command: rebar3 cover -v - run: - name: Convert coverage report to codecov supported format - command: bin/covertool -cover _build/test/cover/eunit.coverdata -appname lager_humio_backend -output cobertura.xml + name: Generate coverage report to codecov supported format + command: rebar3 covertool generate -p2 - run: name: Upload coverage report to Codecov command: | python3 -m venv venv . venv/bin/activate pip3 install --upgrade codecov - codecov + codecov -f _build/test/covertool/lager_humio_backend.covertool.xml - save_cache: key: build-cache-{{ checksum "rebar.lock" }} paths: diff --git a/bin/covertool b/bin/covertool deleted file mode 100755 index 06eef8b0388f46dbee4748f5d83a489a8b31285c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24679 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 N{o8WYtM{+le*sZ$G1344 diff --git a/rebar.config b/rebar.config index 555e3c8..189f83c 100644 --- a/rebar.config +++ b/rebar.config @@ -22,6 +22,7 @@ , {iso8601, "1.3.1"} , {meck, "0.8.11"} , {eunit_formatters, "0.5.0"} + , {covertool, "2.0.0"} ]}. {overrides, @@ -36,12 +37,13 @@ }] } ]} + + ]}. -{cover_enabled, true}. -{cover_print_enabled, true}. -{eunit_opts, [verbose, - no_tty, - {report, {eunit_progress, [colored]}}]}. +{plugins, [covertool]}. +{cover_export_enabled, true}. +{covertool_eunit, {".eunit/eunit.coverdata", "eunit.coverage.xml"}}. +{covertool_prefix_len, 2}. {xref_checks, [undefined_function_calls]}. diff --git a/rebar.lock b/rebar.lock index 04e6d2a..ee8b62d 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,5 +1,6 @@ {"1.1.0", -[{<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.5.0">>},0}, +[{<<"covertool">>,{pkg,<<"covertool">>,<<"2.0.0">>},0}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.5.0">>},0}, {<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.9">>},1}, {<<"iso8601">>,{pkg,<<"iso8601">>,<<"1.3.1">>},0}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"0.15.2">>},0}, @@ -7,6 +8,7 @@ {<<"meck">>,{pkg,<<"meck">>,<<"0.8.11">>},0}]}. [ {pkg_hash,[ + {<<"covertool">>, <<"2E0172F35D95AA573F113970E077FBA644DD809F450C27E88C173CA3A66FDB42">>}, {<<"eunit_formatters">>, <<"6A9133943D36A465D804C1C5B6E6839030434B8879C5600D7DDB5B3BAD4CCB59">>}, {<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>}, {<<"iso8601">>, <<"D1CEE73F56D71C35590C6B2DB2074873BF410BABAAB768F6EA566366D8CA4810">>},