Last active
November 24, 2019 23:19
-
-
Save stephanos/08d611fc8886ff92ab1aa07296abb5fc to your computer and use it in GitHub Desktop.
using Google Cloud's Stackdriver Logging API on App Engine with Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule GoogleAppEngine do | |
def google_cloud_project() do | |
System.get_env("GCLOUD_PROJECT", | |
System.fetch_env!("GOOGLE_CLOUD_PROJECT")) | |
end | |
def google_app_engine_service() do | |
System.fetch_env!("GAE_SERVICE") | |
end | |
def google_app_engine_version() do | |
System.fetch_env!("GAE_VERSION") | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule GoogleCloudLogger do | |
use GenServer | |
alias GoogleAppEngine | |
alias GoogleApi.Logging.V2.Connection | |
alias GoogleApi.Logging.V2.Api.Entries | |
alias GoogleApi.Logging.V2.Model.{LogEntry, MonitoredResource, WriteLogEntriesRequest} | |
def init(_) do | |
{:ok, configure([])} | |
end | |
def handle_call({:configure, opts}, _config) do | |
{:ok, :ok, configure(opts)} | |
end | |
def handle_event({_level, gl, _event}, config) when node(gl) != node() do | |
{:ok, config} | |
end | |
def handle_event({level, _gl, {Logger, msg, ts, md}}, %{level: min_level} = config) do | |
if is_nil(min_level) or Logger.compare_levels(level, min_level) != :lt do | |
case log_event(level, msg, ts, md, config) do | |
{:ok, _} -> :ok | |
{:error, reason} -> IO.puts("unable to write to Logging API: #{inspect reason}") | |
end | |
end | |
{:ok, config} | |
end | |
defp log_event(level, msg, ts, md, config) do | |
%{project_id: project_id, module_id: module_id, version_id: version_id} = config | |
case Goth.Token.for_scope("https://www.googleapis.com/auth/cloud-platform") do | |
{:ok, token} -> | |
request = %WriteLogEntriesRequest{ | |
entries: [%LogEntry{ | |
textPayload: IO.chardata_to_string(msg), | |
severity: to_severity(level), | |
timestamp: to_iso8601(ts) | |
}], | |
labels: to_labels(md), | |
logName: "projects/#{project_id}/logs/appengine.googleapis.com%2Fstdout", | |
partialSuccess: true, | |
resource: %MonitoredResource{ | |
labels: %{ | |
project_id: project_id, | |
module_id: module_id, | |
version_id: version_id | |
}, | |
type: "gae_app" | |
} | |
} | |
Entries.logging_entries_write(Connection.new(token.token), [ | |
body: request | |
]) | |
error -> error | |
end | |
end | |
defp configure(opts) do | |
env = Application.get_env(:logger, :google_cloud, []) | |
opts = Keyword.merge(env, opts) | |
Application.put_env(:logger, :google_cloud, opts) | |
%{ | |
level: Keyword.get(opts, :level, :info), | |
project_id: GoogleAppEngine.google_cloud_project(), | |
module_id: GoogleAppEngine.google_app_engine_service(), | |
version_id: GoogleAppEngine.google_app_engine_version() | |
} | |
end | |
defp to_severity(:debug), do: "DEBUG" | |
defp to_severity(:info), do: "INFO" | |
defp to_severity(:warn), do: "WARNING" | |
defp to_severity(:error), do: "ERROR" | |
defp to_labels(metadata) do | |
Enum.reduce(metadata, %{}, fn | |
({key, val}, acc) when is_binary(val) -> | |
Map.put(acc, key, to_string(val)) | |
({key, val}, acc) -> | |
case String.Chars.impl_for(val) do | |
nil -> acc | |
_ -> Map.put(acc, key, to_string(val)) | |
end | |
end) | |
end | |
defp to_iso8601({{year, month, day}, {hour, minute, second, millisecond}}) do | |
%DateTime{ | |
year: year, month: month, day: day, | |
hour: hour, minute: minute, second: second, microsecond: {millisecond * 1000, 3}, | |
zone_abbr: "UTC", utc_offset: 0, std_offset: 0, time_zone: "Etc/UTC" # assuming UTC | |
} | |
|> DateTime.to_iso8601() | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defp deps do | |
[ | |
{:goth, "~> 1.2.0"}, | |
{:google_api_logging, "~> 0.18"}, | |
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use Mix.Config | |
config :logger, | |
backends: [ | |
GoogleCloudLogger | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment