clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
Alys Brooks 2020-12-17T01:00:15.115100Z

Is the raw data available to the latest State of Clojure anywhere? I'd like to get a sense of how many people target Java versions after 11, but the survey only has the overlapping data, so I can't tell if the 10 percentish on 12, 13, and 14 are mostly distinct or mostly overlap. (Alternatively, is there a survey or data source of what Java versions Clojure projects typically use other than the State of Clojure?)

Alys Brooks 2020-12-17T01:00:54.115600Z

FYI to people who haven't seen this before, it's a useful survey: https://clojure.org/news/2020/02/20/state-of-clojure-2020

seancorfield 2020-12-17T01:04:53.116100Z

The raw data for all years is linked at the bottom of that article. 2020 is https://www.surveymonkey.com/results/SM-CDBF7CYT7/

seancorfield 2020-12-17T01:06:36.117400Z

The totals shown in the table beneath the graph should indicate whether it was multiple-response (overlapping) or single-response I guess?

Alys Brooks 2020-12-17T01:09:31.118300Z

Oh sorry. I was referring to the raw response data, which was available for some of the older surveys, like 2014, not the full SurveyMonkey report.

dominicm 2020-12-18T22:06:00.227600Z

I have no impetus to upgrade.

dominicm 2020-12-18T22:06:10.227800Z

Just feels like risk, java 8 is great.

seancorfield 2020-12-18T22:47:37.230600Z

We were originally on Oracle's JDK 8 so we had a good incentive to switch to OpenJDK 8 (we went with AdoptOpenJDK's build), and once we had decided to stop running on an Oracle "official" build, it just made sense for us to start testing on newer JDKs and there were enough differences between 8 and 11 to be a worthwhile upgrade (memory management, GC reporting, etc). We dev/test against 14 and 15 but haven't felt the need to roll it out to QA/production at this point (although 15 has some nice GC enhancements).

seancorfield 2020-12-17T01:52:02.118600Z

This isn't detailed enough?

seancorfield 2020-12-17T01:54:43.119Z

Looking at the numbers, they add up to more that the total respondents so it must be a multiple-response question.

Alys Brooks 2020-12-17T03:33:46.119300Z

To be honest, it gives me enough of an idea in practice. I was just hoping to have a more definitive answer to "how many people use non-LTS versions"

seancorfield 2020-12-17T03:50:36.119500Z

Yeah, I was a bit surprised nearly 60% are still using 8.

kwladyka 2020-12-17T12:40:37.122100Z

RROR StatusLogger Unrecognized format specifier [d]
ERROR StatusLogger Unrecognized conversion specifier [d] starting at position 16 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [thread]
ERROR StatusLogger Unrecognized conversion specifier [thread] starting at position 25 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [level]
ERROR StatusLogger Unrecognized conversion specifier [level] starting at position 35 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [logger]
ERROR StatusLogger Unrecognized conversion specifier [logger] starting at position 47 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [msg]
ERROR StatusLogger Unrecognized conversion specifier [msg] starting at position 54 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [n]
ERROR StatusLogger Unrecognized conversion specifier [n] starting at position 56 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [d]
ERROR StatusLogger Unrecognized conversion specifier [d] starting at position 16 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [thread]
ERROR StatusLogger Unrecognized conversion specifier [thread] starting at position 25 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [level]
ERROR StatusLogger Unrecognized conversion specifier [level] starting at position 35 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [logger]
ERROR StatusLogger Unrecognized conversion specifier [logger] starting at position 47 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [msg]
ERROR StatusLogger Unrecognized conversion specifier [msg] starting at position 54 in conversion pattern.
ERROR StatusLogger Unrecognized format specifier [n]
ERROR StatusLogger Unrecognized conversion specifier [n] starting at position 56 in conversion pattern.
Do you know clean solution for this? log4j2 works in REPL, but not after make uberjar. I read this is about Log4j2Plugins.dat from plugins. I use deps.edn. I would never understand why logging in Java is so hard 🙂

kwladyka 2020-12-19T22:57:22.244700Z

FYI I found java.util.logging is the best for me to generate custom JSON and in general doing custom things with logs. This is the easiest and simplest way which I found. Here is my solution. Maybe you will find it useful one day :)

