This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-12-17
Channels
- # adventofcode (56)
- # announcements (1)
- # aws (6)
- # beginners (127)
- # bristol-clojurians (1)
- # calva (29)
- # cider (7)
- # clj-kondo (37)
- # cljdoc (20)
- # cljsrn (7)
- # clojure (159)
- # clojure-europe (67)
- # clojure-italy (23)
- # clojure-nl (4)
- # clojure-provo (3)
- # clojure-uk (18)
- # clojurescript (19)
- # code-reviews (59)
- # community-development (105)
- # conjure (6)
- # core-async (16)
- # core-logic (1)
- # cursive (21)
- # datomic (19)
- # defnpodcast (1)
- # emacs (8)
- # events (2)
- # fulcro (71)
- # graalvm (23)
- # jobs-discuss (1)
- # kaocha (5)
- # luminus (5)
- # meander (16)
- # nrepl (32)
- # off-topic (6)
- # pathom (159)
- # pedestal (3)
- # reagent (14)
- # reitit (8)
- # reveal (12)
- # rewrite-clj (9)
- # shadow-cljs (169)
- # spacemacs (16)
- # specter (2)
- # sql (19)
- # tools-deps (36)
- # vim (6)
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?)
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
The raw data for all years is linked at the bottom of that article. 2020 is https://www.surveymonkey.com/results/SM-CDBF7CYT7/
The totals shown in the table beneath the graph should indicate whether it was multiple-response (overlapping) or single-response I guess?
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.
This isn't detailed enough?
Looking at the numbers, they add up to more that the total respondents so it must be a multiple-response question.
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"
Yeah, I was a bit surprised nearly 60% are still using 8.
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).
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 🙂https://www.mail-archive.com/[email protected]/msg106499.html - I found someone else has this issue too
@U04V70XH6 I guess you are the one who can know the answer. It touch topic around https://github.com/seancorfield/depstar
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)
`
`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 updateBut 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
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
@U0WL6FA77 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.
https://github.com/arctype-co/log4j2-plugins-cache Do you know how to use this in deps.edn ?
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.
That's a Leiningen plugin. It cannot be used with deps.edn
.
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…
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.
Nope.
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.
As you can see, logging in Java is a giant mess.
I assume you'll be switching to a different logging library now?
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.
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
Because it would have to be at that exact same relative path in resources
and your :exclude
would still exclude it.
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 :)
(ns api.logs.google-jsonPayload
(:require [jsonista.core :as json]
[clojure.stacktrace :as stacktrace])
(:import (java.util.logging LogManager ConsoleHandler Level SimpleFormatter LogRecord)
( 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->
{: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->jsonPayload [^LogRecord record]
(let [thrown ^Throwable (.getThrown record)
thrown-map (getThrown record)
level (.getLevel record)
ex-info? (= clojure.lang.ExceptionInfo (class thrown))]
(cond->
{: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" "")
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->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)))))
`thrown-map
is an addition for me for now to debug logs 😛 😉 You can remove it, so then cut code by half.
camelCase
naming is not idiomatic Clojure 😛
Take a look at Throwable->map
(in clojure.core
) -- it would simplify your code.
Hello clojurians Anyone here using Clojure + gRPC ? Curious about Which libs do you use?
I haven't watched it myself but it sounds like it may be of use to you: https://www.youtube.com/watch?v=iyHvwkc6Wis
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?
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 :)
Not that I’m aware of
Usually this is done by executing code at the top-level. This would be executed on require.
(I just fixed a couple of problems with some lib that did this when trying to use it with GraalVM :))
Would you guys say it’s a bad practice for a lib to run some code at the top level?
I am providing a logger lib on top of timbre that meant to be used only internally
The logger lib exposes a (configure settings)
function
Before configure
is called, the behaviour of the lib is undefined
The problem is that settings
are usually read from a service
Yes: service means network call And sometimes, an app needs to log before reading from a service>
The current solution is that the app calls configure
twice
Even if it’s accumulative, calling configure twice is cumbersome
@dpsutton the logger could work before the network call as it can accept an empty map as settings
@dpsutton @U04V15CAJ let’s continue here
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
Correct. To be 100% precise: I need to initialize a logger with a map that is received from a netwok call
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?
@U04V15CAJ It’ CLJ. The logger settings come from the network
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?
I’m fine with blocking
What about logs that are sent before the request to the network is sent?
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
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
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.
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"))
It’s not a big issue that the logs that are sent between the two init-logger!
are not using the same settings
Nothing
It’s just that it’s cumbersome for the apps to have to call init-logger!
twice
I was thinking of calling (init-logger)
in the library code
you call init and it starts immediately with an empty config and does a network call, updating the logging settings when that resolves
The logger doesn’t know about the network call. It’s an app related stuff
There are dozens of apps
that use the same logger lib
My question is: is it a bad practice to call (init-logger!) inside the logger lib. @U04V15CAJ you wrote earlier that https://clojurians.slack.com/archives/C03S1KBA2/p1608223187132500 On what side of the “it depends” my use case fall?
You mean: it’s ok or not ok?
Why the fact that this is my own library matters?
because this would be crazy for something the general population used. but for an internal logging library its tied to your particular usecase
You mean that I hold both sides: the lib and the usage of the lib
Ok. Thank you guys
Enjoy your meeting @U04V15CAJ
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.
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?@U2FRKM4TW Yes, I tried it but the missing piece is how to structure the specs so that they are interpreted correctly.
The docs at https://github.com/clojure/tools.cli are very vague about that.
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?
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
.
@U2FRKM4TW I’m trying to write a CLI tool that accepts commands and subcommands as command line arguments, like eg: the git CLI
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.
Just a moment @ccidral. Let us complete the discussion
ok. let’s do it
Livestream happening now: https://www.youtube.com/watch?v=KES-lKTq-3M
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"}]}]
I would imagine it’s a combination of group-by
and map
but trying to avoid multiple traversals if possible
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.
no, and furthermore clojure cli doesn't do java compilation for you - there are extension tasks that can do it though
they are deps that add special tasks https://github.com/clojure/tools.deps.alpha/wiki/Tools
the only one I notice right away today that compiles java is mevyn, and it does that by delegating to mvn