Skip to content

Commit b124133

Browse files
committed
Improve unit test for cloudwatch logger
1 parent 44f68e5 commit b124133

File tree

2 files changed

+86
-26
lines changed

2 files changed

+86
-26
lines changed

lib/cadet/logger/cloudwatch_logger.ex

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ defmodule Cadet.Logger.CloudWatchLogger do
2020

2121
@max_buffer_size 1000
2222
@max_retries 3
23-
@retry_delay 500
23+
@retry_delay 200
2424
@flush_interval 5000
25+
@failed_message "Failed to send log to CloudWatch."
2526

2627
@impl true
2728
def init({__MODULE__, opts}) when is_list(opts) do
@@ -113,14 +114,19 @@ defmodule Cadet.Logger.CloudWatchLogger do
113114
init(config, state)
114115
end
115116

117+
defp normalize_level(lvl) when lvl in [:warn, :warning], do: :warning
118+
defp normalize_level(lvl), do: lvl
119+
116120
defp meet_level?(_lvl, nil), do: true
117121

118122
defp meet_level?(lvl, min) do
123+
lvl = normalize_level(lvl)
124+
min = normalize_level(min)
119125
Logger.compare_levels(lvl, min) != :lt
120126
end
121127

122128
defp meet_cloudwatch_error?(msg) when is_binary(msg) do
123-
String.starts_with?(msg, "Failed to send log to CloudWatch")
129+
String.contains?(msg, @failed_message)
124130
end
125131

126132
defp meet_cloudwatch_error?(_) do
@@ -182,16 +188,14 @@ defmodule Cadet.Logger.CloudWatchLogger do
182188
cond do
183189
is_nil(id) or id == "" or is_nil(secret) or secret == "" ->
184190
Logger.error(
185-
"Failed to send log to CloudWatch. AWS credentials missing. Ensure AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set.
186-
"
191+
"#{@failed_message} AWS credentials missing. Ensure AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set or configured in ex_aws."
187192
)
188193

189194
:error
190195

191196
region in [nil, ""] ->
192197
Logger.error(
193-
"Failed to send log to CloudWatch. AWS region not configured. Set AWS_REGION or :region under :ex_aws in config.
194-
"
198+
"#{@failed_message} AWS region not configured. Ensure AWS_REGION is set or configured in ex_aws."
195199
)
196200

197201
:error
@@ -209,15 +213,15 @@ defmodule Cadet.Logger.CloudWatchLogger do
209213
:ok
210214

211215
{:error, reason} ->
212-
Logger.error("Failed to send log to CloudWatch: #{inspect(reason)}. Retrying...")
216+
Logger.error("#{@failed_message} #{inspect(reason)}. Retrying...")
213217
# Wait before retrying
214218
:timer.sleep(@retry_delay)
215219
send_with_retry(operation, retries - 1)
216220
end
217221
end
218222

219223
defp send_with_retry(_, 0) do
220-
Logger.error("Failed to send log to CloudWatch after multiple retries.")
224+
Logger.error("#{@failed_message} After multiple retries.")
221225
end
222226

223227
defp init(config, state) do
Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
defmodule Cadet.Logger.CloudWatchLoggerTest do
22
use ExUnit.Case, async: false
3+
34
require Logger
45
import Mox
56
alias Cadet.Logger.CloudWatchLogger
@@ -15,36 +16,27 @@ defmodule Cadet.Logger.CloudWatchLoggerTest do
1516
level: :info,
1617
log_group: "test_log_group",
1718
log_stream: "test_log_stream",
18-
buffer_size: 10,
1919
format: "$time $metadata[$level] $message",
2020
metadata: [:request_id]
2121
)
2222

2323
LoggerBackends.add({CloudWatchLogger, :cloudwatch_logger})
2424

25+
Logger.configure_backend(:console, level: :error)
26+
2527
on_exit(fn ->
2628
LoggerBackends.remove({CloudWatchLogger, :cloudwatch_logger})
29+
Logger.configure_backend(:console, level: :warning)
2730
end)
2831

