defmodule Kmsg do @moduledoc """ A logger backend for the printk buffer. Documentation is available on [hex.pm](https://hexdocs.pm/kmsg) ## Usage Add Kmsg to your application by adding `{:kmsg, "~> #{Mix.Project.config()[:version]}"}` to your list of dependencies in `mix.exs`: ```elixir def deps do [ # ... {:kmsg, "~> #{Mix.Project.config()[:version]}"} ] end ``` And adding it to your `:logger` configuration in `config.exs`: ```elixir config :logger, backends: [ :console, Kmsg ] ``` ## Nerves Currently this is not compatible with nerves due to the `KmsgTailer` which creates a neverending circulation of Logentries. I'm working on fixing this. """ # Copyright 2019 Hans Bernhard Goedeke # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @behaviour :gen_event @kmsg_device "/dev/kmsg" # @kmsg_device "/dev/stdout" defstruct [ :format, :metadata, :level ] def init(__MODULE__) do config = Application.get_env(:logger, __MODULE__, []) if File.exists?(@kmsg_device) do {:ok, init(config, %__MODULE__{})} else {:error, :ignore} end end def init({__MODULE__, opts}) when is_list(opts) do config = Keyword.merge(Application.get_env(:logger, __MODULE__, []), opts) {:ok, init(config, %__MODULE__{})} end def handle_call({:configure, options}, state) do {:ok, :ok, configure(options, state)} end def handle_event( {level, group_leader, {Logger, message, timestamp, metadata}}, state ) do alias Logger.Formatter if meet_level?(level, state.level) and node(group_leader) == node() do formatted = Formatter.format(state.format, level, message, timestamp, metadata) File.write!(@kmsg_device, formatted) end {:ok, state} end def handle_event(_, state) do {:ok, state} end def handle_info(_, state) do {:ok, state} end def code_change(_old_vsn, state, _extra) do {:ok, state} end def terminate(_reason, _state) do :ok end ## Helpers defp meet_level?(_lvl, nil), do: true defp meet_level?(lvl, min) do Logger.compare_levels(lvl, min) != :lt end defp configure(options, state) do config = Keyword.merge(Application.get_env(:logger, __MODULE__, []), options) Application.put_env(:logger, __MODULE__, config) init(config, state) end defp init(config, state) do level = Keyword.get(config, :level) format = Logger.Formatter.compile(Keyword.get(config, :format)) metadata = Keyword.get(config, :metadata, []) # max_buffer = Keyword.get(config, :max_buffer, 32) %__MODULE__{ state | format: format, metadata: metadata, level: level } end end