Skip to content

Commit 7cc18d2

Browse files
authored
Merge branch 'master' into copilot/fix-1285
2 parents d769ba2 + 93b07bb commit 7cc18d2

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

lib/cadet/logger/cloudwatch_logger.ex

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ defmodule Cadet.Logger.CloudWatchLogger do
2727
@impl true
2828
def init({__MODULE__, opts}) when is_list(opts) do
2929
config = configure_merge(read_env(), opts)
30-
{:ok, init(config, %__MODULE__{})}
30+
state = init(config, %__MODULE__{})
31+
ensure_log_stream_exists(state.log_group, state.log_stream)
32+
{:ok, state}
3133
end
3234

3335
@impl true
3436
def init({__MODULE__, name}) when is_atom(name) do
3537
config = read_env()
36-
{:ok, init(config, %__MODULE__{})}
38+
state = init(config, %__MODULE__{})
39+
ensure_log_stream_exists(state.log_group, state.log_stream)
40+
{:ok, state}
3741
end
3842

3943
@impl true
@@ -159,6 +163,65 @@ defmodule Cadet.Logger.CloudWatchLogger do
159163
end
160164
end
161165

166+
# Ensures the log stream exists, creates it if not
167+
# Returns :ok or :error
168+
# Uses ExAws.Logs.describe_log_streams and ExAws.Logs.create_log_stream
169+
# Assumes ExAws.Logs is available
170+
171+
defp ensure_log_stream_exists(log_group, log_stream) do
172+
describe_op = %ExAws.Operation.JSON{
173+
http_method: :post,
174+
service: :logs,
175+
headers: [
176+
{"x-amz-target", "Logs_20140328.DescribeLogStreams"},
177+
{"content-type", "application/x-amz-json-1.1"}
178+
],
179+
data: %{
180+
"logGroupName" => log_group,
181+
"logStreamNamePrefix" => log_stream
182+
}
183+
}
184+
185+
client = Application.get_env(:ex_aws, :ex_aws_mock, ExAws)
186+
187+
case client.request(describe_op) do
188+
{:ok, %{"logStreams" => streams}} ->
189+
if Enum.any?(streams, fn s -> s["logStreamName"] == log_stream end) do
190+
:ok
191+
else
192+
create_log_stream(log_group, log_stream, client)
193+
end
194+
195+
{:error, reason} ->
196+
Logger.error("Failed to describe log streams: #{inspect(reason)}")
197+
:error
198+
end
199+
end
200+
201+
defp create_log_stream(log_group, log_stream, client) do
202+
create_op = %ExAws.Operation.JSON{
203+
http_method: :post,
204+
service: :logs,
205+
headers: [
206+
{"x-amz-target", "Logs_20140328.CreateLogStream"},
207+
{"content-type", "application/x-amz-json-1.1"}
208+
],
209+
data: %{
210+
"logGroupName" => log_group,
211+
"logStreamName" => log_stream
212+
}
213+
}
214+
215+
case client.request(create_op) do
216+
{:ok, _} ->
217+
:ok
218+
219+
{:error, reason} ->
220+
Logger.error("Failed to create log stream: #{inspect(reason)}")
221+
:error
222+
end
223+
end
224+
162225
defp build_log_operation(log_stream, log_group, buffer) do
163226
# The headers and body structure can be found in the AWS API documentation:
164227
# https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html

test/cadet/logger/cloudwatch_logger_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ defmodule Cadet.Logger.CloudWatchLoggerTest do
2121
metadata: [:request_id]
2222
)
2323

24+
# Mock the DescribeLogStreams request during initialization
25+
expect(ExAwsMock, :request, fn %ExAws.Operation.JSON{} = op
26+
when op.headers == [
27+
{"x-amz-target", "Logs_20140328.DescribeLogStreams"},
28+
{"content-type", "application/x-amz-json-1.1"}
29+
] ->
30+
# Simulate no existing log streams
31+
{:ok, %{"logStreams" => []}}
32+
end)
33+
34+
# Mock the CreateLogStream request during initialization
35+
expect(ExAwsMock, :request, fn %ExAws.Operation.JSON{} = op
36+
when op.headers == [
37+
{"x-amz-target", "Logs_20140328.CreateLogStream"},
38+
{"content-type", "application/x-amz-json-1.1"}
39+
] ->
40+
# Simulate successful log stream creation
41+
{:ok, %{}}
42+
end)
43+
2444
LoggerBackends.add({CloudWatchLogger, :cloudwatch_logger})
2545

2646
Logger.configure_backend(:console, level: :error, format: "$metadata[$level] $message\n")

0 commit comments

Comments
 (0)