Fork me on GitHub
#clojure
<
2019-12-15
>
kwladyka05:12:50

yes, now it is obviously 😉

kwladyka06:12:52

What do you use for tracing in Clojure? I am trying OpenCensus with stackdriver and I am trying to figure out how to trace all fn by automate. Well ok maybe have list of exluded functions. I mean I don’t want to wrap each function with my custom span name. I want it to be automated, but not sure how to achieve it in clever way in Clojure.

seancorfield06:12:19

Never needed to trace code much... What's the problem you're trying to solve?

kwladyka06:12:18

(with-open [span (.startScopedSpan (.spanBuilder tracer "foo"))]
    (println "foo")
    (dorun 5 (repeatedly #(println "hi")))
    (doall (map println [1 2 3]))
    (for [x (range 10)]
      (inc x)))
I don’t want to wrap each function with
(with-open [span (.startScopedSpan (.spanBuilder tracer "foo"))]

seancorfield06:12:51

That's just code. What problem are you trying to solve?

4
kwladyka06:12:03

> I don’t want to wrap each function with

seancorfield06:12:33

No, take a step back. That's an implementation issue. What overall problem are you trying to solve?

kwladyka06:12:27

At that moment I am learning how to do it right 🙂 Just for learning.

kwladyka06:12:42

On the end if I could use trace / logs / metric and read this data for 1 query and part of the trace it will make it super nice for debugging

seancorfield06:12:17

Seems a complicated way to debug code...

kwladyka06:12:58

unless time of response matter and you have to notice this time to fix it

kwladyka06:12:16

so you can easy find query which take 5 minutes intead of 1 and check it

kwladyka06:12:37

whatever, at that moment I just want to learn how to do it in Clojure in a simple way.

seancorfield06:12:33

Databases can log slow queries -- that's the right way to solve that problem.

seancorfield06:12:03

We use a combination of New Relic and DBA-specific tools for that.

kwladyka06:12:37

Why do you assume slowness is made only by DB?

kwladyka06:12:50

At work we use New Relic and trace everything. It is easy to find long responses and see why it is long.

seancorfield06:12:02

I don't. But you were the one that said you have a query that takes 5 minutes instead of one

kwladyka06:12:11

I wasn’t thinking about DB query

kwladyka06:12:17

http for example

seancorfield06:12:35

Tracing changes the performance of the code tho'. It changes what JIT can do. So in tracing the code, you inherently change what you are observing. Something to bear in mind.

☝️ 4
seancorfield06:12:57

New Relic should be able to trace your HTTP calls BTW -- it does for us.

kwladyka06:12:13

I know, but I am learning stackdriver for my own purpose. New Relic is unecessary expensive for my needs.

kulminaator13:12:48

you can also consider running probes to http endpoints from other providers, statuscake, copperegg, datadog

kulminaator13:12:39

and for local tracing of "stuff" people often employ structured logging + filebeat (or logstash) , ship it to a elasticsearch machine(s) and analyze the detailed info from there via tools like grafana or kibana

kulminaator13:12:09

i usually trace where i lost milliseconds, so it's interesting to see people chasing other units 😄

kulminaator14:12:31

there's also metricbeat which ships machine data to elasticsearch , so you can track other possible things that can make your applications slow

kulminaator14:12:38

e.g. your linux providers' automatic update cycles (ubuntu for example has decided that all the planet has to do maintenance and other daily cron stuff at 6:30 utc roughly ... which is a bit unpleasant if your hundreds of machines figure they have to do it all at almost once 😄)

kwladyka17:12:18

My purpose is to use something which I don’t have to maintain and invest my time into it. So all prometheus and similar stuff is not what I want.

kwladyka17:12:27

That is why I want to try stackdriver

kwladyka17:12:03

And I want to have logs / metric/ trace in one tool so I can easy select period of time to see what happen

kwladyka17:12:27

So how did you achieve tracing without wrap each function?

kulminaator12:12:13

i don't need to trace each function, i only trace things that do i/o and for places that may handle more than a reasonable amount of records i have statistics counters for both time and recourd counts processed.

kulminaator12:12:35

it's impossible to trace every function call if you run your code on several hundreds of machines 🙂

kulminaator12:12:00

and "i trace" -> i drop numbers into elastic and have the visualization + alerting tools hooked to it. i only work 8 hours a day 5 days a week, but the services run 365/24/7

kulminaator12:12:06

our usecases may be very different though 🙂

kulminaator12:12:45

but one thing is sure, if you start to trace every method call you will start to do different stuff than the jit does with the code when your traces are not there. so you are tracing "something else" than the trace-less code would do.

kwladyka12:12:42

> it’s impossible to trace every function call if you run your code on several hundreds of machines 🙂 We are doing this in work. It is possible 😉

kwladyka12:12:15

but what I am doing now is : I want to learn how to do it in right way for myself. I want to develop my skills in this topic.

kwladyka12:12:35

So I also want to answer myself: should I track all fn or only fn of my choice

kwladyka12:12:00

Tracing is something where I don’t have strong experience and opinions so far

kwladyka12:12:31

I would be happy to discuss about this topic further, even on screen share

kulminaator13:12:33

i dont think you understand , we are logging terabyte already at the i/o + stats logging level

kulminaator13:12:49

would you imagine how much we would log per month if we would trace every function call ? 🙂

kulminaator13:12:08

i couldn't possible afford the storage amount and the storage speed required to write so fast

kulminaator13:12:29

as said, it sounds like we have very different usecases 🙂

kwladyka13:12:56

I don’t know how much we store in work, but we have at least 400 servers. I don’t know more info at that moment.

kwladyka13:12:16

But my purpose it to learn this things for myself, so in very small scale.

kwladyka13:12:30

Mainly for my hobby projects and develop my skills.

kwladyka13:12:54

Do you store all query (tracing) to services or only 10% in production?

kwladyka13:12:53

I was reading it is not recommended to store 100% of samples. But my intuition says me it is the right way. Otherwise it doesn’t make sense.

kwladyka13:12:02

I would like to hear any tips / best practices from your experience how to do it right on production

kwladyka13:12:18

So far we have 1 point: do not trace all fn, but only this which you want

kulminaator13:12:18

i process payments. i have to keep all the logs. 🙂

kulminaator13:12:08

but yes, in other fields sampling is a very healthy choice 🙂

kulminaator13:12:32

i definitely recommend stats logging ... min max avg & percentiles

kulminaator13:12:36

for the places which concern you

kulminaator13:12:53

percentiles give you a really good idea how good-bad your service really is for the end user

kwladyka13:12:49

but here we are talking about metrics now. I am talking mainly about tracing.

kwladyka13:12:32

this is the are where I don’t have strong experience

kulminaator14:12:44

if your logging is reasonable enough it provides a soft trace. it's not method-by-method trace yes.

kulminaator14:12:48

as said before, i can not see the real idea behind stack tracing method by method (as it will hinder jit and show you a different result than what would take place without tracing)

kulminaator14:12:23

and it's only affordable if your one request has considerable money behind it to pay for all the tracing 🙂

kwladyka13:12:07

I was reading about tracking. I will start from selected function by my own and always sample. Probably I will make my own macro for this purpose. Something like defn-trace to replace defn and use name of the function for span name. How did you solve it?

kulminaator15:12:23

replacing defn would only replace it in your current namespace ... and if you actually want to track what's going down in the machinery you should check the jvm level rather (if your aim is to improve performance)

kulminaator15:12:39

clojutre had a talk which partially covers the topic

kwladyka06:12:46

> you inherently change what you are observing. The law of physic 😉

kwladyka06:12:12

So what you are trying to say is… what is exactly? When do you like to use tracing? And when not?

valerauko10:12:37

you really need to have the pedal to the metal for new relic's tracing to make a difference though

tianshu18:12:53

I have a question that maybe very stupid, clojure.spec encourage using qualified keyword, so there are

#:x{:a 1 :2}
But why there's no
#:x[:a :b]
These should be very common in s/keys.

Lennart Buit18:12:04

well there is (s/keys :req [:x/a :x/b])

didibus18:12:16

I think there is a jira about possibly adding it, if I remember corretly

tianshu18:12:14

I think there should be a design reason for this? I want to know that.

tianshu18:12:23

@lennart.buit I mean I can't write #:x[:a :b].

didibus18:12:41

I think its mostly how useful it would be. Most useful was for maps, so it was added to that

Lennart Buit18:12:35

yeah, I just don’t encounter that situation often, as in ‘Oh what would it be nice if I could ns-qualify this vector of keywords’. But I also find {:x/a 1 :x/b 2} more readable than #:x{:a 1 :b 2} :’).

Lennart Buit18:12:12

personal preference I guess

yuhan18:12:57

I recall one of the objections was that it would have to extend to sets by analogy: #:x#{:a} which looks pretty bad

didibus18:12:33

Ya, I was thinking where do you stop

didibus18:12:55

Do you add it to sets, lists, strings, etc.

didibus18:12:47

That said, not as bad with a space: #:x #{:a}

didibus18:12:37

I've found that I rarely use that notation for maps as well. I tend to almost never have homogeneous maps.

didibus18:12:31

I guess maybe I could still use it with merge, to combine groups of keys

didibus18:12:02

But a notation like say: {#:x [:a 10 :b 20] #:y [:a 50]} would be nice

didibus18:12:36

Which would become: {:x/a 10 :x/b 20 :y/a 50}

didibus18:12:04

Meh, this is good enough: (merge #:x {:a 10 :b 20} #:y {:a 50})

didibus18:12:38

I'll remember to use it

tianshu18:12:59

interesting thought

Alex Miller (Clojure team)19:12:16

The original design consideration was indeed, where do you stop. We decided to stop at maps.

Alex Miller (Clojure team)19:12:54

With the idea that we can always do more later

👍 8
frozenlock19:12:18

Is there a way to know which java modules are required to run a uberjared clojure program? I tried with jdeps --list-deps my-uberjar.jar but I get "java.lang.module.InvalidModuleDescriptorException". 😞

hindol19:12:26

How can I add missing functions to ClojureDocs? Currently I don't see thrown? and not-thrown? from clojure.test.

vemv19:12:15

they are documented in is, since they are not real functions or macros

andy.fingerhut19:12:39

Only ways I know would be to create your own modified web site, or create an issue on this Github repo requesting addition of things you would like to see. Click on the "open a ticket" link near the bottom left of the http://ClojureDocs.org home page.

andy.fingerhut20:12:16

Oh, yeah, or what @U45T93RA6 said about those particular things. They are custom only-inside-of-`is` macro invocation things.

hindol20:12:14

Oh, that makes sense. I really wish it was more discoverable though. Like, when searching for thrown?, I wish the UI would tell me to look at is.

👍 4
seancorfield20:12:44

I've been struggling with this a bit in expectations.clojure.test because it has several "special" forms that aren't actually functions or macros -- they're part of the DSL (`more`, more->, more-of, in, from-each). clojure.test is the "same" in terms of providing a DSL with elements that aren't functions/macros. One option I've considered is adding dummy versions of the "functions" with docstrings that explain what they are, but which throw exceptions if they're used incorrectly...

seancorfield20:12:24

The equivalent for clojure.test would be adding macros for thrown? etc -- but changing part of Clojure itself like that would need a lot of justification.

andy.fingerhut20:12:41

The Clojure core has a special case for (doc catch) that has its own custom doc string, IIRC, since it is a similar only-inside-of-`try` thing.

andy.fingerhut20:12:37

But such things are not very common, so there is no general way to add new things like that for doc to find that I know about, other than adding dummy doc-only Vars with the desired names.

seancorfield20:12:54

(monitor-exit try do if new recur set! . var quote throw monitor-enter def)

seancorfield20:12:02

From clojure.repl/special-doc-map

andy.fingerhut20:12:43

I haven't tried, but maybe one could consider a hack like alter-var-root! on that?

seancorfield20:12:00

Trying to find where catch is handling...

seancorfield20:12:32

Oh, duh, it's in the first line of doc itself!

seancorfield20:12:47

(if-let [special-name ('{& fn catch try finally try} name)]

andy.fingerhut20:12:50

Yeah, that one is pretty baked into the code.

jumpnbrownweasel22:12:46

I have created dummy defs for such things in my own code, but it was mainly so they wouldn't show up as unresolved symbols in the IDE.

potetm20:12:09

It’s honestly kind of hard for me to understand the suggestion. Map = “This is a set of names with values.” List = “These are unnamed, positional, possibly duplicate values.”

potetm20:12:04

It makes sense to allow you to document based on position. But integrating that into the reader is a little more dubious.

potetm20:12:21

Basically turns a list into a map.

seancorfield20:12:44

I've been struggling with this a bit in expectations.clojure.test because it has several "special" forms that aren't actually functions or macros -- they're part of the DSL (`more`, more->, more-of, in, from-each). clojure.test is the "same" in terms of providing a DSL with elements that aren't functions/macros. One option I've considered is adding dummy versions of the "functions" with docstrings that explain what they are, but which throw exceptions if they're used incorrectly...

alfredx21:12:45

A little bit surprised by below result:

(def f <)
(if (= f <)
  1
  2)
(case f
  < 1
  2)
; if yields 1, case yields 2

andy.fingerhut21:12:10

Compare to this for some insight, perhaps:

user=> (case < < 1 2)
2
user=> (case '< < 1 2)
1

andy.fingerhut21:12:50

I believe that the < as a case tag is the symbol < , not the value of the function named clojure.core/<

4
Cameron22:12:56

For the hell of it I went and instrumented case and went through its evaluation and while I can't assume 100% without looking through the java part of it, that appears to be the case 😄

andy.fingerhut21:12:33

In (case < < 1 2) the first < evaluates to the function named clojure.core/<

andy.fingerhut21:12:42

In the doc string for case , these sentences are relevant: "The test-constants are not evaluated. They must be compile-time  literals, and need not be quoted."

p-himik21:12:50

That's also why you can write expressions like (f x) in any test-constant which, confusingly enough, looks like a function call but is in fact just a list of two symbols.

andy.fingerhut21:12:22

case is a macro, and has complete control over which parts of its arguments are evaluated, and which are not.

👍 4
alfredx21:12:10

yeah, and how arguments are evaluated

pablore22:12:40

Question: If I have a namespace map (ie. #user{:id 1 :name "John"}), is there a way to treat like a normal map so I can do (get some-namespaced-user :id) ?

weavejester22:12:10

You can remove the namespaces from the keys of the maps:

weavejester22:12:00

(into {} (map (fn [[k v]] [(keyword (name k)) v]) m))

Lennart Buit22:12:03

(^If you do, watch out for ambiguity: {:user/id 1 :person/id 2} )

pablore22:12:47

Yeah thinking more about it, maybe it’s not worth it

potetm01:12:51

this is literally a waste of time, effort, and money

potetm01:12:59

only time it makes sense to remove the ns (or to rename keys at all) is when the data is leaving your program and your encoding doesn’t support namespacing

potetm01:12:42

(Yes, this is oversimplifying in the case of an existing codebase. But it would be wise to start there and carefully work your way elsewhere by listing out what you gain.)

potetm01:12:14

(No, I don’t know whether you’ve done that already. Perhaps I’m just preaching to the choir 😄 )

pablore22:12:04

instead of (get some-namespaced-user :user/id)

seancorfield23:12:01

Just get used to namespace-qualified keywords. Don't fight them.