beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2021-04-12T00:13:00.381400Z

I have a lazyseq which contains numbers in pairs, such as (1323 5123) and so on, the thing is, that I was to do a few things, first of: sum that pair into a single one, so for example (+ 1323 5123), but keep in mind there are around 10000 pairs, then I'd like to check which pair, when summed the result is x number, and what was the original pair, any idea?

raspasov 2021-04-12T00:42:58.382200Z

Try a combination of map-indexed, filter, first

2021-04-12T01:13:41.382400Z

Why map-indexed? doesn't it return a lazyseq too?

Valentín 2021-04-12T01:14:43.382600Z

Hi guys, I have a silly question. I got that re-frame template, I made some changes... how can I deploy that code to heroku?

raspasov 2021-04-12T12:41:28.390200Z

🙂

raspasov 2021-04-12T12:42:30.390400Z

All of the above sounds about right.

Edward Ciafardini 2021-04-12T14:43:43.399300Z

this is a really good answer

Valentín 2021-04-12T01:15:07.382700Z

https://github.com/day8/re-frame-template

2021-04-12T01:16:46.382900Z

also, could you explain what you mean, step by step and why?

Zaymon 2021-04-12T01:33:38.383200Z

I use ! to denote this function has a side-effect or mutates some state. It’s a nice little reminder when using any ! function that you should be aware it’s not a pure transformation.

👍 1
nmkip 2021-04-12T01:37:56.383400Z

