Debugging concurrent programs with watchers

Watchers are not only good for logging and counting, but they are also useful for debugging. If an agent or reference is not getting updated the way we expect, we can temporarily add a watcher to track what's happening so we can see what's going on.

For this recipe, we'll continue the theme we've been working with for the last few recipes. This time, instead of counting the data, the watch function will print the change of state to the console.

Getting ready

We'll use the dependencies and requirements that we did in the Monitoring processing with watchers recipe. For this recipe, we just need to add a two more functions to them.

How to do it…

  1. The main difference from the last recipe is the watch function. Here's the new one:
    (defn debug-watch [watch-key watch-agent old-state new-state]
      (let [output (str watch-key
                        ": "
                        (pr-str old-state)
                        " => "
                        (pr-str new-state)
                        
    ewline)]
        (print output)))
  2. Of course, the main function that creates the system and runs it is different. I've highlighted the changes:
    (defn watch-debugging
      [input-file]
      (let [reader (agent
                     (seque
                       (mapcat
                         lazy-read-csv
                         input-files)))
            caster (agent nil)
            sink (agent [])
            counter (ref 0)
            done (ref false)]
        (add-watch caster :counter
                   (partial watch-caster counter))
        (add-watch caster :debug debug-watch)
        (send reader read-row caster sink done)
        (wait-for-it 250 done)
        {:results @sink
         :count-watcher @counter}))
  3. Now, when we run this processing system, we get a lot of debugging output:
    user=> (:count-watcher (watch-debugging (take 2 data-files)))
    :debug: [1990 "|0028670|" "|C00186288|" "|N00001783|" 1000 "06/15/1990" "|C4100|" "|24K|" "|D|" "|H6MI16034|"] => [1990 "|0028677|" "|C00186288|" "|N00007967|" 2000 "06/15/1990" "|C4100|" "|24K|" "|D|" "|H6WA05023|"]
    :debug: [1990 "|0028677|" "|C00186288|" "|N00007967|" 2000 "06/15/1990" "|C4100|" "|24K|" "|D|" "|H6WA05023|"] => [1990 "|0028687|" "|C00186288|" "|N00002497|" 500 "07/18/1990" "|C4100|" "|24K|" "|D|" "|H6SC03025|"]
    :debug: [1990 "|0028687|" "|C00186288|" "|N00002497|" 500 "07/18/1990" "|C4100|" "|24K|" "|D|" "|H6SC03025|"] => [1990 "|0028706|" "|C00186288|" "|N00007763|" 1000 "09/28/1990" "|C4100|" "|24K|" "|D|" "|S8OR00017|"]

There's more...

This is a good option to debug output if you need a lot of flexibility. However, if all you need is to track function calls, arguments, and outputs, clojure.tools.trace (https://github.com/clojure/tools.trace) is better. It does this and only this, and it's also less intrusive on your program's structure.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.34.62