Re: Socket servers, threads, and redirecting error output.

2021-01-06 Thread jum...@gmail.com
Thanks Sean, the stuff with file/line and thread name was helpful!
I updated my own logging-future macro(s) - here's an interesting version 
(logging-future+) that logs the client stacktrace at the time when it 
called future. 
I often find that much more useful that the stacktrace inside the thread 
itself, especially when using things like 
Hystrix: 
https://github.com/jumarko/clojure-experiments/blob/master/src/clojure_experiments/concurrency.clj#L31-L50
```
(defn logging-future+* [file line body]
  `(let [client-stack-trace# (Exception. "Client stack trace")]
 (future
   (try ~@body
(catch Throwable e#
  (log/error e# "Unhandled exception at:"
   ~file "line:" ~line
   "on thread:"
   (.getName (Thread/currentThread)))
  (log/error client-stack-trace# "client stack trace:"))
```


On Tuesday, 5 January 2021 at 17:51:42 UTC+1 Austin Haas wrote:

> Thank you, Sean. That is an excellent example.
>
> -austin
>
> On Sunday, January 3, 2021 at 12:48:55 PM UTC-8 Sean Corfield wrote:
>
>> Austin,
>>
>> You might find a macro like this helpful -- just use it directly instead 
>> of future. You can replace println with whatever sort of logging you want.
>>
>> (defmacro logged-future
>> "Given a body, execute it in a try/catch and log any errors."
>> [& body]
>> (let [line (:line (meta ))
>> file *file*]
>> `(future
>> (try
>> ~@body
>> (catch Throwable t#
>> (println t# "Unhandled exception at:"
>> ~file "line:" ~line
>> "on thread:"
>> (.getName (Thread/currentThread
>>
>>
>> On Sat, Jan 2, 2021 at 5:59 PM Austin Haas  wrote:
>>
>>> Ah, thanks for pointing that out. I must've overlooked your example, 
>>> because I'd already written off futures.
>>>
>>> It seems like what you are suggesting, catch and print, might be about 
>>> as good as I could hope for. If I don't want to block the main thread, then 
>>> I don't see what else I could possibly do but print the exception. I guess 
>>> I could store it somewhere, but in any case, I'd use this same pattern.
>>>
>>> Thanks, Justin.
>>> On Saturday, January 2, 2021 at 1:44:55 PM UTC-8 noise...@gmail.com 
>>> wrote:
>>>
 to be clear, in my second example you see the error from the future 
 without using deref

 good luck finding your solution

 On Sat, Jan 2, 2021 at 12:50 PM Austin Haas  
 wrote:

> Thank you very much for the explanation, Justin.
>
> I don't see how I can use futures, though, without blocking on the 
> main thread (to get the exception when it occurs). I'm spawning a 
> long-running process that never returns a value.
> On Saturday, January 2, 2021 at 12:43:14 AM UTC-8 noise...@gmail.com 
> wrote:
>
>> By the time the exception is caught, you are already outside the 
>> context of the Thread which the repl client is interacting with. The 
>> default exception handler has no information tying the executing thread 
>> to 
>> the repl process (not to mention the dynamic variables clojure is using 
>> to 
>> associate output from code your client runs with that socket connection).
>>
>> You probably don't want to rebind the root exception handler to show 
>> *all* exceptions to your client socket. Which means that you need to set 
>> up 
>> some soft of infrastructure connecting the information about the failed 
>> function to the socket you are listening to.
>>
>> I find "future" very convenient for this, it uses a pool which will 
>>  perform better than creating Threads ad-hoc, and will capture 
>> Exceptions 
>> and re-throw when you deref (of course, it's up to you to ensure you 
>> deref, 
>> or use try/catch and otherwise forward the failure information via the 
>> catch block). Also, it conveys dynamic bindings for things like 
>> clojure.core/*out* and clojure.core/*err* that java classes don't know 
>> about.
>>
>> (ins)user=> (def fut (future (throw (Exception. "oops"
>> #'user/fut
>> (ins)user=> @fut ; waits until deref to raise the error
>> Execution error at user/fn (REPL:11).
>> oops
>> (ins)user=> (def fut2 (future (try (throw (Exception. "oops")) (catch 
>> Exception e (println "wat\n" e) ; prints instead of raising
>> #'user/fut2
>> user=> wat
>>  #error {
>>  :cause oops
>>  :via
>>  [{:type java.lang.Exception
>>:message oops
>>:at [user$fn__165 invokeStatic NO_SOURCE_FILE 13]}]
>>  :trace
>>  [[user$fn__165 invokeStatic NO_SOURCE_FILE 13]
>>   [user$fn__165 invoke NO_SOURCE_FILE 13]
>>   [clojure.core$binding_conveyor_fn$fn__5754 invoke core.clj 2030]
>>   [clojure.lang.AFn call AFn.java 18]
>>   [java.util.concurrent.FutureTask run FutureTask.java 264]
>>   [java.util.concurrent.ThreadPoolExecutor runWorker 
>> ThreadPoolExecutor.java 1128]
>>   

Re: Socket servers, threads, and redirecting error output.

2021-01-05 Thread Austin Haas
Thank you, Sean. That is an excellent example.

-austin

On Sunday, January 3, 2021 at 12:48:55 PM UTC-8 Sean Corfield wrote:

> Austin,
>
> You might find a macro like this helpful -- just use it directly instead 
> of future. You can replace println with whatever sort of logging you want.
>
> (defmacro logged-future
> "Given a body, execute it in a try/catch and log any errors."
> [& body]
> (let [line (:line (meta ))
> file *file*]
> `(future
> (try
> ~@body
> (catch Throwable t#
> (println t# "Unhandled exception at:"
> ~file "line:" ~line
> "on thread:"
> (.getName (Thread/currentThread
>
>
> On Sat, Jan 2, 2021 at 5:59 PM Austin Haas  wrote:
>
>> Ah, thanks for pointing that out. I must've overlooked your example, 
>> because I'd already written off futures.
>>
>> It seems like what you are suggesting, catch and print, might be about as 
>> good as I could hope for. If I don't want to block the main thread, then I 
>> don't see what else I could possibly do but print the exception. I guess I 
>> could store it somewhere, but in any case, I'd use this same pattern.
>>
>> Thanks, Justin.
>> On Saturday, January 2, 2021 at 1:44:55 PM UTC-8 noise...@gmail.com 
>> wrote:
>>
>>> to be clear, in my second example you see the error from the future 
>>> without using deref
>>>
>>> good luck finding your solution
>>>
>>> On Sat, Jan 2, 2021 at 12:50 PM Austin Haas  
>>> wrote:
>>>
 Thank you very much for the explanation, Justin.

 I don't see how I can use futures, though, without blocking on the main 
 thread (to get the exception when it occurs). I'm spawning a long-running 
 process that never returns a value.
 On Saturday, January 2, 2021 at 12:43:14 AM UTC-8 noise...@gmail.com 
 wrote:

> By the time the exception is caught, you are already outside the 
> context of the Thread which the repl client is interacting with. The 
> default exception handler has no information tying the executing thread 
> to 
> the repl process (not to mention the dynamic variables clojure is using 
> to 
> associate output from code your client runs with that socket connection).
>
> You probably don't want to rebind the root exception handler to show 
> *all* exceptions to your client socket. Which means that you need to set 
> up 
> some soft of infrastructure connecting the information about the failed 
> function to the socket you are listening to.
>
> I find "future" very convenient for this, it uses a pool which will 
>  perform better than creating Threads ad-hoc, and will capture Exceptions 
> and re-throw when you deref (of course, it's up to you to ensure you 
> deref, 
> or use try/catch and otherwise forward the failure information via the 
> catch block). Also, it conveys dynamic bindings for things like 
> clojure.core/*out* and clojure.core/*err* that java classes don't know 
> about.
>
> (ins)user=> (def fut (future (throw (Exception. "oops"
> #'user/fut
> (ins)user=> @fut ; waits until deref to raise the error
> Execution error at user/fn (REPL:11).
> oops
> (ins)user=> (def fut2 (future (try (throw (Exception. "oops")) (catch 
> Exception e (println "wat\n" e) ; prints instead of raising
> #'user/fut2
> user=> wat
>  #error {
>  :cause oops
>  :via
>  [{:type java.lang.Exception
>:message oops
>:at [user$fn__165 invokeStatic NO_SOURCE_FILE 13]}]
>  :trace
>  [[user$fn__165 invokeStatic NO_SOURCE_FILE 13]
>   [user$fn__165 invoke NO_SOURCE_FILE 13]
>   [clojure.core$binding_conveyor_fn$fn__5754 invoke core.clj 2030]
>   [clojure.lang.AFn call AFn.java 18]
>   [java.util.concurrent.FutureTask run FutureTask.java 264]
>   [java.util.concurrent.ThreadPoolExecutor runWorker 
> ThreadPoolExecutor.java 1128]
>   [java.util.concurrent.ThreadPoolExecutor$Worker run 
> ThreadPoolExecutor.java 628]
>   [java.lang.Thread run Thread.java 834]]}
>
>
>
> On Thu, Dec 31, 2020 at 1:48 PM Austin Haas  
> wrote:
>
>>
>> Problem: When I connect to a socket server and create a thread, 
>> exceptions in the thread are printed in the server's process, not the 
>> client's. I'd like them to appear in the client's process, where the 
>> thread 
>> was created.
>>
>> (I'm using the term "process" very generally here, because I don't 
>> understand what is going on.)
>>
>> From searching, I understand that there are some other things at 
>> play, like System/err, but I don't understand what is happening or how I 
>> can work around it. Why does an exception thrown in the client process 
>> show 
>> in the client process, but an exception thrown in a thread created by 
>> the 
>> client process shows in the server process? Why doesn't binding *err* in 
>> a 
>> thread seem to have any 

Re: Socket servers, threads, and redirecting error output.

2021-01-03 Thread Sean Corfield
Austin,

You might find a macro like this helpful -- just use it directly instead of
future. You can replace println with whatever sort of logging you want.

(defmacro logged-future
"Given a body, execute it in a try/catch and log any errors."
[& body]
(let [line (:line (meta ))
file *file*]
`(future
(try
~@body
(catch Throwable t#
(println t# "Unhandled exception at:"
~file "line:" ~line
"on thread:"
(.getName (Thread/currentThread


On Sat, Jan 2, 2021 at 5:59 PM Austin Haas  wrote:

> Ah, thanks for pointing that out. I must've overlooked your example,
> because I'd already written off futures.
>
> It seems like what you are suggesting, catch and print, might be about as
> good as I could hope for. If I don't want to block the main thread, then I
> don't see what else I could possibly do but print the exception. I guess I
> could store it somewhere, but in any case, I'd use this same pattern.
>
> Thanks, Justin.
> On Saturday, January 2, 2021 at 1:44:55 PM UTC-8 noise...@gmail.com wrote:
>
>> to be clear, in my second example you see the error from the future
>> without using deref
>>
>> good luck finding your solution
>>
>> On Sat, Jan 2, 2021 at 12:50 PM Austin Haas  wrote:
>>
>>> Thank you very much for the explanation, Justin.
>>>
>>> I don't see how I can use futures, though, without blocking on the main
>>> thread (to get the exception when it occurs). I'm spawning a long-running
>>> process that never returns a value.
>>> On Saturday, January 2, 2021 at 12:43:14 AM UTC-8 noise...@gmail.com
>>> wrote:
>>>
 By the time the exception is caught, you are already outside the
 context of the Thread which the repl client is interacting with. The
 default exception handler has no information tying the executing thread to
 the repl process (not to mention the dynamic variables clojure is using to
 associate output from code your client runs with that socket connection).

 You probably don't want to rebind the root exception handler to show
 *all* exceptions to your client socket. Which means that you need to set up
 some soft of infrastructure connecting the information about the failed
 function to the socket you are listening to.

 I find "future" very convenient for this, it uses a pool which will
  perform better than creating Threads ad-hoc, and will capture Exceptions
 and re-throw when you deref (of course, it's up to you to ensure you deref,
 or use try/catch and otherwise forward the failure information via the
 catch block). Also, it conveys dynamic bindings for things like
 clojure.core/*out* and clojure.core/*err* that java classes don't know
 about.

 (ins)user=> (def fut (future (throw (Exception. "oops"
 #'user/fut
 (ins)user=> @fut ; waits until deref to raise the error
 Execution error at user/fn (REPL:11).
 oops
 (ins)user=> (def fut2 (future (try (throw (Exception. "oops")) (catch
 Exception e (println "wat\n" e) ; prints instead of raising
 #'user/fut2
 user=> wat
  #error {
  :cause oops
  :via
  [{:type java.lang.Exception
:message oops
:at [user$fn__165 invokeStatic NO_SOURCE_FILE 13]}]
  :trace
  [[user$fn__165 invokeStatic NO_SOURCE_FILE 13]
   [user$fn__165 invoke NO_SOURCE_FILE 13]
   [clojure.core$binding_conveyor_fn$fn__5754 invoke core.clj 2030]
   [clojure.lang.AFn call AFn.java 18]
   [java.util.concurrent.FutureTask run FutureTask.java 264]
   [java.util.concurrent.ThreadPoolExecutor runWorker
 ThreadPoolExecutor.java 1128]
   [java.util.concurrent.ThreadPoolExecutor$Worker run
 ThreadPoolExecutor.java 628]
   [java.lang.Thread run Thread.java 834]]}



 On Thu, Dec 31, 2020 at 1:48 PM Austin Haas 
 wrote:

>
> Problem: When I connect to a socket server and create a thread,
> exceptions in the thread are printed in the server's process, not the
> client's. I'd like them to appear in the client's process, where the 
> thread
> was created.
>
> (I'm using the term "process" very generally here, because I don't
> understand what is going on.)
>
> From searching, I understand that there are some other things at play,
> like System/err, but I don't understand what is happening or how I can 
> work
> around it. Why does an exception thrown in the client process show in the
> client process, but an exception thrown in a thread created by the client
> process shows in the server process? Why doesn't binding *err* in a thread
> seem to have any effect? Any suggestions or workarounds?
>
> I'm not using futures, because this is a long-running process that
> never returns a value.
>
> Example transcript:
>
> # Socker server
>
> (The only command entered is the first one, which begins with clj.
> Everything after "user=>" is due to the client below.)
>

Re: Socket servers, threads, and redirecting error output.

2021-01-02 Thread Austin Haas
Ah, thanks for pointing that out. I must've overlooked your example, 
because I'd already written off futures.

It seems like what you are suggesting, catch and print, might be about as 
good as I could hope for. If I don't want to block the main thread, then I 
don't see what else I could possibly do but print the exception. I guess I 
could store it somewhere, but in any case, I'd use this same pattern.

Thanks, Justin.
On Saturday, January 2, 2021 at 1:44:55 PM UTC-8 noise...@gmail.com wrote:

> to be clear, in my second example you see the error from the future 
> without using deref
>
> good luck finding your solution
>
> On Sat, Jan 2, 2021 at 12:50 PM Austin Haas  wrote:
>
>> Thank you very much for the explanation, Justin.
>>
>> I don't see how I can use futures, though, without blocking on the main 
>> thread (to get the exception when it occurs). I'm spawning a long-running 
>> process that never returns a value.
>> On Saturday, January 2, 2021 at 12:43:14 AM UTC-8 noise...@gmail.com 
>> wrote:
>>
>>> By the time the exception is caught, you are already outside the context 
>>> of the Thread which the repl client is interacting with. The default 
>>> exception handler has no information tying the executing thread to the repl 
>>> process (not to mention the dynamic variables clojure is using to associate 
>>> output from code your client runs with that socket connection).
>>>
>>> You probably don't want to rebind the root exception handler to show 
>>> *all* exceptions to your client socket. Which means that you need to set up 
>>> some soft of infrastructure connecting the information about the failed 
>>> function to the socket you are listening to.
>>>
>>> I find "future" very convenient for this, it uses a pool which will 
>>>  perform better than creating Threads ad-hoc, and will capture Exceptions 
>>> and re-throw when you deref (of course, it's up to you to ensure you deref, 
>>> or use try/catch and otherwise forward the failure information via the 
>>> catch block). Also, it conveys dynamic bindings for things like 
>>> clojure.core/*out* and clojure.core/*err* that java classes don't know 
>>> about.
>>>
>>> (ins)user=> (def fut (future (throw (Exception. "oops"
>>> #'user/fut
>>> (ins)user=> @fut ; waits until deref to raise the error
>>> Execution error at user/fn (REPL:11).
>>> oops
>>> (ins)user=> (def fut2 (future (try (throw (Exception. "oops")) (catch 
>>> Exception e (println "wat\n" e) ; prints instead of raising
>>> #'user/fut2
>>> user=> wat
>>>  #error {
>>>  :cause oops
>>>  :via
>>>  [{:type java.lang.Exception
>>>:message oops
>>>:at [user$fn__165 invokeStatic NO_SOURCE_FILE 13]}]
>>>  :trace
>>>  [[user$fn__165 invokeStatic NO_SOURCE_FILE 13]
>>>   [user$fn__165 invoke NO_SOURCE_FILE 13]
>>>   [clojure.core$binding_conveyor_fn$fn__5754 invoke core.clj 2030]
>>>   [clojure.lang.AFn call AFn.java 18]
>>>   [java.util.concurrent.FutureTask run FutureTask.java 264]
>>>   [java.util.concurrent.ThreadPoolExecutor runWorker 
>>> ThreadPoolExecutor.java 1128]
>>>   [java.util.concurrent.ThreadPoolExecutor$Worker run 
>>> ThreadPoolExecutor.java 628]
>>>   [java.lang.Thread run Thread.java 834]]}
>>>
>>>
>>>
>>> On Thu, Dec 31, 2020 at 1:48 PM Austin Haas  
>>> wrote:
>>>

 Problem: When I connect to a socket server and create a thread, 
 exceptions in the thread are printed in the server's process, not the 
 client's. I'd like them to appear in the client's process, where the 
 thread 
 was created.

 (I'm using the term "process" very generally here, because I don't 
 understand what is going on.)

 From searching, I understand that there are some other things at play, 
 like System/err, but I don't understand what is happening or how I can 
 work 
 around it. Why does an exception thrown in the client process show in the 
 client process, but an exception thrown in a thread created by the client 
 process shows in the server process? Why doesn't binding *err* in a thread 
 seem to have any effect? Any suggestions or workarounds?

 I'm not using futures, because this is a long-running process that 
 never returns a value.

 Example transcript:

 # Socker server

 (The only command entered is the first one, which begins with clj. 
 Everything after "user=>" is due to the client below.)

 $ clj -J-Dclojure.server.myrepl='{:port 
 ,:accept,clojure.core.server/repl}'
 Clojure 1.10.1
 user=> My second message.
 Exception in thread "Thread-0" clojure.lang.ExceptionInfo: My second 
 exception {}
 at user$eval5$fn__141.invoke(NO_SOURCE_FILE:7)
 at clojure.lang.AFn.run(AFn.java:22)
 at java.lang.Thread.run(Thread.java:745)
 Exception in thread "Thread-1" clojure.lang.ExceptionInfo: My third 
 exception {}
 at user$eval144$fn__145.invoke(NO_SOURCE_FILE:16)
 at 

Re: Socket servers, threads, and redirecting error output.

2021-01-02 Thread Justin Smith
to be clear, in my second example you see the error from the future without
using deref

good luck finding your solution

On Sat, Jan 2, 2021 at 12:50 PM Austin Haas  wrote:

> Thank you very much for the explanation, Justin.
>
> I don't see how I can use futures, though, without blocking on the main
> thread (to get the exception when it occurs). I'm spawning a long-running
> process that never returns a value.
> On Saturday, January 2, 2021 at 12:43:14 AM UTC-8 noise...@gmail.com
> wrote:
>
>> By the time the exception is caught, you are already outside the context
>> of the Thread which the repl client is interacting with. The default
>> exception handler has no information tying the executing thread to the repl
>> process (not to mention the dynamic variables clojure is using to associate
>> output from code your client runs with that socket connection).
>>
>> You probably don't want to rebind the root exception handler to show
>> *all* exceptions to your client socket. Which means that you need to set up
>> some soft of infrastructure connecting the information about the failed
>> function to the socket you are listening to.
>>
>> I find "future" very convenient for this, it uses a pool which will
>>  perform better than creating Threads ad-hoc, and will capture Exceptions
>> and re-throw when you deref (of course, it's up to you to ensure you deref,
>> or use try/catch and otherwise forward the failure information via the
>> catch block). Also, it conveys dynamic bindings for things like
>> clojure.core/*out* and clojure.core/*err* that java classes don't know
>> about.
>>
>> (ins)user=> (def fut (future (throw (Exception. "oops"
>> #'user/fut
>> (ins)user=> @fut ; waits until deref to raise the error
>> Execution error at user/fn (REPL:11).
>> oops
>> (ins)user=> (def fut2 (future (try (throw (Exception. "oops")) (catch
>> Exception e (println "wat\n" e) ; prints instead of raising
>> #'user/fut2
>> user=> wat
>>  #error {
>>  :cause oops
>>  :via
>>  [{:type java.lang.Exception
>>:message oops
>>:at [user$fn__165 invokeStatic NO_SOURCE_FILE 13]}]
>>  :trace
>>  [[user$fn__165 invokeStatic NO_SOURCE_FILE 13]
>>   [user$fn__165 invoke NO_SOURCE_FILE 13]
>>   [clojure.core$binding_conveyor_fn$fn__5754 invoke core.clj 2030]
>>   [clojure.lang.AFn call AFn.java 18]
>>   [java.util.concurrent.FutureTask run FutureTask.java 264]
>>   [java.util.concurrent.ThreadPoolExecutor runWorker
>> ThreadPoolExecutor.java 1128]
>>   [java.util.concurrent.ThreadPoolExecutor$Worker run
>> ThreadPoolExecutor.java 628]
>>   [java.lang.Thread run Thread.java 834]]}
>>
>>
>>
>> On Thu, Dec 31, 2020 at 1:48 PM Austin Haas  wrote:
>>
>>>
>>> Problem: When I connect to a socket server and create a thread,
>>> exceptions in the thread are printed in the server's process, not the
>>> client's. I'd like them to appear in the client's process, where the thread
>>> was created.
>>>
>>> (I'm using the term "process" very generally here, because I don't
>>> understand what is going on.)
>>>
>>> From searching, I understand that there are some other things at play,
>>> like System/err, but I don't understand what is happening or how I can work
>>> around it. Why does an exception thrown in the client process show in the
>>> client process, but an exception thrown in a thread created by the client
>>> process shows in the server process? Why doesn't binding *err* in a thread
>>> seem to have any effect? Any suggestions or workarounds?
>>>
>>> I'm not using futures, because this is a long-running process that never
>>> returns a value.
>>>
>>> Example transcript:
>>>
>>> # Socker server
>>>
>>> (The only command entered is the first one, which begins with clj.
>>> Everything after "user=>" is due to the client below.)
>>>
>>> $ clj -J-Dclojure.server.myrepl='{:port
>>> ,:accept,clojure.core.server/repl}'
>>> Clojure 1.10.1
>>> user=> My second message.
>>> Exception in thread "Thread-0" clojure.lang.ExceptionInfo: My second
>>> exception {}
>>> at user$eval5$fn__141.invoke(NO_SOURCE_FILE:7)
>>> at clojure.lang.AFn.run(AFn.java:22)
>>> at java.lang.Thread.run(Thread.java:745)
>>> Exception in thread "Thread-1" clojure.lang.ExceptionInfo: My third
>>> exception {}
>>> at user$eval144$fn__145.invoke(NO_SOURCE_FILE:16)
>>> at clojure.lang.AFn.run(AFn.java:22)
>>> at java.lang.Thread.run(Thread.java:745)
>>>
>>> # Client
>>>
>>> $ nc localhost 
>>> user=> (println "My first message.")
>>> My first message.
>>> nil
>>> user=> (throw (ex-info "My first exception." {}))
>>> Execution error (ExceptionInfo) at user/eval3 (REPL:2).
>>> My first exception.
>>> user=> (.start
>>>  (Thread.
>>>   (fn []
>>> (println "My second message.")
>>> (throw (ex-info "My second exception" {})
>>> nil
>>> user=> (.start
>>>  (Thread.
>>>   (let [out *out*
>>> err *err*]
>>> (fn []
>>>   (binding [*out* out
>>> *err* err]
>>> (println "My third message.")
>>>

Re: Socket servers, threads, and redirecting error output.

2021-01-02 Thread Austin Haas
Thank you very much for the explanation, Justin.

I don't see how I can use futures, though, without blocking on the main 
thread (to get the exception when it occurs). I'm spawning a long-running 
process that never returns a value.
On Saturday, January 2, 2021 at 12:43:14 AM UTC-8 noise...@gmail.com wrote:

> By the time the exception is caught, you are already outside the context 
> of the Thread which the repl client is interacting with. The default 
> exception handler has no information tying the executing thread to the repl 
> process (not to mention the dynamic variables clojure is using to associate 
> output from code your client runs with that socket connection).
>
> You probably don't want to rebind the root exception handler to show *all* 
> exceptions to your client socket. Which means that you need to set up some 
> soft of infrastructure connecting the information about the failed function 
> to the socket you are listening to.
>
> I find "future" very convenient for this, it uses a pool which will 
>  perform better than creating Threads ad-hoc, and will capture Exceptions 
> and re-throw when you deref (of course, it's up to you to ensure you deref, 
> or use try/catch and otherwise forward the failure information via the 
> catch block). Also, it conveys dynamic bindings for things like 
> clojure.core/*out* and clojure.core/*err* that java classes don't know 
> about.
>
> (ins)user=> (def fut (future (throw (Exception. "oops"
> #'user/fut
> (ins)user=> @fut ; waits until deref to raise the error
> Execution error at user/fn (REPL:11).
> oops
> (ins)user=> (def fut2 (future (try (throw (Exception. "oops")) (catch 
> Exception e (println "wat\n" e) ; prints instead of raising
> #'user/fut2
> user=> wat
>  #error {
>  :cause oops
>  :via
>  [{:type java.lang.Exception
>:message oops
>:at [user$fn__165 invokeStatic NO_SOURCE_FILE 13]}]
>  :trace
>  [[user$fn__165 invokeStatic NO_SOURCE_FILE 13]
>   [user$fn__165 invoke NO_SOURCE_FILE 13]
>   [clojure.core$binding_conveyor_fn$fn__5754 invoke core.clj 2030]
>   [clojure.lang.AFn call AFn.java 18]
>   [java.util.concurrent.FutureTask run FutureTask.java 264]
>   [java.util.concurrent.ThreadPoolExecutor runWorker 
> ThreadPoolExecutor.java 1128]
>   [java.util.concurrent.ThreadPoolExecutor$Worker run 
> ThreadPoolExecutor.java 628]
>   [java.lang.Thread run Thread.java 834]]}
>
>
>
> On Thu, Dec 31, 2020 at 1:48 PM Austin Haas  wrote:
>
>>
>> Problem: When I connect to a socket server and create a thread, 
>> exceptions in the thread are printed in the server's process, not the 
>> client's. I'd like them to appear in the client's process, where the thread 
>> was created.
>>
>> (I'm using the term "process" very generally here, because I don't 
>> understand what is going on.)
>>
>> From searching, I understand that there are some other things at play, 
>> like System/err, but I don't understand what is happening or how I can work 
>> around it. Why does an exception thrown in the client process show in the 
>> client process, but an exception thrown in a thread created by the client 
>> process shows in the server process? Why doesn't binding *err* in a thread 
>> seem to have any effect? Any suggestions or workarounds?
>>
>> I'm not using futures, because this is a long-running process that never 
>> returns a value.
>>
>> Example transcript:
>>
>> # Socker server
>>
>> (The only command entered is the first one, which begins with clj. 
>> Everything after "user=>" is due to the client below.)
>>
>> $ clj -J-Dclojure.server.myrepl='{:port 
>> ,:accept,clojure.core.server/repl}'
>> Clojure 1.10.1
>> user=> My second message.
>> Exception in thread "Thread-0" clojure.lang.ExceptionInfo: My second 
>> exception {}
>> at user$eval5$fn__141.invoke(NO_SOURCE_FILE:7)
>> at clojure.lang.AFn.run(AFn.java:22)
>> at java.lang.Thread.run(Thread.java:745)
>> Exception in thread "Thread-1" clojure.lang.ExceptionInfo: My third 
>> exception {}
>> at user$eval144$fn__145.invoke(NO_SOURCE_FILE:16)
>> at clojure.lang.AFn.run(AFn.java:22)
>> at java.lang.Thread.run(Thread.java:745)
>>
>> # Client
>>
>> $ nc localhost 
>> user=> (println "My first message.")
>> My first message.
>> nil
>> user=> (throw (ex-info "My first exception." {}))
>> Execution error (ExceptionInfo) at user/eval3 (REPL:2).
>> My first exception.
>> user=> (.start
>>  (Thread.
>>   (fn []
>> (println "My second message.")
>> (throw (ex-info "My second exception" {})
>> nil
>> user=> (.start
>>  (Thread.
>>   (let [out *out*
>> err *err*]
>> (fn []
>>   (binding [*out* out
>> *err* err]
>> (println "My third message.")
>> (throw (ex-info "My third exception" {})))
>> nil
>> My third message.
>>
>> Any clues would be appreciated. Thanks!
>>
>> -- 
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to 

Re: Socket servers, threads, and redirecting error output.

2021-01-02 Thread Justin Smith
By the time the exception is caught, you are already outside the context of
the Thread which the repl client is interacting with. The default exception
handler has no information tying the executing thread to the repl process
(not to mention the dynamic variables clojure is using to associate output
from code your client runs with that socket connection).

You probably don't want to rebind the root exception handler to show *all*
exceptions to your client socket. Which means that you need to set up some
soft of infrastructure connecting the information about the failed function
to the socket you are listening to.

I find "future" very convenient for this, it uses a pool which will
 perform better than creating Threads ad-hoc, and will capture Exceptions
and re-throw when you deref (of course, it's up to you to ensure you deref,
or use try/catch and otherwise forward the failure information via the
catch block). Also, it conveys dynamic bindings for things like
clojure.core/*out* and clojure.core/*err* that java classes don't know
about.

(ins)user=> (def fut (future (throw (Exception. "oops"
#'user/fut
(ins)user=> @fut ; waits until deref to raise the error
Execution error at user/fn (REPL:11).
oops
(ins)user=> (def fut2 (future (try (throw (Exception. "oops")) (catch
Exception e (println "wat\n" e) ; prints instead of raising
#'user/fut2
user=> wat
 #error {
 :cause oops
 :via
 [{:type java.lang.Exception
   :message oops
   :at [user$fn__165 invokeStatic NO_SOURCE_FILE 13]}]
 :trace
 [[user$fn__165 invokeStatic NO_SOURCE_FILE 13]
  [user$fn__165 invoke NO_SOURCE_FILE 13]
  [clojure.core$binding_conveyor_fn$fn__5754 invoke core.clj 2030]
  [clojure.lang.AFn call AFn.java 18]
  [java.util.concurrent.FutureTask run FutureTask.java 264]
  [java.util.concurrent.ThreadPoolExecutor runWorker
ThreadPoolExecutor.java 1128]
  [java.util.concurrent.ThreadPoolExecutor$Worker run
ThreadPoolExecutor.java 628]
  [java.lang.Thread run Thread.java 834]]}



On Thu, Dec 31, 2020 at 1:48 PM Austin Haas  wrote:

>
> Problem: When I connect to a socket server and create a thread, exceptions
> in the thread are printed in the server's process, not the client's. I'd
> like them to appear in the client's process, where the thread was created.
>
> (I'm using the term "process" very generally here, because I don't
> understand what is going on.)
>
> From searching, I understand that there are some other things at play,
> like System/err, but I don't understand what is happening or how I can work
> around it. Why does an exception thrown in the client process show in the
> client process, but an exception thrown in a thread created by the client
> process shows in the server process? Why doesn't binding *err* in a thread
> seem to have any effect? Any suggestions or workarounds?
>
> I'm not using futures, because this is a long-running process that never
> returns a value.
>
> Example transcript:
>
> # Socker server
>
> (The only command entered is the first one, which begins with clj.
> Everything after "user=>" is due to the client below.)
>
> $ clj -J-Dclojure.server.myrepl='{:port
> ,:accept,clojure.core.server/repl}'
> Clojure 1.10.1
> user=> My second message.
> Exception in thread "Thread-0" clojure.lang.ExceptionInfo: My second
> exception {}
> at user$eval5$fn__141.invoke(NO_SOURCE_FILE:7)
> at clojure.lang.AFn.run(AFn.java:22)
> at java.lang.Thread.run(Thread.java:745)
> Exception in thread "Thread-1" clojure.lang.ExceptionInfo: My third
> exception {}
> at user$eval144$fn__145.invoke(NO_SOURCE_FILE:16)
> at clojure.lang.AFn.run(AFn.java:22)
> at java.lang.Thread.run(Thread.java:745)
>
> # Client
>
> $ nc localhost 
> user=> (println "My first message.")
> My first message.
> nil
> user=> (throw (ex-info "My first exception." {}))
> Execution error (ExceptionInfo) at user/eval3 (REPL:2).
> My first exception.
> user=> (.start
>  (Thread.
>   (fn []
> (println "My second message.")
> (throw (ex-info "My second exception" {})
> nil
> user=> (.start
>  (Thread.
>   (let [out *out*
> err *err*]
> (fn []
>   (binding [*out* out
> *err* err]
> (println "My third message.")
> (throw (ex-info "My third exception" {})))
> nil
> My third message.
>
> Any clues would be appreciated. Thanks!
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to