kwladyka 2020-12-19T22:57:22.244900Z

(ns api.logs.google-jsonPayload
  (:require [jsonista.core :as json]
            [clojure.stacktrace :as stacktrace])
  (:import (java.util.logging LogManager ConsoleHandler Level SimpleFormatter LogRecord)
           (<http://java.io|java.io> StringWriter PrintWriter)))

(def cloud-run? (some? (System/getenv "K_REVISION")))

(def line-separator (System/getProperty "line.separator"))

(def ANSI-colours {:reset "\u001B[0m"
                   :black "\u001B[30m"
                   :red "\u001B[31m"
                   :green "\u001B[32m"
                   :yellow "\u001B[33m"
                   :blue "\u001B[34m"
                   :purple "\u001B[35m"
                   :cyan "\u001B[36m"
                   :white "\u001B[37m"})

(defn getCause [^Throwable thrown]
  (when-let [cause ^Throwable (.getCause thrown)]
    {:class (.getClass cause)
     :message (.getMessage cause)
     :print-thrownable (with-out-str (clojure.stacktrace/print-throwable cause))
     :print-stack-trace (with-out-str (stacktrace/print-stack-trace cause))
     :print-cause-trace (with-out-str (clojure.stacktrace/print-cause-trace cause))}))

(defn getThrown [^LogRecord record]
  (when-let [thrown ^Throwable (.getThrown record)]
    (let [cause (getCause thrown)]
      (cond-&gt;
        {:class (.getClass thrown)
         :message (.getMessage thrown)
         :print-thrownable (with-out-str (clojure.stacktrace/print-throwable thrown))
         :print-stack-trace (with-out-str (stacktrace/print-stack-trace thrown))
         :print-cause-trace (with-out-str (clojure.stacktrace/print-cause-trace thrown))}
        cause (assoc :cause cause)))))

(defn record-&gt;jsonPayload [^LogRecord record]
  (let [thrown ^Throwable (.getThrown record)
        thrown-map (getThrown record)
        level (.getLevel record)
        ex-info? (= clojure.lang.ExceptionInfo (class thrown))]
    (cond-&gt;
      {:severity (.getName level)
       :logger-name (.getLoggerName record)
       :message (if thrown
                  (let [w (StringWriter.)]
                    (.printStackTrace thrown (PrintWriter. w))
                    (.toString w))
                  (.getMessage record))}
      ex-info? (assoc :ex-info (ex-data thrown))
      (= Level/SEVERE level) (assoc "@type" "<http://type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent|type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent>")
      thrown (assoc :logger-message (.getMessage record))
      thrown-map (assoc :thrown thrown-map))))

(def root-logger (when cloud-run?
                   (let [root-logger (.getLogger (LogManager/getLogManager) "")
                         google-jsonPayload (proxy [SimpleFormatter] []
                                              (^String format [^LogRecord record]
                                                (let [color (if (= Level/SEVERE (.getLevel record))
                                                              (:red ANSI-colours)
                                                              (:white ANSI-colours))]
                                                  (str
                                                    #_color
                                                    (json/write-value-as-string (record-&gt;jsonPayload record)
                                                                                #_(json/object-mapper {:pretty true}))
                                                    #_(:reset ANSI-colours)
                                                    line-separator))))
                         console-handler (doto (ConsoleHandler.)
                                           ;(.setUseParentHandlers false)
                                           (.setFormatter google-jsonPayload))]

                     (doseq [handler (.getHandlers root-logger)]
                       (.removeHandler root-logger handler))

                     (doto root-logger
                       (.setLevel Level/INFO)
                       (.addHandler console-handler)))))
`

kwladyka 2020-12-19T22:58:05.245100Z

thrown-map is an addition for me for now to debug logs 😛 😉 You can remove it, so then cut code by half.

seancorfield 2020-12-20T00:39:29.245600Z

camelCase naming is not idiomatic Clojure 😛

seancorfield 2020-12-20T00:40:28.245800Z

Take a look at Throwable-&gt;map (in clojure.core) -- it would simplify your code.

kwladyka 2020-12-20T00:47:40.246Z

yes, it is not final solution

kwladyka 2020-12-20T00:47:50.246200Z

thank you, I will that

kwladyka 2020-12-17T12:49:34.122300Z

https://www.mail-archive.com/clojure@googlegroups.com/msg106499.html - I found someone else has this issue too

kwladyka 2020-12-17T12:52:08.122700Z

@seancorfield I guess you are the one who can know the answer. It touch topic around https://github.com/seancorfield/depstar

kwladyka 2020-12-17T13:17:45.123Z

hmm I updated to new ver of depstar and I have different errors

kwladyka 2020-12-17T13:19:04.123200Z

1) option 1

:exec-args {             :aot true
                                 :main-class api.core}
Exception in thread "main" java.lang.IllegalArgumentException: No implementation of method: :get-logger of protocol: #'clojure.tools.logging.impl/LoggerFactory found for class: nil
	at clojure.core$_cache_protocol_fn.invokeStatic(core_deftype.clj:583)
	at clojure.core$_cache_protocol_fn.invoke(core_deftype.clj:575)
	at clojure.tools.logging.impl$fn__881$G__865__888.invoke(impl.clj:25)
	at api.core$_main.invokeStatic(core.clj:43)
	at api.core$_main.invoke(core.clj:42)
	at clojure.lang.AFn.applyToHelper(AFn.java:152)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at api.core.main(Unknown Source)
2) option 2
:main-opts ["-m" "hf.depstar.uberjar" "api.jar" "-C" "-m" "api.core"]
2020-12-17 13:17:06,472 main ERROR Unable to locate plugin type for JsonTemplateLayout
2020-12-17 13:17:06,473 main ERROR Unable to locate plugin type for JsonTemplateLayout
2020-12-17 13:17:06,497 main ERROR Unable to locate plugin for JsonTemplateLayout
2020-12-17 13:17:06,508 main ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.ConsoleAppender for element Console: java.lang.NullPointerException java.lang.NullPointerException
	at org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor.findNamedNode(PluginElementVisitor.java:104)
	at org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor.visit(PluginElementVisitor.java:88)
	at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.injectFields(PluginBuilder.java:185)
	at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:121)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:1002)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:942)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:934)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:552)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.initialize(AbstractConfiguration.java:241)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:288)
	at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:622)
	at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:695)
	at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:712)
	at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:267)
	at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:245)
	at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:47)
	at org.apache.logging.log4j.LogManager.getContext(LogManager.java:174)
	at clojure.tools.logging$eval136.invokeStatic(NO_SOURCE_FILE:0)
	at clojure.tools.logging$eval136.invoke(NO_SOURCE_FILE)
	at clojure.lang.Compiler.eval(Compiler.java:7177)
	at clojure.lang.Compiler.eval(Compiler.java:7132)
	at clojure.core$eval.invokeStatic(core.clj:3214)
	at clojure.core$eval.invoke(core.clj:3210)
	at clojure.tools.logging.impl$log4j2_factory.invokeStatic(impl.clj:183)
	at clojure.tools.logging.impl$log4j2_factory.invoke(impl.clj:178)
	at clojure.lang.Var.invoke(Var.java:380)
	at clojure.tools.logging$call_str.invokeStatic(logging.clj:316)
` `

kwladyka 2020-12-17T13:20:36.123500Z

all the time work in REPL

MatthewLisp 2020-12-17T16:18:40.127700Z

Hello clojurians Anyone here using Clojure + gRPC ? Curious about Which libs do you use?

p-himik 2020-12-20T20:40:41.274400Z

I haven't watched it myself but it sounds like it may be of use to you: https://www.youtube.com/watch?v=iyHvwkc6Wis

Yehonathan Sharvit 2020-12-17T16:34:33.129Z

Is there a way to build a Clojure library that automatically runs some code when it is in the list of dependencies of an application?

alexmiller 2020-12-17T16:35:29.129300Z

Not that I’m aware of

alexmiller 2020-12-17T16:35:42.129800Z

Seems like that would be a security issue :)

👍 4
borkdude 2020-12-17T16:36:18.130500Z

Usually this is done by executing code at the top-level. This would be executed on require.

borkdude 2020-12-17T16:37:03.131Z

(I just fixed a couple of problems with some lib that did this when trying to use it with GraalVM :))

Ben Sless 2020-12-17T16:38:08.131400Z

Is that something like emacsclient for nrepl?

borkdude 2020-12-17T16:38:42.131600Z

not really, it's just reply but then compiled to native

Yehonathan Sharvit 2020-12-17T16:39:40.132300Z

Would you guys say it’s a bad practice for a lib to run some code at the top level?

borkdude 2020-12-17T16:39:47.132500Z

It depends.

borkdude 2020-12-17T16:39:58.132700Z

What is your use case?

Yehonathan Sharvit 2020-12-17T16:41:20.133900Z

I am providing a logger lib on top of timbre that meant to be used only internally

Yehonathan Sharvit 2020-12-17T16:41:50.134500Z

The logger lib exposes a (configure settings) function

dpsutton 2020-12-17T16:42:17.135300Z

sounds like something someone should require and call some kind of init function

Yehonathan Sharvit 2020-12-17T16:42:24.135500Z

Before configure is called, the behaviour of the lib is undefined

Yehonathan Sharvit 2020-12-17T16:42:41.136Z

The problem is that settings are usually read from a service

borkdude 2020-12-17T16:42:43.136200Z

Being explicit about this like @dpsutton suggests is usually best

dpsutton 2020-12-17T16:43:01.137Z

read from a service means network call?

borkdude 2020-12-17T16:43:33.138Z

The place where you would do this is in your apps -main method for example

Yehonathan Sharvit 2020-12-17T16:43:35.138100Z

Yes: service means network call And sometimes, an app needs to log before reading from a service>

dpsutton 2020-12-17T16:43:55.139Z

and your logger is explicitly undefined behavior before this network call succeeds?

Yehonathan Sharvit 2020-12-17T16:43:56.139200Z

The current solution is that the app calls configure twice

borkdude 2020-12-17T16:44:05.139500Z

you could make configure idempotent

borkdude 2020-12-17T16:44:22.139700Z

or accumulative

Yehonathan Sharvit 2020-12-17T16:44:58.140500Z

Even if it’s accumulative, calling configure twice is cumbersome

Yehonathan Sharvit 2020-12-17T16:45:45.141500Z

@dpsutton the logger could work before the network call as it can accept an empty map as settings

CĂ©lio 2020-12-17T16:46:11.141700Z

Where can I find examples of tools.cli using the :in-order option? I’m having a hard time figuring out how to use it correctly.

Yehonathan Sharvit 2020-12-17T16:46:42.142100Z

Just a moment @ccidral. Let us complete the discussion

dpsutton 2020-12-17T16:46:56.142400Z

we shoudl use a thread. people don't have to wait to ask other questions

👍 2
Yehonathan Sharvit 2020-12-17T16:47:09.142600Z

ok. let’s do it

Yehonathan Sharvit 2020-12-17T16:47:37.143Z

Sorry @ccidral Keep going

😁 1
borkdude 2020-12-17T16:48:01.143400Z

Maybe you should use locking around your config... j/k ;)

Yehonathan Sharvit 2020-12-17T16:48:27.143600Z

@dpsutton @borkdude let’s continue here

dpsutton 2020-12-17T16:48:55.143800Z

i kinda want to recap as i don't quite have a good grasp. You need to initialize the logger with a network call. The logger's behavior before being initialized is undefined. You have seen instances of needing to log before initialization

dpsutton 2020-12-17T16:48:58.144Z

is all of this true?

Yehonathan Sharvit 2020-12-17T16:49:53.144200Z

Correct. To be 100% precise: I need to initialize a logger with a map that is received from a netwok call

borkdude 2020-12-17T16:50:42.144400Z

Is this in CLJS? Why would you do a network call to init a logger?

dpsutton 2020-12-17T16:51:03.144600Z

so what's your question? You could block awaiting the results of the network call. But fundamentally you need to handle failing network calls or slow calls. And does the app crash?

Yehonathan Sharvit 2020-12-17T16:51:45.144800Z

@borkdude It’ CLJ. The logger settings come from the network

dpsutton 2020-12-17T16:51:56.145Z

if not, and you allow work to happen while its waiting, do you buffer all of these logs and then send them through once the network has (hopefully) succeeded? Do you have a fallback and log it to a file and then push that file through whatever configured logging settings eventually return?

Yehonathan Sharvit 2020-12-17T16:52:20.145200Z

I’m fine with blocking

dpsutton 2020-12-17T16:52:27.145400Z

then do that

Yehonathan Sharvit 2020-12-17T16:52:59.145600Z

What about logs that are sent before the request to the network is sent?

dpsutton 2020-12-17T16:53:03.145800Z

have (my-logger/init!) make the network call and block. i'd do this before anyone needs to log otherwise you get into weird territory

dpsutton 2020-12-17T16:53:08.146Z

i wouldn't allow that

Yehonathan Sharvit 2020-12-17T16:53:17.146200Z

No!

dpsutton 2020-12-17T16:53:29.146400Z

but if you want to, then you need to figure out a way to store them and then log them in the future with the correct logging settings

dpsutton 2020-12-17T16:54:29.146600Z

but this could be a crazy hard problem. what do you do with those logs when the network request fails, or the program dies before initializing.

Yehonathan Sharvit 2020-12-17T16:54:57.146800Z

The way it works is

(init-logger! {})
(log "App is up")
(let [settings (read-from-network)]
    (init-logger! (:log-settings settings))
    (log "after settings are read"))
      

borkdude 2020-12-17T16:55:36.147Z

maybe not bring the app up before you have the required info to log

borkdude 2020-12-17T16:55:45.147200Z

that's probably what I would do

Yehonathan Sharvit 2020-12-17T16:55:56.147400Z

It’s not a big issue that the logs that are sent between the two init-logger! are not using the same settings

dpsutton 2020-12-17T16:56:19.147600Z

then what's wrong with what you've posted?

Yehonathan Sharvit 2020-12-17T16:56:26.147800Z

Nothing

borkdude 2020-12-17T16:56:46.148Z

you could queue the pending logs until you have the right info, another option

Yehonathan Sharvit 2020-12-17T16:56:51.148200Z

It’s just that it’s cumbersome for the apps to have to call init-logger! twice

dpsutton 2020-12-17T16:57:17.148400Z

why not have the init logging do that instead?

Yehonathan Sharvit 2020-12-17T16:57:34.148600Z

I was thinking of calling (init-logger) in the library code

dpsutton 2020-12-17T16:57:37.148800Z

you call init and it starts immediately with an empty config and does a network call, updating the logging settings when that resolves

Yehonathan Sharvit 2020-12-17T16:58:06.149Z

The logger doesn’t know about the network call. It’s an app related stuff

Yehonathan Sharvit 2020-12-17T16:58:18.149200Z

There are dozens of apps

Yehonathan Sharvit 2020-12-17T16:58:27.149400Z

that use the same logger lib

p-himik 2020-12-17T16:58:33.149600Z

Seems like you just have to pass it into cli/parse-opts, so something like:

(cli/parse-opts args specs :in-order true)
What have you tried that didn't work?

dpsutton 2020-12-17T16:58:48.149800Z

i don't think i know how to help then

Yehonathan Sharvit 2020-12-17T17:00:31.150Z

My question is: is it a bad practice to call (init-logger!) inside the logger lib. @borkdude you wrote earlier that https://clojurians.slack.com/archives/C03S1KBA2/p1608223187132500 On what side of the “it depends” my use case fall?

borkdude 2020-12-17T17:01:00.150300Z

considering that it is your own library, I wouldn't worry about it

borkdude 2020-12-17T17:01:23.150700Z

maybe document it in the README

Yehonathan Sharvit 2020-12-17T17:01:34.150900Z

You mean: it’s ok or not ok?

dpsutton 2020-12-17T17:01:53.151100Z

we're just people on the internet. but he's saying its ok

borkdude 2020-12-17T17:01:55.151300Z

up to you

Yehonathan Sharvit 2020-12-17T17:02:02.151500Z

Why the fact that this is my own library matters?

dpsutton 2020-12-17T17:02:28.151700Z

because this would be crazy for something the general population used. but for an internal logging library its tied to your particular usecase

dpsutton 2020-12-17T17:02:49.151900Z

this is 100% your code top to bottom. so do what you need to solve your goals

Yehonathan Sharvit 2020-12-17T17:02:51.152100Z

You mean that I hold both sides: the lib and the usage of the lib

dpsutton 2020-12-17T17:02:57.152300Z

yes

Yehonathan Sharvit 2020-12-17T17:02:58.152500Z

?

Yehonathan Sharvit 2020-12-17T17:03:06.152700Z

Ok. Thank you guys

borkdude 2020-12-17T17:03:09.152900Z

I'm in a meeting now, have to drop

Yehonathan Sharvit 2020-12-17T17:03:21.153200Z

Enjoy your meeting @borkdude

CĂ©lio 2020-12-17T17:23:06.154100Z

@p-himik Yes, I tried it but the missing piece is how to structure the specs so that they are interpreted correctly.

CĂ©lio 2020-12-17T17:23:29.154300Z

The docs at https://github.com/clojure/tools.cli are very vague about that.

p-himik 2020-12-17T17:40:18.154600Z

I don't think it has anything to do with the specs. As per the docs: "`cli/parse-opts` accepts an :in-order option that directs it to stop processing arguments at the first unrecognized token." What exactly are you trying to achieve?

p-himik 2020-12-17T17:41:59.154800Z

So e.g. if your specs are [["-p" "--port PORT"]] then cli/parse-opts will parse only -p and --port. Anything else will become arguments.

CĂ©lio 2020-12-17T18:02:58.155Z

@p-himik I’m trying to write a CLI tool that accepts commands and subcommands as command line arguments, like eg: the git CLI

p-himik 2020-12-17T18:05:59.155200Z

If my understanding is correct, then you have to create a global spec that does not include your subcommands and use it with :in-order true. Then, you create a spec for each subcommand and feed it to cli/parse-opts along with the arguments received from the cli/parse-opts called with the global spec.

CĂ©lio 2020-12-17T18:11:55.155600Z

Oh I see, that makes sense.

escherize 2020-12-17T18:16:27.156Z

Livestream happening now: https://www.youtube.com/watch?v=KES-lKTq-3M

benny 2020-12-17T18:59:27.158500Z

what’s the most efficient way to take a vector of maps like this

[{:a 1 :b "foo"} {:a 1 :b "bar"} {:a 2 :b "baz"}]
and turn it into this?
[{:a 1 :items [{:b "foo"} {:b "bar"}]} {:a 2 :items [{:b "baz"}]}]

benny 2020-12-17T19:00:42.158600Z

I would imagine it’s a combination of group-by and map but trying to avoid multiple traversals if possible

nwjsmith 2020-12-17T19:11:03.158800Z

You could probably rig up a single-traversal version with reduce

➕ 4
2020-12-17T19:16:28.159100Z

it depends on if things are sorted or not

2020-12-17T19:16:42.159300Z

but it is some kind of join

2020-12-17T19:16:47.159500Z

a self join

CĂ©lio 2020-12-17T19:27:53.159800Z

Thanks @p-himik I get it now.

👍 1
vemv 2020-12-17T21:22:05.160500Z

I think this is possible via Java static initializers The trick that java classes are present regardless whether you :import them. So you could invoke clojure from a java class static initializer. If such class is in the classpath, mission accomplished I doubt it's a pleasant "API" though :)

caumond 2020-12-17T21:47:00.160900Z

use the specter library?

kwladyka 2020-12-17T22:46:32.162Z

I ended with

2020-12-17 22:45:53,526 main ERROR Unable to locate plugin type for JsonTemplateLayout
2020-12-17 22:45:53,527 main ERROR Unable to locate plugin type for JsonTemplateLayout
2020-12-17 22:45:53,550 main ERROR Unable to locate plugin for JsonTemplateLayout
2020-12-17 22:45:53,560 main ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.ConsoleAppender for element Console: java.lang.NullPointerException java.lang.NullPointerException
	at org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisito
after depstar update

kwladyka 2020-12-17T22:49:19.162200Z

But I see this in verbose mode /Users/kwladyka/.m2/repository/org/apache/logging/log4j/log4j-layout-template-json/2.14.0/log4j-layout-template-json-2.14.0.jar so it should be there

kwladyka 2020-12-17T22:50:13.162400Z

and this Found META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat in multiple dependencies, selecting the largest maybe this is the reason

seancorfield 2020-12-17T22:54:16.162600Z

@kwladyka That message is expected, when multiple Log4j2Plugins.dat files are provided in dependencies. Selecting the largest is usually the correct strategy but perhaps you have an unusual library on your classpath that is pulling in a larger version that does not include the default plugins cache. If your project is on GitHub, I can take a look. If not, I can't debug it so I can't help you.

kwladyka 2020-12-17T22:55:08.162800Z

https://github.com/arctype-co/log4j2-plugins-cache Do you know how to use this in deps.edn ?

seancorfield 2020-12-17T22:55:15.163200Z

This is a design problem with log4j2 that people have complained about for years and the maintainers have agreed it's a design problem and said they would change how it works in 3.0 but we have no idea when that will be release.

seancorfield 2020-12-17T22:55:44.163400Z

That's a Leiningen plugin. It cannot be used with deps.edn.

kwladyka 2020-12-17T22:56:23.163600Z

yes
 just thinking if not log4j2 and choose something else. I am trying whole week to make a simple JSON log output in custom format


kwladyka 2020-12-17T22:56:31.163800Z

for google cloud jsonPayload logging

seancorfield 2020-12-17T22:57:41.164Z

When I write depstar 2.0, I will be able to have external dependencies and I can address the log4j2 problem using similar code. But for now depstar cannot have external dependencies.

❀ 1
kwladyka 2020-12-17T22:58:23.164200Z

Is any way to choose Log4j2Plugins.dat source?

seancorfield 2020-12-17T22:58:34.164500Z

Nope.

kwladyka 2020-12-17T22:58:49.164700Z

ok, this already help with decision 🙂

kwladyka 2020-12-17T22:58:56.164900Z

thank you

seancorfield 2020-12-17T22:59:19.165100Z

As I said, if your project is on GitHub, I can take a look at it and see what might improve the log4j2 file selection.

kwladyka 2020-12-17T22:59:37.165300Z

it is not

seancorfield 2020-12-17T22:59:46.165500Z

As you can see, logging in Java is a giant mess.

kwladyka 2020-12-17T23:00:15.165700Z

yes, I really hate logging in Java.

kwladyka 2020-12-17T23:00:37.165900Z

Java is the worst part of Clojure 😉

seancorfield 2020-12-17T23:02:25.166300Z

I assume you'll be switching to a different logging library now?

kwladyka 2020-12-17T23:03:21.166500Z

I guess logback as blind choice 🙂

kwladyka 2020-12-17T23:05:21.167300Z

I will see the difference with log4j2. I have never do so deep logging things.

kwladyka 2020-12-17T23:08:35.167500Z

hmm maybe I can also try to copy Log4j2Plugins.dat to jar as an extra command somehow form the right place if this will even work. I use only 1 plugin at once, so in this specific case it should.

kwladyka 2020-12-17T23:21:56.168200Z

oh logback is not supported by clojure.tools.logging, I have bad luck today

bringe 2020-12-17T23:30:22.169900Z

Hello. Is there a way to include java source paths using clojure cli? Something like :java-source-paths for lein? It tried adding the path to :paths in deps.edn, but that didn't seem to work.

2020-12-17T23:33:46.170500Z

no, and furthermore clojure cli doesn't do java compilation for you - there are extension tasks that can do it though

bringe 2020-12-17T23:35:12.171100Z

Oh I see, thanks for the info

bringe 2020-12-17T23:37:11.171400Z

Could you elaborate on "extension tasks?" I'm unfamiliar

2020-12-17T23:47:36.172700Z

they are deps that add special tasks https://github.com/clojure/tools.deps.alpha/wiki/Tools

2020-12-17T23:49:50.172900Z

the only one I notice right away today that compiles java is mevyn, and it does that by delegating to mvn

kwladyka 2020-12-17T23:58:42.174200Z

I had hope to add :exclude ["META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat"] and add this file into resources but it doesn’t work with the same exception