2932
:ok
3033
end
3134

3235
test "flushes buffered events via ExAws" do
3336
expect(ExAwsMock, :request, fn %ExAws.Operation.JSON{} = op ->
34-
IO.puts("ExAws request: #{inspect(op)}")
35-
%{http_method: http_method, data: data, headers: headers, service: service} = op
36-
37-
assert http_method == :post
38-
assert service == :logs
37+
%{data: data} = op
3938

40-
assert headers == [
41-
{"x-amz-target", "Logs_20140328.PutLogEvents"},
42-
{"content-type", "application/x-amz-json-1.1"}
43-
]
44-
45-
assert data["logGroupName"] == "test_log_group"
46-
assert data["logStreamName"] == "test_log_stream"
47-
assert is_list(data["logEvents"])
39+
assert_config(op)
4840

4941
assert Enum.all?(data["logEvents"], fn event ->
5042
is_map(event) and
@@ -55,8 +47,11 @@ defmodule Cadet.Logger.CloudWatchLoggerTest do
5547
assert Enum.all?(
5648
data["logEvents"],
5749
fn event ->
58-
String.contains?(event["message"], "[error] this is an error") or
59-
String.contains?(event["message"], "[warn] this is a warning")
50+
is_map(event) and
51+
Map.has_key?(event, "message") and
52+
(String.contains?(event["message"], "[error] this is an error") or
53+
String.contains?(event["message"], "[warning] this is a warning") or
54+
String.contains?(event["message"], "[warn] this is a warning"))
6055
end
6156
)
6257

@@ -67,9 +62,70 @@ defmodule Cadet.Logger.CloudWatchLoggerTest do
6762
end)
6863

6964
Logger.error("this is an error")
70-
Logger.warn("this is a warning")
65+
Logger.warning("this is a warning")
7166

72-
# wait for async flush
67+
# wait for timer to flush the buffer
7368
Process.sleep(5100)
7469
end
70+
71+
test "Force flush the buffer when the buffer size is reached" do
72+
expect(ExAwsMock, :request, fn %ExAws.Operation.JSON{} = op ->
73+
%{data: data} = op
74+
75+
assert_config(op)
76+
77+
assert Enum.all?(data["logEvents"], fn event ->
78+
is_map(event) and
79+
Map.has_key?(event, "timestamp") and
80+
Map.has_key?(event, "message")
81+
end)
82+
83+
assert Enum.all?(
84+
data["logEvents"],
85+
fn event ->
86+
is_map(event) and
87+
Map.has_key?(event, "message") and
88+
(String.contains?(event["message"], "[warning] this is a warning") or
89+
String.contains?(event["message"], "[warn] this is a warning"))
90+
end
91+
)
92+
93+
{:ok,
94+
%{
95+
status_code: 200
96+
}}
97+
end)
98+
99+
for _ <- 1..1000 do
100+
Logger.warning("this is a warning")
101+
end
102+
103+
# don't wait for timer
104+
Process.sleep(100)
105+
end
106+
107+
test "Failed to send log to CloudWatch" do
108+
expect(ExAwsMock, :request, 3, fn %ExAws.Operation.JSON{} = op ->
109+
assert_config(op)
110+
111+
{:error, "Failed to send log to CloudWatch"}
112+
end)
113+
114+
Logger.warning("this is a warning")
115+
116+
Process.sleep(6000)
117+
end
118+
119+
defp assert_config(%{http_method: http_method, data: data, headers: headers, service: service}) do
120+
assert http_method == :post
121+
assert service == :logs
122+
123+
assert headers == [
124+
{"x-amz-target", "Logs_20140328.PutLogEvents"},
125+
{"content-type", "application/x-amz-json-1.1"}
126+
]
127+
128+
assert data["logGroupName"] == "test_log_group"
129+
assert data["logStreamName"] == "test_log_stream"
130+
end
75131
end

0 commit comments

Comments
 (0)