Packages
timber
2.5.1
3.1.2
3.1.1
3.1.0
3.0.0
3.0.0-alpha.3
3.0.0-alpha.2
3.0.0-alpha.1
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.0
2.6.1
2.6.0
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.1
2.3.0
2.2.1
2.2.0
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
2.0.0-rc7
2.0.0-rc6
2.0.0-rc5
2.0.0-rc4
2.0.0-rc3
2.0.0-rc2
2.0.0-rc1
1.1.18
1.1.17
1.1.16
1.1.15
1.1.14
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
🌲 Great Elixir Logging Made Easy. Official Timber.io Integration.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/timber/install.ex
defmodule Mix.Tasks.Timber.Install do
@moduledoc false
use Mix.Task
alias __MODULE__.{API, Application, EndpointFile, IOHelper, Messages, Project, TimberConfigFile,
WebFile}
alias Mix.Tasks.Timber.Install.HTTPClient.InvalidAPIKeyError
alias Mix.Tasks.Timber.TestThePipes
require Logger
def run([]) do
explain_warnings()
Messages.header()
|> IOHelper.puts()
Messages.forgot_key()
|> IOHelper.puts()
end
def run([api_key]) do
api = API.new(api_key)
try do
:ok = API.start()
API.event!(api, :started)
explain_warnings()
Messages.header()
|> IOHelper.puts()
application = Application.new!(api)
project = Project.new(api)
create_config_file!(application, project, api)
link_config_file!(project, api)
add_plugs!(project, api)
disable_default_phoenix_logging!(project, api)
IOHelper.puts ""
install_user_context!(api)
#install_http_client_context!(session_id, api_key)
platform_install!(API.has_logs?(api), api, application)
API.event!(api, :success)
Messages.free_data()
|> IOHelper.puts()
"""
#{Messages.separator()}
"""
|> IOHelper.puts()
Messages.commit_and_deploy_reminder()
|> IOHelper.puts()
"""
#{Messages.separator()}
"""
|> IOHelper.puts()
collect_feedback(api)
rescue
e in InvalidAPIKeyError ->
message = Exception.message(e)
"""
#{String.duplicate("!", 80)}
#{message}
#{Messages.get_help()}
"""
|> IOHelper.puts(:red)
e ->
message = Exception.message(e)
stacktrace = Exception.format_stacktrace(System.stacktrace())
"""
#{String.duplicate("!", 80)}
#{message}
#{Messages.get_help()}
---
Here's the stacktrace for reference:
#{stacktrace}
"""
|> IOHelper.puts(:red)
# case IOHelper.ask_yes_no("Permission to send this error to Timber?", api) do
# :yes ->
# data = %{message: message, stacktrace: stacktrace}
# API.event!(api, :exception, data: data)
# :no -> :ok
# end
:ok
end
end
defp explain_warnings do
if !Code.ensure_loaded?(:hackney) || !Code.ensure_loaded?(Plug) do
"""
^ These warnings are perfectly normal :) We include various libraries as *optional*
depdencies, which logs these *expected* warnings. Now onto the good stuff...
"""
|> IOHelper.puts(:yellow)
Messages.separator()
|> IOHelper.puts()
end
end
defp create_config_file!(application, project, api) do
Messages.action_starting("Creating #{TimberConfigFile.file_path()}...")
|> IOHelper.write()
TimberConfigFile.create!(application, project, api)
Messages.success()
|> IOHelper.puts(:green)
end
# Links config/timber.exs within config/config.exs
defp link_config_file!(%{config_file_path: config_file_path}, api) do
Messages.action_starting("Linking #{TimberConfigFile.file_path()} in #{config_file_path}...")
|> IOHelper.write()
TimberConfigFile.link!(config_file_path, api)
Messages.success()
|> IOHelper.puts(:green)
end
# Adds the timber plugs in the endpoint.ex file
defp add_plugs!(%{endpoint_file_path: nil}, _), do: nil
defp add_plugs!(%{endpoint_file_path: endpoint_file_path}, api) do
Messages.action_starting("Adding Timber plugs to #{endpoint_file_path}...")
|> IOHelper.write()
EndpointFile.update!(endpoint_file_path, api)
Messages.success()
|> IOHelper.puts(:green)
end
# Disables the default phoenix logging since we handle that with our Phoenix
# instrumenter
defp disable_default_phoenix_logging!(%{web_file_path: nil}, _), do: nil
defp disable_default_phoenix_logging!(%{web_file_path: web_file_path}, api) do
Messages.action_starting("Disabling default Phoenix logging in #{web_file_path}...")
|> IOHelper.write()
WebFile.update!(web_file_path, api)
Messages.success()
|> IOHelper.puts(:green)
end
# Asks the user if they want to install user context to track users in their logs.
defp install_user_context!(api) do
"""
#{Messages.separator()}
"""
|> IOHelper.puts()
answer = IOHelper.ask_yes_no("Does your application have user accounts?", api)
case answer do
:yes ->
Messages.user_context_instructions()
|> IOHelper.puts()
answer = IOHelper.ask_yes_no("Ready to proceed?", api)
IOHelper.puts ""
case answer do
:yes -> :ok
:no -> install_user_context!(api)
end
:no ->
false
end
end
# defp install_http_client_context!(api) do
# """
# #{Messages.separator()}
# """
# |> IOHelper.puts()
# answer = IOHelper.ask_yes_no("Does your application send outgoing HTTP requests?", api)
# case answer do
# :yes ->
# Messages.outgoing_http_instructions()
# |> IOHelper.puts()
# case IOHelper.ask_yes_no("Ready to proceed?", api) do
# :yes -> :ok
# :no -> install_http_client_context!()
# end
# :no -> false
# end
# end
defp platform_install!(true, _api, _application) do
"""
#{Messages.separator()}
"""
|> IOHelper.write()
Messages.action_starting("Checking if your application is already sending logs...")
|> IOHelper.write()
Messages.success()
|> IOHelper.puts(:green)
end
defp platform_install!(false, api, %{platform_type: "heroku", heroku_drain_url: heroku_drain_url}) do
Messages.heroku_drain_instructions(heroku_drain_url)
|> IOHelper.puts()
API.wait_for_logs(api)
end
defp platform_install!(false, %{api_key: api_key} = api, _application) do
Messages.action_starting("Sending a few test logs...")
|> IOHelper.write()
# We manually initialize the backend here and mimic the behavior
# of the GenEvent system
{:ok, http_client} = Timber.LoggerBackends.HTTP.init(Timber.LoggerBackends.HTTP, [api_key: api_key])
log_entries = TestThePipes.log_entries()
http_client =
Enum.reduce(log_entries, http_client, fn log_entry, http_client ->
{:ok, http_client} = Timber.LoggerBackends.HTTP.handle_event(log_entry, http_client)
http_client
end)
Timber.LoggerBackends.HTTP.handle_event(:flush, http_client)
Messages.success()
|> IOHelper.puts(:green)
API.wait_for_logs(api)
end
defp collect_feedback(api) do
case IOHelper.ask("How would rate this install experience? 1 (bad) - 5 (perfect)", api) do
v when v in ["4", "5"] ->
API.event!(api, :feedback, %{rating: v})
"""
💖 We love you too! Let's get to loggin' 🌲
"""
|> IOHelper.puts()
v when v in ["1", "2", "3"] ->
"""
Bummer! That is certainly not the experience we were going for.
Could you tell us why you a bad experience?
(this will be sent directly to the Timber engineering team)
"""
|> IOHelper.puts()
case IOHelper.ask("Type your comments (enter sends)", api) do
comments ->
API.event!(api, :feedback, %{rating: v, comments: comments})
"""
Thank you! We take feedback seriously and will work to resolve this.
"""
|> IOHelper.puts()
end
v ->
IOHelper.puts("#{inspect(v)} is not a valid option. Please enter a number between 1 and 5.\n", :red)
collect_feedback(api)
end
end
end