tj0
I was trying to get some structured logging with duration for liveview, but am running into a few issues in keeping the logs useful.
03:44:09.182 [info] user_id=8 ip=1.2.3.4 guid=03de7c92-cdbf-4f67-ad10-6abd51ef634c duration=215.585 status=200 params={} path=/a/b/ method=GET headers={"referer":"http://localhost:4000/a"}
- I can’t seem to figure out how/why “handle_params” is being called when I’m leaving the page to a completely different LiveView. This only happens on some LiveViews and not others. Anyway, it is doing all the work on the page, 3-4 other handlers are just doing a push_patch which handle_params takes care of, so I don’t get the appropriate duration if I capture those. I think telemetry has the same issue, but haven’t checked.
- I cannot seem to disable the channel logs.
The documentation says:
By default, channel `"join"` and `"handle_in"` events are logged, using
the level `:info` and `:debug`, respectively. Logs can be customized per
event type or disabled by setting the `:log_join` and `:log_handle_in`
options when using `Phoenix.Channel`. For example, the following
configuration logs join events as `:info`, but disables logging for
incoming events:
use Phoenix.Channel, log_join: :info, log_handle_in: false
In my some_app.ex, I’m modified Phoenix.Channel, but it still doesn’t appear to work after a clean recompile. I can’t seem to find any other instance of Phoenix.Channel in the codebase. Any ideas?
def channel do
quote do
use Phoenix.Channel, log_join: false, log_handle_in: :warn
import SomeApp.Gettext
end
end
- I wanted to log as json, but I also need some metadata appended. If I format my messages as json, the Logger adds the metadata and it becomes invalid.
Any ideas regarding any of these questions? Thanks.
Trending in Questions
Hey guys,
I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly
Do you guys have any suggestions what is the best prac...
New
Hello!
Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app.
I creat...
New
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
Anyone here using Honeybadger?
My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of
Bandit.HTTPError...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
There are three potential reasons for members of this forum to have a look at https://vutuv.de
You are tired or annoyed of LinkedIn.
Yo...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixir-ls
- #ai
- #elixirconf-us
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
hauleth
I am on my phone, so I will be able to respons only to some of these right now.
About 2. the reason is probably because Phoenix decided to log on Telemetry messages instead of using Logger directly, which caused that weird behaviour. However someone with better Phoenix logging experience should tell you more. Personally I think that this is wrong approach (as this makes the stuff like location metadata useless), but I see that it is pretty common in Phoenix and Ecto.
About 3. you cannot really do so with Elixir Backends. You could “hack around” with translators, but that would be hacking instead of looking for a solution. “Real” solution would be usage of Erlang Handlers with Formatters, but using these from Elixir isn’t really straightforward, so this may require a little bit more knowledge (however it will came with a great power).
tj0
Hey, thanks for your work on logging.
So for 2, I found the following to disable Phoenix logs completely:
config :phoenix, :logger, falseNot sure if it’s a good idea, but it works.
Regarding 3 - can you point me in the right direction? I’m considering patching the logster library right now or maybe using flatlog.
Glad you already answered the question about that
tj0
I’ve spent some time now messing around with a bunch of libraries.
For posterity:
flatlog - works reasonably well with 1.11.4 and calling
:logger.logdirectly. Unfortunately, it flattens all the elixir structs to their root elements, meaning that something likeparams: %{"b_time" => ~N[2021-03-21 00:00:06]becomesparams_b_time_year=2021 params_b_time_second=6 params_b_time_month=3 params_b_time_minute=0 params_b_time_microsecond={0,0}. Wraps exceptions nicely though.Ink - Dumps everything to js which is fine, but not in a single log msg. And since the msg is not broken out, we can’t really use it unless we do regexp on the other side. Did not test exceptions.
{"guid":"03de7c92-cdbf-4f67-ad10-6abd51ef634c","hostname":"x230a","level":30,"msg":"Sent 200 in 214ms","name":"x","pid":32183,"time":"2021-03-20T18:35:18.017Z","user_id":8,"v":0}elixir-metadata-logger · GitHub - does what it can given the interface. It turns out that if you write a formatter, Logger gives you back the msg as a string. This means that this project formats all the metadata well, but not the actual msg. Did not test exceptions.
Logster - closest to a functioning solution with the Plug installation and a custom log helper for liveview. There’s some funkiness with metadata when using json which can be easily patched. Now using the plug and the custom format for liveview is fine, but that means the log format for logger is simply
$message. This means that the exceptions are not wrapped. Again, as what goes to the format function in a string, there’s not much to do about it unless you want to encode it, pass it along, decode it, manipulate it, and send out the full msg encoded again. Seems a bit cpu-intensive for a log. But it mostly works.Logfmt - I was hopeful for this library, but it does not parse something like
iex> Logfmt.decode(~S(x={"foo":"bar"}))%{":" => true, "bar" => true, "foo" => true, "x" => "{", "}" => true}Telemetry - I was thinking of hooking up from here instead, but then there’s no way to add any metadata.
It seems that there are three different steps:
So I’m a bit flummoxed. Hopefully, I’m somehow wrong or missed an alternative/option?
hauleth
Well, that is exactly in the name of flatlog
Yes, that was the option I was talking about. However it is not true that “erlang library did not like the elixir structs”, it is just that Erlang doesn’t know about Elixir structs at all. It is perfectly feasible for Erlang library to account Elixir structs or even use Elixir’s
inspect/2for formatting data.It is also important to mention that nothing prevents you from writing formatter for Erlang Logger in Elixir. Simplest possible formatter would be:
It only encodes one metadata field, but you get an idea how to make it work.
After that you can use this encoder with handler of your choice, for example like:
soup
I had a poke around at this, but I think writing your own handler is a pretty big undertaking, while also throwing out a what logger gives you for free (afaik you would have to disable logger else I was getting double logs).
You also have to filter to the elixir domain or handle a bunch of erlang reports. If you have disabled Logger and only filter for elixir, you might fall in some visibility holes.
You also lose stuff like coloured output and the single line exceptions can be a pain with stuff like liveview where you don’t see them rendered. This doesn’t really matter except that if you’re writing your own logger, you probably want to be dang sure it works and so running it in dev is a good way to battle test it, so you do actually notice those missing things.
I may have misunderstood and perhaps it’s simpler to run the two side by side (just unset_handler for logger in the elixir domain?) or just hijack specific message signatures.
I did experiment with just enabling all metadata and writing a formatter to drop what I don’t want, but I am not sure I am that keen on it.
code
In the end I think the better option is having your modules return their keys explicitly and collating them into the normal allow list in config.
Logger does actually accept maps and keyword lists, but they get marked as “erlang reports”, which pass through some special formatting. Since we would be passing in an unknown report format, it lands in
Kernel.inspectand such your formatter gets passed just the string.Summary
Unfortunately, you can’t just ignore the inspect because the value is later passed into a truncation function. You can skip that if you set the
truncateoption to:infinity, which is probably ok.I would open a PR with a fix but I am not really sure what it should look like.
Obviously you don’t want to just drop the report code, so you need a way to flag you want to skip it just for specific messages.
You could flag that in the metadata (mixing domains here
) but probably the better choice is letting
Logger.logflag on the way down whether to coerce types or not (since it’s in elixir land, unlikely a real “erlang report” has spawned from there, probably?).But, that’s potentially a breaking change if people expect it to auto coerce the types.
Maybe a
auto_coerce: trueoption default for logger so that you can elect into passing non-strings to your custom formatter.hauleth
I do not know why you say so. It is pretty simple, especially as you do not really need to implement whole backend, just the formatter you want.
Now you can use it like:
Of course this could be improved even more, but it gives the initial view on how to implement the simple formatter in Elixir.
You can wrap other formatter with stuff that will add colouring.
I would need to check, because these two shouldn’t be related to each other.
You can run as many loggers beside each other as you like. You aren’t limited there (in contrast to many Elixir backends that allow only one instance of themselves to be installed at the same time). For filtering on domain you can use
logger_filters.The problem is that message get stringified before it reaches any backend. Making it passthrough would require a lot of boilerplate and checking for support. I think that sooner Elixir will deprecate the backends than implementing such magic behaviour where some handlers can get structured logs and some will get them stringified. The only problem there right now is that there is no built-in overload protection in Erlang handlers, so we cannot deprecate them right now, as writing Erlang handlers in overload-safe way is non-obvious.
soup
I appreciate the code. I had to tweak some parts to make it work for anyone else coming through (just some mixed up params, time unit and adding a new line):
code
Yeah after thinking about tonight, I was mixing terminology between the logger and the console backend. What I really want is some way to let my formatter capture those
{:report...}messages that get sent to the handler, so they could translate them into strings for the back end…SPOILER
This seems like the smallest way you can get easy structured logging.
We can add a translator to Logger,
And then use it just how
youI want it:It’s not a silver bullet:
You can get around these with a custom formatter, that concats many lines into one and that also wraps normal messages in a
message="..."tag. Note that your translated messages also come as strings, so you’ll probably have to prefix your logfmt messages coming out of the translator, match on that and write them out sans prefix.If you want JSON logging you can basically do the exact same thing, push through a translator and catch “bad” logs in your formatter. Since you are given the meta-data you can affix them to your json object. You will have to rehydrate in the formatter to do this however.
tj0
So I’ve been using the following module in production for a few weeks now, took 3-4 hours to write after @hauleth showed me how to do it (thanks again!).
It’s been my to do list to push this to github, etc. but, well, this is better than no where at all. I prefer having the normal logger in dev though as the reports are much better formatted. I wanted to add an option for flat map also, but here is the json version.
tj0
Updated logger @PaintingWithCode . FYI, no stacktraces atm.