This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-10-23
Channels
- # aws-lambda (2)
- # beginners (40)
- # calva (9)
- # cider (17)
- # clojure (84)
- # clojure-europe (13)
- # clojure-nl (1)
- # clojure-norway (77)
- # clojure-uk (26)
- # conjure (1)
- # cursive (7)
- # datomic (7)
- # events (1)
- # exercism (1)
- # gratitude (2)
- # hyperfiddle (4)
- # improve-getting-started (10)
- # jobs-discuss (12)
- # malli (4)
- # missionary (12)
- # off-topic (22)
- # other-languages (4)
- # pedestal (3)
- # portal (6)
- # reagent (6)
- # reitit (1)
- # releases (1)
- # ring (4)
- # shadow-cljs (2)
- # thejaloniki (2)
- # tools-build (27)
- # tools-deps (4)
- # vim (6)
I’ve got some code that uses clojure.tools.logging
and I use the default (which is java util logging). Is there any way to get the actual line of the log statement? I get this message every time I log something: Oct 23, 2023 2:14:47 PM clojure.tools.logging$eval3668$fn__3671 invoke
, so it clearly is reporting the log macro itself instead of where my acutal log statement is.
Yeah I looked at the code of clojure.tools.logging and it seems to have no support for logging function and line, just the namespace at best. Certainly inferior library compared to timbre. Shame.
I don’t think that’s the case, that configuration can fix it. I’ve also seen comments in timber code where it has provider for clojure.tools.logging to the effect that the library can only take namespace information, not function or line information
if they wanted to do this, clojure.tools.logging would need to use LogRecord classes to log, but they don’t.
you are right, looking at the code only these things are explicitly passed to the logging backend
(write! [logger level throwable message]
"Writes a log message to the given Logger.")
Which does not include line or function infobut, there should be a way - either with MDC, dynamic vars, or scope locals - to get that info down there
We've used c.t.l and log4j for ages and we don't miss function/line -- we just use something descriptive in the message to make it easy to tie back to something specific in our source. We do use MDC but that's typically for stuff like user ID, IP of the request, etc. Using standard Java logging under the hood is important to us for observability tooling (New Relic), which is also why we use Jetty rather than http-kit.
as a hack you should be able to add line number with macro. So whenever you call log
fn you can call your log
macro which add line number under the hood. That is one of a few reasons when you can want to use macro.
https://www.reddit.com/r/Clojure/comments/sxwmt9/how_to_log_line_numbers_with_toolslogging/
Something like what I found in google. I didn’t test.
But as Sean mentioned. I don’t have lines in logs for my hobby projects and I even didn’t realise it until this moment.
@U04V70XH6 ok but you could also use Timbre and an appender that posts to java.util.logging . If you don’t need correct namespace or function then that’s fine, but I’d love to see support for that in c.t.l and I am not sure why it’s absent. As a library author I avoid putting any logging in my libraries specifically because I don’t want to force any user to use Timbre or whatever specifically. So I wanted to use c.t.l so library users could use whatever. However since the lines look like this:
Oct 23, 2023 2:15:14 PM clojure.tools.logging$eval3668$fn__3671 invoke
this makes the whole thing really ugly. Now the library user needs to adjust JUL pattern to use logger name instead of class. WHich then changes it for all of their logging on the project. This requirement to change JUL pattern to make c.t.l work is just a bad thing.The only reason I would say avoid timbre is that it pulls in a bunch of stacktrace prettification libraries by default, which makes reading logs wretched
In my java libraries, i've started just using System.Logger
. Means consumers need to pull in the slf4j adapter usually, but i don't have any consumers
@U66G3SGP5 We tried Timbre for a while and disliked it a lot for all sorts of reasons. We have everything going through log4j2 now -- every bridge-to-log4j -- and we can configure it centrally and override that as needed, it supports MDC, and works well with New Relic log forwarding. We have multiple log outputs (rolling detailed logs), plain console logs with less detail. And we have a bunch of smart email reporting wired into log4j2. I also have my dev setup with c.t.l wired into Portal so I can expect log messages in my editor too. I agree that it would be nice for c.t.l to have an optional way to add line numbers to the logging tho'. Someone should post a request on http://ask.clojure.org 🙂
Can someone explain this behaviour ? I am running this code, it returns 160000 if I remove the doall it only returns 20000, I understand lazy sequences in that they don't process unless used but seems I am using it, or is this a case of because the db is a side effect that causes it to not realize it should process all the data ? trying to get a better understanding as this surprised me.
(doall
(->> (partition-all 5000 users)
(map-indexed (fn [idx users]
(-> (sqlh/select [:email :email]
:first_name
:last_name)
(sqlh/from :accounts)
(sqlh/where [:in :%lower.email users])
(db/query))))
(flatten)))
I think I normally use mapv but in this instance wanted to keep an eye on progress so switched to map-indexed so I could print the indexJust in case, how I'd write that code if I didn't need lazy collections at all:
(into []
(comp (partition-all 5000)
(mapcat (fn [users] ...)))
users)
The code is in a function which I call to get the results basically, interesting mapcat is not something I use that often
fair enough, still interested in why I need the doall with the above, I will change what I have to use mapcat and comp
> still interested in why I need the doall with the above It depends on how the data is used. I don't think there's anything that can be said without that detail.
Now, if you (count ...)
it and get different numbers, then it's extremely suspicious.
so basically I am calling the above code, passing it through a function that maps it into another structure then I am pushing it into another function which inserts data
but I was calling the above with count to debug what was happening which is when I noticed I got less results
(defn fetch-data [users]
(doall
(->> (partition-all 5000 users)
(map-indexed (fn [idx users]
(-> (sqlh/select [:email :email]
:first_name
:last_name)
(sqlh/from :accounts)
(sqlh/where [:in :%lower.email users])
(db/query))))
(flatten))))
(def data
(fetch-data [""]))
(count data)
so this is basically what I am doing,Oh, one other thing - seems like db/query
is using a dynamic variable to get the DB connection.
Do not mix dynamic variables and lazy collections, you're gonna have a bad time unless you're extremely careful:
user=> (def ^:dynamic *x* 1)
#'user/*x*
user=> (let [c (binding [*x* 2] (repeatedly #(identity *x*)))] (take 1 c))
(1)
The biggest problem is that by the time the lazy collection gets around to doing some computation, the dynamic binding will likely no longer be in effect.
@UU67HFS2X https://clojure-goes-fast.com/blog/clojures-deadly-sin/#dynamic-bindings
Thanks make sense now, I remember reading that article before but the refresher helped, probably forget again by time I hit the issue again 🙂
Actively avoiding laziness might be easier than trying to remember all the intricacies, you know!
https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects is a broadly supported idiom, regardless of broader opinions on laziness
I found out this weekend that my not-so-secret plot to trick musical artists into learning some Clojure has ensnared Deadmau5, who has long been my archetypal ideal user. He posted this, and in the comments thread mentions he’s using my Deep Symmetry projects and “a weird as :face_with_symbols_on_mouth: scripting lang”, which is Clojure. Beat Link Trigger is written in Clojure, and provides a user interface for people to add new integrations (including pulling down new dependencies) using Clojure. https://www.instagram.com/p/CyoWEm9h5fi/?igshid=MzRlODBiNWFlZA==

Here’s a slightly earlier post where he shows a video of the way he’s using my stuff to synchronize his CDJs with tracks playing in Ableton Live: https://www.instagram.com/p/CyoXCTsB2H3/
Oh, and the reverse-engineering documentation I created to help other people build similar things in other languages uses sci
to provide a nice domain-specific language for embedding byte-field diagrams inside AsciiDoc documents, which has proven to be an even more effective stealth avenue for bringing other kinds of people into the Clojure fold. 😆
That's awesome, once we see al DJs wearing t-shirts with parens, the Clojure community has won 😉
On a related note, I'm looking into Max/MSP's Java support but I'm not sure how the performance is going to be :thinking_face:
> “a weird as :face_with_symbols_on_mouth: scripting lang” - Deadmau5 This should be somewhere!
@U0JEFEZH6 I had previously played a bit with ClojureScript + https://docs.cycling74.com/nodeformax/api/ and it worked well!
@UA2U3KW0L oh yeah, that's an option too - and wouldn't require the JVM to be installed, I'll look into that!
Nice one 💪 I invested pretty heavily into cdj and ableton stuff in the past, but sadly I don't have as much time/energy these days. Would have loved to hack on Deep Symmetry 😿
Yeah, I think that may be general JVM negativity, which does still exist out there!
@U0JEFEZH6 I did embed Afterglow, my Clojure live-coding lighting controller, in Max/MSP and it seemed to work fine, but the direction there definitely seems to have shifted to Node/JS lately and Afterglow is heavily dependent on a lot of JVM libraries so I have not explored working with ClojureScript there.
Nice, my needs are much simpler as I'm mostly looking into MIDI effects... but just as @U45T93RA6 there's just no time to look into this stuff
wow- very cool stuff @U0EHA00G1! I'm going to try really hard not to fool around with any of it for a few months (tight deadlines).
I'm running into an issue with aliases and global + project deps.edn
- more details in 🧵
I have a project deps.edn
with :dev
alias, which sets up jvm-opts
and extra-paths
. Then in my ~/.clojure/deps.edn
my :dev
alias adds a dependency, a collection of REPL helpers, and it brings a couple of other things like Portal. The global dev
alias uses :extra-deps
to add it.
Last thing is :nrepl
alias in my project's deps.edn
, which pulls in nREPL and uses main-opts
to start it.
The issue I'm running into is that when I run clj -M:dev:nrepl
and try to require my helper library, I get an error that it's not found on the classpath. I read the guide an it's not obvious what's going on or how to debug it.
can you share the actual aliases from both deps.edn's?
if it's a lib issue, might also be useful to check your deps with clj -X:deps tree :aliases '[:dev :nrepl]'
Thanks, here are the relevant parts: https://gist.github.com/lukaszkorecki/7d3f4d2cf28dd18fc0b1c622a99586d6
When I run deps tree
, I can see my helper lib being pulled in along with its dependencies
that doesn't seem exactly to match what you said above - it's including nrepl in you main deps (seems weird) and not in the :nrepl alias, but if you're seeing your helper lib being pulled in then it's probably something else. what does your helper lib deps.edn look like, and are you sure you've not mis-typed the namespace of your helper lib namespace?
can you load a repl in the helper lib and do the same require?
I guess it's just (require 'r)
?
Yeah, it should be (require 'r)
- running that in the clj
repl in my library works just fine. As for the nREPL dependency is also used by the application in production, I can connect to a live process Just In Case ™️
I tried just doing
% clj -Sdeps '{:deps {io.github.lukaszkorecki/rumble {:git/sha "45eac130737b8049821c45859d374c1db1d82471"} nrepl/nrepl {:mvn/version "1.0.0"}}}'
Clojure 1.11.1
user=> (require 'r)
> c > #'r/c > ([component-name])
Pul out a compont from a running system, pass keyword for the component name
...
which seemed to work finethe tree thing you did above should be loading the same set of deps
if you did clj -Sverbose -M:dev:nrepl
that should print a cp_file location in your .cpcache (that's the actual classpath used) and you can find the basis file (merged deps.edns) at the same path but .basis instead of .cp
I don't know if it's possible you have an old Clojure CLI, but you can check that with clj -version
. Latest is 1.11.1.1413.
Looks like I have latest. I've created an empty project, just with the dev alias, and inspected the basis file. Merged dev alias looks like this:
:dev {:extra-paths ["dev-resources" "dev-data"] :jvm-opts ["-XX:-OmitStackTraceInFastThrow" "-Duser.timezone=UTC" "-Dfile.encoding=UTF-8" "-Djava.awt.headless=true"]}
I assume I should see deps/extra-deps here, right? Here's the test deps.edn
file: https://gist.github.com/lukaszkorecki/7d3f4d2cf28dd18fc0b1c622a99586d6#file-test-deps-ednJust to confirm one thing, when I add my library directly to the dev alias dependencies list in my project, it works as expected
@U02EMBDU2JU You said:
> I have a project deps.edn
with :dev
alias, which sets up jvm-opts and extra-paths
. Then in my ~/.clojure/deps.edn
my :dev
alias adds a dependency
The same alias in both deps.edn
files? The project :dev
will hide the user :dev
-- they don't combine.
Am I misunderstanding you?
One reason I use qualified keywords for the user deps.edn is to avoid clashes with project xep.edn aliases that people create https://practical.li/clojure/clojure-cli/practicalli-config/
@U04V70XH6 oh, so I misread the merge section on this page? https://clojure.org/reference/deps_and_cli#_merging_deps_edn
I suppose it makes sense why aliases wouldn't be merged just from the sensible approach perspective - it would be a nightmare to debug, but the way that section is written it implies that everything is merged (except the keys mentioned). I guess my global deps.edn
can just include with top level :deps
list which does get merged from what I'm seeing but it was somewhat surprising that aliases do not work this way.
The contents of the set of aliases you provide are merged -- but that means that you merge system + user + project deps.edn
as hash maps first, then get the (juxt :all :the :aliases :provided)
-- in your case (juxt :dev :repl)
-- and then that vector of data is merged using the deps-specific merge rules.
It would be bad if a project couldn't rely on its own aliases but instead they were modified (merged) with arbitrary data coming from a different user deps.edn
on everyone's machine -- whereas if a developer says -M:my/dev:dev:nrepl
and :my/dev
is not in the project deps.edn
, that's specifically up to the developer who knows what they are adding to the project deps etc.
Right, I get why it's not working that way - it was just not apparent from the reference, when I read merge... I thought it would truly merge everything. All clear now!
I guess you could create a GH issue for clojure-site to suggest that wording be clarified...
common to all platforms