Questions about Elixir Logger

Hey, I tried to debug how I can catch some things.
I created a new local application by mix new my_logger command.
My config/config.exs:

use Mix.Config

config :logger,
  backends: [MyLogger],
  handle_otp_reports: true,
  handle_sasl_reports: true

and here is my main module:

defmodule MyLogger do
  use GenEvent

  def init(_) do
    {:ok, %{}}
  end

  def handle_call({:configure, options}, _state) do
    {:ok, :ok, %{}}
  end

  def handle_event({_level, gl, {Logger, _, _, _}}, state) when node(gl) != node() do
    {:ok, state}
  end
  def handle_event({level, group_leader, {Logger, message, {date, time}, details}}, state) do
    IO.inspect "catched?"
    # stacktrace here ...
    {:ok, state}
  end
end

at end I run iex -S mix command.
In Interactive Elixir I can see already catched 3 log messages.
It works also when I try to execute an: Logger macros such as info.
I checked what data I could collect and it’s really good.
From here I have some questions:

  1. I know that &System.stacktrace/0 works only for exceptions. Is there any other way (logger configuration?) to get a stacktrace when message is logged?
  2. Is there any way to catch also all raise (I mean allow crash, but send info about it to Logger). I already tried set some logger configuration variables like @OvermindDL1 already wrote, but I don’t got a “catched?” message.
  3. Is it possible (by configuration change?) to got passed argument values in stacktrace (optionally only for Logger), so I could easily reproduce an logged message in user specific situations?

Unsure about that, worst case you could throw and catch an exception to get a stacktrace
 sounds slow though

Any uncaught raise’s or any uncaught exception in general should kill its process and log to :sasl, which you can catch through there. However ‘caught’ exceptions are ‘caught’, thus handled, thus nothing reported, so you will never get a ‘catched’ message of any form, only uncaught exceptions. You’d have to decorate raise with a replacement macro or something to do that I’d think?

Stacktraces do not hold that much information, especially because that information might be gone by the time the exception is thrown if those names end up unused by the point the exception is thrown and, say, garbage collected, so I’m unsure if that would even be possible in all cases. At the very least you should have the information for the current application, module, function name, file name, and line number of a log, beyond that you need to add the extra information yourself (which you could easily add the passed in arguments to by adding them to the process log of metadata by calling something like Logger.metadata([:func_args, [blah, bleep]]) but that will get overwritten of course if calling any other function before that point. If you made a custom function (replace def with your own) then you could decorate a log call as much as you want safely, but you’d have to change your function definitions everywhere that you’d want to log that information. Remember that Elixir is about explicitness, minimize magical stuff. :slight_smile:

I could easily be wrong on much of this, I use Logger to log, I’ve not made my own back-end yet. ^.^

1 Like

No, I already tried that:

try do
  raise "oops"
rescue
  exception ->
    IO.inspect System.stacktrace
end

This returns stacktrace, but only for: :proc_lib, GenEvent and my logger.

hmm, I would like to catch all raise without try do 
 rescue 
 end code.
I do not expect too many mistakes, but I want to catch them all like Pokemons trainer :smile:

Third question was optional. It’s not big problem to detect what goes wrong without know a value of function arguments.

You’d have to do it at the call site via a custom log function, it would not work within your logger. Sorry I forgot to say that. :-)[quote=“Eiji, post:3, topic:2237”]
hmm, I would like to catch all raise without try do 
 rescue 
 end code.
I do not expect too many mistakes, but I want to catch them all like Pokemons trainer :smile:
[/quote]

Hehe, you should be able to by turning on :sasl and such. Raise’s without try’s will eventually kill the process that it is in, which will be reported by :sasl. If it does not kill the process then there was a try that caught it. :slight_smile:

The pointer honestly probably still exists on the stack itself until the function returns (thus no GC’ing it), but no clue how it would be accessible as for all intents and purposes it is dead at those times after its last use. A custom log function/macro could keep it alive though, but I’m pretty sure that would require a custom def regardless unless you use one of the :after_compile hooks or so


1 Like

Ahahah, I want to add one more question to my previous response:

One more question:
Is it good to catch raise (by try do ...) to get stacktrace and call Logger.error instead of just raise an exception in my libraries in methods with names ends with “!” character?

but you was faster - just love this fast forum :smile:

It looks interesting, because I will not have stacktrace in external libraries as “Ecto”, which is even recommended and simply beautiful! :smile:

It’s probably not work for me, because I tried to raise in iex. It probably have some catches to let console live for >10 years :smile:

It looks like complicated. I want to write code that anyone can understand without “hacks”. As I wrote it already. It was only optional question. Anyway thanks for your that answer.

The shell, sadly, does not live on an OTP tree (it lives ‘in’ it, but not managed by it, it is manually managed), so you will not get messages from it, but you will from anything attached to the OTP tree. :slight_smile:

You could easily make a defLog or so macro that people can replaced def with that adds in all kinds of extra useful log metadata or so (and clears it when it exits). :slight_smile:

That, to me, would be quite beautiful. :slight_smile:

1 Like

I will add one more field to database table to save other_data map and after my initial release feel free to create PR for that. At now I’m feel that my Elixir knowledge is not so good to write so advanced macro like that. :smile:
It could be useful when in job you work with beginners. You could just instruct them to use “special” macro to let inspect some data and let them learn “special” user cases.