maybe something like: (first (filter #(= x (reduce + %)) pairs))

2021-04-12T01:42:48.383600Z

what is that pairs for? keep in mind I already made the pairs

2021-04-12T01:43:01.384Z

How to pronounce assoc and dissoc ?

Azzurite 2021-04-12T11:51:01.389900Z

I always say ahsok and dissok

2021-04-12T17:24:09.420200Z

Yeah me too. Ah-sock and dis-sock, but now i see assowsh that makes much more sense!

nmkip 2021-04-12T01:44:12.384100Z

pairs is your lazyseq which contains numbers in pairs

2021-04-12T01:44:33.384300Z

ah alright, let me try that code really quick

2021-04-12T01:44:46.384500Z

if I'm not wrong, % is an empty argument

2021-04-12T01:44:50.384700Z

what's the usage here?

nmkip 2021-04-12T01:44:58.384900Z

you could also use some (some #(and (= 40 (reduce + %)) %) pairs)

2021-04-12T01:45:02.385100Z

sorry if I make dumb questions, I'm relatively new to clojure

2021-04-12T01:45:11.385300Z

and functional programming it self

nmkip 2021-04-12T01:46:05.385500Z

(first (filter #(= x (reduce + %)) pairs)) is the same as (first (filter (fn [pair] (= x (reduce + pair))) pairs))

nmkip 2021-04-12T01:46:58.385700Z

https://clojure.org/guides/learn/functions#_anonymous_function_syntax should explain it

2021-04-12T01:47:03.385900Z

assoc(iate) and dissoc(iate), just don't say the iate

🙃 2
Azzurite 2021-04-12T11:49:41.389500Z

ah yes, "a sauce" and "the sauce"

simongray 2021-04-12T01:47:16.386Z

Assowsh and dissowsh

2021-04-12T01:49:59.386300Z

well, what you sent doesn't work but it may be an issue with my code it self

2021-04-12T01:50:26.386500Z

I'll appreciate any help, here's my code:

(ns d1.core
  (:require [clojure.math.combinatorics :as combo]))

(require (quote [clojure.string :as str]))

(let [f (slurp "d1num.txt")
      u (str/split f #"\s+")
      p (combo/selections u 2)
      h (map bigint p)]

  (first (filter #(= 2 (reduce + %)) h)))

2021-04-12T01:51:00.386700Z

d1num.txt just contains 200 numbers

nmkip 2021-04-12T02:00:43.386900Z

what's the error?

2021-04-12T02:03:12.387100Z

Execution error (IllegalArgumentException) No matching ctor found for class java.math.BigInteger

2021-04-12T02:03:39.387300Z

looks like it's looking for a constructor

nmkip 2021-04-12T02:07:37.387500Z

(combo/selections u 2) is returning a lazyseq of pairs right? I haven't used clojure.math.combinatorics if that's the case, when you map over p, each element is a sequence. try this:

(let [f (slurp "d1num.txt")
      u (str/split f #"\s+")
      p (map #(BigInteger. %) u)
      h (combo/selections p 2)]

  (first (filter #(= 2 (reduce + %)) h)))

2021-04-12T02:09:41.387700Z

returns nil

2021-04-12T02:11:08.387900Z

and yes, (combo/selections u 2) returns a lazyseq of pairs

nmkip 2021-04-12T02:11:39.388100Z

try evaluating every step and see if the intermediate results are ok

raspasov 2021-04-12T02:25:51.388400Z

@jimmyssj3 map-indexed in case you want to know the index of your pair, otherwise not needed

finchharold 2021-04-12T14:00:23.391Z

any jackdaw people?

dharrigan 2021-04-12T14:06:47.391300Z

There is a #jackdaw channel, perhaps that may help?

pez 2021-04-12T14:21:11.393300Z

Why does map “cancel” the dynamic binding here?

(def ^:dynamic *foo* :FOO)
(defn foo [x] (println "1" *foo*) x)
(binding [*foo* :BAR]
  (println "0" *foo*)
  (map foo [:foo])) 
prints:
0 :BAR
1 :FOO

mg 2021-04-12T14:26:06.395400Z

@pez Likely that's because of laziness. The map returns a lazy seq that isn't immediately evaluated - in the REPL it's likely not going to be evaluated until the REPL prints the results, but that happens outside the context of the binding call. If you did (doall (map foo [:foo])) it should exhibit the behavior you're looking for, as doall will force evaluation of the lazy seq immediately.

pez 2021-04-12T14:28:20.396Z

Thanks! That works. I will need to digest this a bit. 😃

pez 2021-04-12T14:33:01.398200Z

Actually, in my real usecase, I am not printing, but it still behaves like it “cancel”. But also still, doall fixes it. I don’t need the lazyness in my case.

raspasov 2021-04-12T14:33:41.398900Z

Perhaps try run! instead of map (if you don’t need the return values); I also like mapv

pez 2021-04-12T15:16:59.400200Z

Oh, I do need the return values. 😃

pez 2021-04-12T15:33:06.400900Z

So, this is more like what my use case is:

(def ^:dynamic *foo* :FOO)
(defn foo [x] [*foo* x])
(binding [*foo* :BAR]
  (map foo [1 2 3])) => ([:FOO 1] [:FOO 2] [:FOO 3])

pez 2021-04-12T15:36:23.403300Z

So, if I understand @michael.gaare correctly, it is the REPL forcing the realisation of the sequence. But it happens at a stage where the binding form is out of scope (or whatever is the right term here). Is that correct?

pez 2021-04-12T15:43:34.404900Z

I added an example to ClojureDocs now: https://clojuredocs.org/clojure.core/binding#example-60746e7de4b0b1e3652d74c4 , please edit it if it needs clarification. (I don’t dare clarify it yet, because I don’t know if I have understood it correctly.)

2021-04-12T15:57:44.405Z

map and many other Clojure functions are lazy.

2021-04-12T15:58:24.405200Z

Until and unless something forces the evaluation of elements of their returned values, the function given to map, foo in your example, will not be called until you force the evaluation of the elements of the sequence returned from map.

2021-04-12T15:58:40.405400Z

If you force the evaluation inside of the binding, then calls to foo will happen then.

2021-04-12T15:58:44.405600Z

with the extra bindings

2021-04-12T15:59:11.405800Z

if you force the evaluation after the binding form has returned, then calls to foo will happen then, without the extra bindings in place, because they will have been removed by that time.

2021-04-12T15:59:43.406Z

binding and lazy evaluation do not mix well together. Standard realization that all Clojure developers come to when they try to mix the two things.

2021-04-12T16:00:35.406200Z

You can either force the evaluation of the lazy sequence(s) inside of the binding scope, or not. If you want the extra bindings in place, better force lazy evaluations inside of the binding body.

pez 2021-04-12T16:02:16.406400Z

Thanks! I think that where I am going with my script, the realization will be forced within the binding, but I also need to be able to REPL it in small chunks, so I’ll either get rid of the dynamic variable or stay with doall. We’ll see. TIL!

2021-04-12T16:32:15.407800Z

i have a side effecting form that is not idempotent. is there a way to only evaluate it once per repl session? i guess i could set up an atom and flag it as evaluated

2021-04-12T16:32:49.408100Z

defer?

mg 2021-04-12T16:32:50.408300Z

@michael740 defonce could work

2021-04-12T16:33:48.408400Z

thank you, i'll take a look!

Dave Suico 2021-04-12T16:35:50.410100Z

Hey guys, I'm trying to use the library ultra-csv in my polylith project but I keep getting this error FileNotFoundException. How do I install this dependency using VSCode and Calva? thanks!

2021-04-12T16:37:47.410300Z

sorry, meant delay

2021-04-12T16:42:28.410500Z

yeah, a delay or promise is how I do this

2021-04-12T16:43:12.410700Z

delay if it's always the result of the same calculation, promise if various different paths might realize it (eg. different functions for connecting to local / staging / prod resources)

2021-04-12T16:44:04.410900Z

@michael740 also consider libs like stuartsierra/component or integrant that are made for managing stateful resources in a systematic and functional way

2021-04-12T16:47:56.411900Z

at a glance, be sure that your repl has restarted since the dep was added, and that the dep is in your classpath

2021-04-12T16:48:02.412200Z

you are adding it as an extra dep when the :dev alias is used, my guess is you aren't using that alias, you should likely move all that up to a toplevel :deps

💯 1
2021-04-12T16:48:25.412300Z

try clj -Spath and see if the dep is in the resulting classpath

2021-04-12T16:49:02.412800Z

oh yeah, contextually having a CSV dep but only at dev time is weird

2021-04-12T16:53:49.413200Z

aha, ok

2021-04-12T16:54:00.413400Z

thank you @noisesmith!

2021-04-12T16:54:24.413600Z

can you say a little about choosing between delay and defonce?

2021-04-12T16:57:11.413800Z

if you want the value to be re-inintialized when code reloads, just use delay. if you want the value to only be reinitialized explicitly use delay inside defonce (and use ns-unmap or def to delete the defonce var in order to reload)

2021-04-12T16:57:59.414100Z

alternatively use neither delay or defonce, and provide the initialized value as an argument to the code that uses it

2021-04-12T16:58:11.414400Z

(which is what component and integrant are for)

Dave Suico 2021-04-12T16:59:47.415900Z

Hello @hiredman @noisesmith, did I do this right at this time now? I added deps at top level as you said and then I run Calva jack-in but still having the same error

2021-04-12T16:59:51.416Z

> provide the initialized value as an argument to the code that uses it aka dependency inject?

2021-04-12T17:00:42.416800Z

that looks right, does "Calva jack-in" actually start a new repl process or just reuse a running one?

2021-04-12T17:01:27.417200Z

and clj -Spath will verify whether the dep is actually being loaded

Dave Suico 2021-04-12T17:01:28.417300Z

I think it is connecting to an existing repl, how do I make it run fresh?

2021-04-12T17:01:43.417500Z

sorry, I don't know Calva

Dave Suico 2021-04-12T17:02:59.417700Z

unfortunately I don't have clj cli right now since it requires higher version of java, I have to keep an old version in my machine since there are some of my apps that will break if I update to higher version

Dave Suico 2021-04-12T17:03:27.417900Z

One weird behavior is when I copy the jack in command and then it starts a new repl server and it works.

2021-04-12T17:04:29.418200Z

wait, if you don't have the clj cli, how is the deps.edn config doing anything ?

2021-04-12T17:05:27.418400Z

clj is the program that uses deps.edn to start a clojure process (or strictly speaking it's the line editing wrapper over the tool that does that)

Dave Suico 2021-04-12T17:08:20.418600Z

I believe Calva and Clojure extensions in VSCode does the heavy lifting. feels weird actually for me as a beginner

2021-04-12T17:10:33.418800Z

I would be very surprised if Calva / VSCode replicated or replaced the clj tool

2021-04-12T17:11:45.419Z

the strict definintion, minus the baggage that all mainstream usage of "dependency injection" carries in tooling / project flow

🙌 1
2021-04-12T17:12:40.419200Z

(eg. it's not config driven, its part of your normal code base)

Dave Suico 2021-04-12T17:13:26.419400Z

Yeah it's really awesome, btw i fixed it now. I was running the wrong command by connecting to an existing repl instead of running a fresh repl server by calva

2021-04-12T17:14:46.419700Z

I don't use the term when introducing those libs because people with java experience get all the wrong ideas

2021-04-12T17:19:31.419900Z

right on, makes total sense

pez 2021-04-12T17:34:17.420400Z

Calva jack-in will kill any process it has started and start a new one. Connect will just connect to an existing nREPL server.

pez 2021-04-12T17:36:19.420600Z

Guessing you are using Windows if you get away without the clj tool. To avoid all the different kinds of ways the clojure tools could be installed and need to be started on Windows, Calva bundles borkdude´s deps.clj.

piyer 2021-04-12T18:15:34.421800Z

is there a way to prevent lein search to not return snapshot versions?

Dave Suico 2021-04-12T18:41:40.422700Z

Hey guys, would I know if a variable is a vector or a list? How would I know their differences when I print it in the repl? thanks!

2021-04-12T18:43:02.424100Z

what's the general purpose way to apply a macro to a collection (in terms of an arity mismatch)? this question comes up with the macro or a lot, and the recommended approach is to use a different (non macro) predicate. but in general, could i use (eval (my-macro ~@args))`?

2021-04-12T18:48:14.424300Z

to your second question, they print with different data structure literals

2021-04-12T18:48:17.424500Z

(prn '(1 2 3)) ;; => prints (1 2 3)

  (prn [1 2 3]) ;; => prints [1 2 3]

❤️ 1
2021-04-12T18:49:51.425600Z

to your first question, you could use type

(type '(1 2 3))
;; => clojure.lang.PersistentList
(type [1 2 3])
;; => clojure.lang.PersistentVector

❤️ 1
2021-04-12T18:50:40.426500Z

the real answer is to stop writing macros

2021-04-12T18:50:53.426600Z

🙂

2021-04-12T18:51:05.426900Z

i'm trying to programmatically build up an s/or expression

2021-04-12T18:51:32.427500Z

some day we may get spec2 which is supposed to make that kind of thing easier

raspasov 2021-04-12T18:51:41.427800Z

Some list gotchas: ‘(1 2 3) (1 2 3) ss.react.state=> (map inc ‘(1 2 3)) (2 3 4) ss.react.state=> (list? (map inc ‘(1 2 3))) false ss.react.state=> (into ‘() (map inc ‘(1 2 3))) (4 3 2) ss.react.state=> (list? (into ‘() (map inc ’(1 2 3)))) true

❤️ 1
raspasov 2021-04-12T18:52:35.428800Z

To check if it’s a list or a vector, use vector? and/or list?

🎯 1
❤️ 1
raspasov 2021-04-12T18:52:56.429100Z

My advice? Stick to vectors 🙂

2021-04-12T18:53:07.429400Z

assuming you have a fairly static set of things you want to generate s/or expressions for, I would just codegen it

omendozar 2021-04-12T18:53:12.429600Z

Hi, how can I transform this vector of maps

[{:numero  "50588887766"
  :mensaje "Lorem ipsum dolor sit amet"}
 {:numero "50588997755"
  :mensaje "Lorem ipsum dolor sit amet"}]
Into this one:
[{:clMensajes
  {:numero    {:__value "50588887766"},
   :mensaje   {:__value "Lorem ipsum dolor sit amet"},
   :remitente {:__value "PLIM"}}}
 {:clMensajes
  {:numero    {:__value "50588997755"}
   :mensaje   {:__value "Lorem ipsum dolor sit amet"}
   :remitente {:__value "PLIM"}}}]
I tried with
(for [[k v] m] (assoc-in m [k] {:__value v}))
to solve one part but is not working as I expected

raspasov 2021-04-12T18:53:46.429700Z

[1 2 3] ss.react.state=> (map inc [1 2 3]) (2 3 4) ss.react.state=> (seq? (map inc [1 2 3])) true ss.react.state=> (vector? (map inc [1 2 3])) false ss.react.state=> (list? (map inc [1 2 3])) false ss.react.state=> (into [] (map inc [1 2 3])) [2 3 4] ss.react.state=> (vector? (into [] (map inc [1 2 3]))) true ss.react.state=>

2021-04-12T18:54:02.429900Z

run (do ~@(for [thing things] (make-or thing))) (but with a syntax quote in front) in the repl then copy and paste it back into the source file

2021-04-12T18:57:00.431500Z

that isn't valid(you have missing delimiters), so I don't think that is what you tried

2021-04-12T18:59:55.432500Z

you are splitting the map into a sequence of key value pairs, and then returning an updated version of the original map for each pair

2021-04-12T19:00:25.432900Z

so making (count m) copies of the original map

omendozar 2021-04-12T19:00:44.433Z

The truth is I have no idea how to solve it

2021-04-12T19:01:34.433300Z

right on

2021-04-12T19:02:00.433600Z

thank you!

Joseph Rollins 2021-04-12T19:03:50.436500Z

This goes most of the way:

(defn valuize [m] (reduce (fn [acc [k v]] (assoc acc k {:__value v})) {} m))

user=> (valuize (input 0))
{:numero {:__value "50588887766"}, :mensaje {:__value "Lorem ipsum dolor sit amet"}}

1
➕ 1
pez 2021-04-12T19:05:07.437700Z

@orlandomr27, since the output vector has as many items as the input vector, I think map is a good choice. So if you make a function f that can take one of those hashmaps and transform it into the new form, then (map f input-vector).

2021-04-12T19:06:34.437800Z

another way to express this, if you don't want to use reduce

(->> {:numero  "50588887766"
      :mensaje "Lorem ipsum dolor sit amet"}
     (map (fn [[k v]] {k {:__value v}}))
     (into {}))

1
2021-04-12T19:06:44.438Z

but i think reduce is the right tool

2021-04-12T19:07:36.438700Z

or maybe medley's map-kv https://weavejester.github.io/medley/medley.core.html#var-map-kv

Joseph Rollins 2021-04-12T19:08:17.438900Z

I knew there had to be a way to do this with into and transducers, but I'm still getting more comfortable with transducers

Dave Suico 2021-04-12T19:08:31.439100Z

Awesome! Thank you guys I appreciate you all!:thumbsup:

👍 1
👌 1
pez 2021-04-12T19:13:17.439500Z

I would define f something like so:

(defn f [x]
  (let [new-x (-> x
                  (assoc :numero {:__value (:numero x)})
                  (assoc :mensaje {:__value (:mensaje x)})
                  (assoc :remitente {:__value "PLIM"}))]
    {:clMensajes new-x}))

omendozar 2021-04-12T19:15:36.439700Z

Awesome! just what I needed. Thank you @pez

omendozar 2021-04-12T19:16:00.439900Z

Very nice. I got it! Thank you all!

👍 1
omendozar 2021-04-12T19:17:29.440100Z

So simple. I confess I tried this approach but I was missing the last exp.

pez 2021-04-12T19:19:07.440300Z

You are welcome!

piyer 2021-04-12T22:29:33.442Z

I am trying to add logging to my project, I couldn't find any documentation on where to add these config files: https://github.com/clojure/tools.logging#log4j2 any pointers?

seancorfield 2021-04-12T22:33:48.442800Z

@munichlinux “on your classpath” which generally means in the resources folder of your project if you have one (or else the src folder).

seancorfield 2021-04-12T22:35:28.444600Z

If you’re going down the log4j2 path — which is what we use at work — you’re also probably going to need bridge libraries to route all over logging through log4j2 and also the JVM option to tell tools.logging to use log4j2 in preference other logging libraries it finds on the classpath.

seancorfield 2021-04-12T22:35:58.444900Z

This is the JVM option: -Dclojure.tools.logging.factory=clojure.tools.logging.impl/log4j2-factory

piyer 2021-04-12T22:36:26.445200Z

got it. What should I name this config file?

seancorfield 2021-04-12T22:37:10.446100Z

Here are the bridge libraries (for you dependencies):

;; use log4j 2.x:
    org.apache.logging.log4j/log4j-api {:mvn/version "2.13.3"}
    ;; bridge into log4j:
    org.apache.logging.log4j/log4j-1.2-api {:mvn/version "2.13.3"}
    org.apache.logging.log4j/log4j-jcl {:mvn/version "2.13.3"}
    org.apache.logging.log4j/log4j-jul {:mvn/version "2.13.3"}
    org.apache.logging.log4j/log4j-slf4j-impl {:mvn/version "2.13.3"}
(that’s in deps.edn format but I expect you get the idea)

seancorfield 2021-04-12T22:37:54.446700Z

If you follow the link from c.t.l to log4j2's docs, you’ll see you have a lot of options for how you actually do the configuration.

piyer 2021-04-12T22:38:46.447600Z

got it. I thought, I can copy the config there and get it to work.

seancorfield 2021-04-12T22:38:55.448Z

We have a default log4j2.properties file — we went with the properties style config rather than XML, JSON, or YAML.

seancorfield 2021-04-12T22:39:35.449200Z

And then we have several alternative properties files that we select via environment variables or JVM options (properties) depending on what level/format of logging we want.

seancorfield 2021-04-12T22:39:46.449500Z

(Java logging is insanely complicated, unfortunately)

piyer 2021-04-12T22:39:59.449600Z

It is.

piyer 2021-04-12T22:40:33.450400Z

That make sense. so, I can copy and put that as logback.edn in my resource?

seancorfield 2021-04-12T22:40:42.450600Z

And it certainly does not help that the Apache docs page shows XML primarily.

seancorfield 2021-04-12T22:40:57.450900Z

What is logback.edn?

seancorfield 2021-04-12T22:41:45.451900Z

log4j2's configuration options are described here: https://logging.apache.org/log4j/2.x/manual/configuration.html

seancorfield 2021-04-12T22:42:03.452400Z

It explains how it finds configuration options/files.

seancorfield 2021-04-12T22:42:23.452700Z

As I said above: > We have a default `log4j2.properties` file — we went with the properties style config rather than XML, JSON, or YAML. That’s on our classpath.

piyer 2021-04-12T22:42:45.453100Z

I see.

piyer 2021-04-12T22:43:41.454Z

are ring logging compactable with log4j2? or should I use SLF4j?

seancorfield 2021-04-12T22:44:06.454100Z

@munichlinux Our default log4j2.properties file contains:

# QA/production normal mode, shows INFO and above, in plain text:
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT

appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{DEFAULT} %-5p [%c] - %X - %m%n

# We do not care about most of c3p0's INFO messages:
logger.c3p0.name = com.mchange.v2.c3p0
logger.c3p0.level = warn
logger.c3p0.appenderRef.stdout.ref = STDOUT

# We do not care about most of Redisson's INFO messages:
logger.redisson.name = org.redisson.connection
logger.redisson.level = warn
logger.redisson.appenderRef.stdout.ref = STDOUT
So we don’t get overwhelmed by c3p0 or redisson (although I think we’ve stopped using the latter and could remove that).

seancorfield 2021-04-12T22:44:52.455Z

You can use log4j2 and have all logging routed to it — that’s what the bridging libraries above are for. Most of the logging systems have similar bridging libraries.

👍 1
seancorfield 2021-04-12T22:45:09.455500Z

Not sure what you mean by “ring logging”.

piyer 2021-04-12T22:46:27.456500Z

I https://github.com/fzakaria/slf4j-timbre#slf4j-timbre that, Jetty speaks SLF4J

piyer 2021-04-12T22:46:46.456700Z

Thanks a lot for the example.

seancorfield 2021-04-12T22:48:31.457100Z

Lots of Java libraries choose different logging solutions. That’s why you need bridge libraries.

seancorfield 2021-04-12T22:49:15.457300Z

If you want to use log4j2, Jetty will also use it — via the log4j2 bridge for slf4j (I listed the bridge libraries above).

seancorfield 2021-04-12T22:49:43.457500Z

You want all four bridge libraries so that all the Java libraries your app ends up pulling it will all be routed to log4j2.

piyer 2021-04-12T22:49:57.457700Z

Thank you. I will try that.

seancorfield 2021-04-12T22:50:34.457900Z

That is how you control all the logging: so you have your configuration (for log4j2) and all the other logging libraries in your project route to log4j2 and obey that configuration.

seancorfield 2021-04-12T22:51:34.458100Z

This article might help you https://lambdaisland.com/blog/2020-06-12-logging-in-clojure-making-sense-of-the-mess (although it picks sl4j but I think log4j2 is a better choice).

seancorfield 2021-04-12T22:52:13.458400Z

Just don’t forget the JVM option -Dclojure.tools.logging.factory=clojure.tools.logging.impl/log4j2-factory when you are running your app!

piyer 2021-04-12T22:52:30.458600Z

Thank you, that is helpful.

seancorfield 2021-04-12T22:53:13.458800Z

And don’t use Timbre or slf4j-timbre. Stick with standard Java logging or you’ll just make life even more confusing.

piyer 2021-04-12T22:53:59.459Z

That is good to know.