mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-12 07:54:49 +08:00
Adds JSON as a format for the log (#3017)
Co-authored-by: José Valim <jose.valim@dashbit.co> Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
parent
624f97912a
commit
4b64b005b9
10 changed files with 85 additions and 6 deletions
|
@ -266,6 +266,9 @@ The following environment variables can be used to configure Livebook on boot:
|
||||||
users (attached to evaluation and request logs). By default includes only
|
users (attached to evaluation and request logs). By default includes only
|
||||||
request_id.
|
request_id.
|
||||||
|
|
||||||
|
* `LIVEBOOK_LOG_FORMAT` - sets the log output format, either "text" (default)
|
||||||
|
for human-readable logs or "json" for structured JSON.
|
||||||
|
|
||||||
* `LIVEBOOK_NODE` - sets the node name for running Livebook in a cluster.
|
* `LIVEBOOK_NODE` - sets the node name for running Livebook in a cluster.
|
||||||
Note that Livebook always runs using long names distribution, so the
|
Note that Livebook always runs using long names distribution, so the
|
||||||
node host name must use a fully qualified domain name (FQDN) or an IP
|
node host name must use a fully qualified domain name (FQDN) or an IP
|
||||||
|
|
|
@ -10,7 +10,7 @@ config :livebook, LivebookWeb.Endpoint,
|
||||||
render_errors: [formats: [html: LivebookWeb.ErrorHTML], layout: false]
|
render_errors: [formats: [html: LivebookWeb.ErrorHTML], layout: false]
|
||||||
|
|
||||||
# Configures Elixir's Logger
|
# Configures Elixir's Logger
|
||||||
config :logger, :console,
|
config :logger, :default_formatter,
|
||||||
format: "$date $time $metadata[$level] $message\n",
|
format: "$date $time $metadata[$level] $message\n",
|
||||||
metadata: [:request_id]
|
metadata: [:request_id]
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ config :livebook, LivebookWeb.Endpoint,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Do not include timestamps in development logs
|
# Do not include timestamps in development logs
|
||||||
config :logger, :console,
|
config :logger, :default_formatter,
|
||||||
format: "$metadata[$level] $message\n",
|
format: "$metadata[$level] $message\n",
|
||||||
metadata: []
|
metadata: []
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,25 @@ config :livebook, LivebookWeb.Endpoint,
|
||||||
http: [port: 4002],
|
http: [port: 4002],
|
||||||
server: false
|
server: false
|
||||||
|
|
||||||
# Print only warnings and errors during test
|
# Print only warnings and errors during test.
|
||||||
config :logger, level: :warning
|
#
|
||||||
|
# We configure the default handler instead of Logger on purpose,
|
||||||
|
# as we want all calls to be processed, especially when using
|
||||||
|
# the JSON formatter.
|
||||||
|
config :logger, :default_handler, level: :warning
|
||||||
|
|
||||||
|
# Also configure the JSON formatter for test.
|
||||||
|
# We make sure we write all currently available metadata.
|
||||||
|
path = "tmp/test.log.json"
|
||||||
|
File.rm(path)
|
||||||
|
|
||||||
|
config :livebook, :logger, [
|
||||||
|
{:handler, :json_log, :logger_std_h,
|
||||||
|
%{
|
||||||
|
config: %{file: ~c"#{path}"},
|
||||||
|
formatter: {LoggerJSON.Formatters.Basic, %{metadata: [:users, :request_id]}}
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
|
||||||
# Disable authentication in tests
|
# Disable authentication in tests
|
||||||
config :livebook,
|
config :livebook,
|
||||||
|
|
|
@ -101,8 +101,19 @@ defmodule Livebook do
|
||||||
config :logger, level: level
|
config :logger, level: level
|
||||||
end
|
end
|
||||||
|
|
||||||
if metadata = Livebook.Config.log_metadata!("LIVEBOOK_LOG_METADATA") do
|
log_metadata = Livebook.Config.log_metadata!("LIVEBOOK_LOG_METADATA")
|
||||||
config :logger, :console, metadata: metadata
|
log_format = Livebook.Config.log_format!("LIVEBOOK_LOG_FORMAT") || :text
|
||||||
|
|
||||||
|
case {log_format, log_metadata} do
|
||||||
|
{:json, log_metadata} ->
|
||||||
|
config :logger, :default_handler,
|
||||||
|
formatter: {LoggerJSON.Formatters.Basic, %{metadata: log_metadata || [:request_id]}}
|
||||||
|
|
||||||
|
{:text, log_metadata} when not is_nil(log_metadata) ->
|
||||||
|
config :logger, :default_formatter, metadata: log_metadata
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
if port = Livebook.Config.port!("LIVEBOOK_PORT") do
|
if port = Livebook.Config.port!("LIVEBOOK_PORT") do
|
||||||
|
|
|
@ -4,6 +4,7 @@ defmodule Livebook.Application do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def start(_type, _args) do
|
def start(_type, _args) do
|
||||||
|
Logger.add_handlers(:livebook)
|
||||||
Livebook.ZTA.init()
|
Livebook.ZTA.init()
|
||||||
create_teams_hub = parse_teams_hub()
|
create_teams_hub = parse_teams_hub()
|
||||||
setup_optional_dependencies()
|
setup_optional_dependencies()
|
||||||
|
|
|
@ -479,6 +479,23 @@ defmodule Livebook.Config do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Parses and validates log format from env.
|
||||||
|
"""
|
||||||
|
def log_format!(env) do
|
||||||
|
formats = ~w(text json)
|
||||||
|
|
||||||
|
if format = System.get_env(env) do
|
||||||
|
if format in formats do
|
||||||
|
String.to_atom(format)
|
||||||
|
else
|
||||||
|
abort!(
|
||||||
|
"expected #{env} to be one of #{Enum.join(formats, ", ")}, got: #{inspect(format)}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Parses and validates the port from env.
|
Parses and validates the port from env.
|
||||||
"""
|
"""
|
||||||
|
|
1
mix.exs
1
mix.exs
|
@ -123,6 +123,7 @@ defmodule Livebook.MixProject do
|
||||||
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
||||||
{:floki, ">= 0.27.0", only: :test},
|
{:floki, ">= 0.27.0", only: :test},
|
||||||
{:bypass, "~> 2.1", only: :test},
|
{:bypass, "~> 2.1", only: :test},
|
||||||
|
{:logger_json, "~> 6.1"},
|
||||||
# So that we can test Python evaluation in the same node
|
# So that we can test Python evaluation in the same node
|
||||||
{:pythonx, "~> 0.4.2", only: :test},
|
{:pythonx, "~> 0.4.2", only: :test},
|
||||||
# ZTA deps
|
# ZTA deps
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -25,6 +25,7 @@
|
||||||
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
||||||
"jsx": {:hex, :jsx, "3.1.0", "d12516baa0bb23a59bb35dccaf02a1bd08243fcbb9efe24f2d9d056ccff71268", [:rebar3], [], "hexpm", "0c5cc8fdc11b53cc25cf65ac6705ad39e54ecc56d1c22e4adb8f5a53fb9427f3"},
|
"jsx": {:hex, :jsx, "3.1.0", "d12516baa0bb23a59bb35dccaf02a1bd08243fcbb9efe24f2d9d056ccff71268", [:rebar3], [], "hexpm", "0c5cc8fdc11b53cc25cf65ac6705ad39e54ecc56d1c22e4adb8f5a53fb9427f3"},
|
||||||
"kubereq": {:hex, :kubereq, "0.3.2", "e425dd94bd74b6510dc45a9557d9378d3fd6ca0ea3ebe0dad2a9c85a08c2e20a", [:mix], [{:fresh, "~> 0.4.4", [hex: :fresh, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: false]}, {:mint_web_socket, "~> 1.0", [hex: :mint_web_socket, repo: "hexpm", optional: false]}, {:pluggable, "~> 1.0", [hex: :pluggable, repo: "hexpm", optional: false]}, {:req, "~> 0.5.0", [hex: :req, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.0", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "d13293ac66bbfe7ac238881ac0404662af5a01f96f9325dd556594fd858f2a3e"},
|
"kubereq": {:hex, :kubereq, "0.3.2", "e425dd94bd74b6510dc45a9557d9378d3fd6ca0ea3ebe0dad2a9c85a08c2e20a", [:mix], [{:fresh, "~> 0.4.4", [hex: :fresh, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: false]}, {:mint_web_socket, "~> 1.0", [hex: :mint_web_socket, repo: "hexpm", optional: false]}, {:pluggable, "~> 1.0", [hex: :pluggable, repo: "hexpm", optional: false]}, {:req, "~> 0.5.0", [hex: :req, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.0", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "d13293ac66bbfe7ac238881ac0404662af5a01f96f9325dd556594fd858f2a3e"},
|
||||||
|
"logger_json": {:hex, :logger_json, "6.2.1", "a1db30e1164e6057f2328a1e4d6b632b9583c015574fdf6c38cf73721128edcb", [:mix], [{:decimal, ">= 0.0.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:ecto, "~> 3.11", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "34acd0bfd419d5fcf08c4108a8a4b59b695fcc60409dc1dd1a868b70c42e1d1f"},
|
||||||
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
|
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
|
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
|
||||||
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
|
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
|
||||||
|
|
|
@ -43,6 +43,34 @@ defmodule Livebook.ConfigTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "log_metadata!/1" do
|
||||||
|
test "parses valida metadata configs" do
|
||||||
|
with_env([TEST_LOG_METADATA: "users,request_id"], fn ->
|
||||||
|
assert Config.log_metadata!("TEST_LOG_METADATA") == [:users, :request_id]
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns nil when environment variable is not set" do
|
||||||
|
assert Config.log_metadata!("TEST_LOG_METADATA") == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "log_format!/1" do
|
||||||
|
test "parses valid formats" do
|
||||||
|
with_env([TEST_LOG_FORMAT: "text"], fn ->
|
||||||
|
assert Config.log_format!("TEST_LOG_FORMAT") == :text
|
||||||
|
end)
|
||||||
|
|
||||||
|
with_env([TEST_LOG_FORMAT: "json"], fn ->
|
||||||
|
assert Config.log_format!("TEST_LOG_FORMAT") == :json
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns nil when environment variable is not set" do
|
||||||
|
assert Config.log_format!("TEST_LOG_FORMAT") == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp with_env(env_vars, fun) do
|
defp with_env(env_vars, fun) do
|
||||||
existing =
|
existing =
|
||||||
Enum.map(env_vars, fn {env, _value} ->
|
Enum.map(env_vars, fn {env, _value} ->
|
||||||
|
|
Loading…
Add table
Reference in a new issue