A simple, fast, monitoring profiler for Clojure/Script. Usage: wrap+name interesting body exprs with the `p` macro. Then activate profiling of these wrapped exprs using the `profiled` or `profile` macros: (profiled {} (p :my-fn (my-fn))) ; Returns [<body-result> <?pstats>] (profile {} (p :my-fn (my-fn))) ; Returns <body-result> and dispatches a ; profiling signal (map) to all registered handlers. Provides extensive facilities for compile-time elision and runtime filtering. See the relevant docstrings for more info: `p`, `profiled`, `profile`, `add-handler!`, etc. (p [opts & body] [id & body]) ; e.g. `(p ::my-id (do-work))` (profiled [opts & body]) ; e.g. `(profiled {:level :info} (my-fn))` (profile [opts & body]) ; e.g. `(profiled {:level :info} (my-fn))` (add-handler! [handler-id handler-fn dispatch-opts]) How/where to use this library: Tufte profiling is highly optimized: even without elision, you can usually leave profiling active in production (e.g. for sampled profiling, or to detect unusual performance behaviour). Tufte's `pstats` data is well suited to programmatic monitoring.
Optional context (state) attached to all signals. Value may be any type, but is usually nil or a map. Default (root) value is nil. Useful for dynamically attaching arbitrary app-level state to signals. Re/bind dynamic value using `with-ctx`, `with-ctx+`, or `binding`. Modify root (default) value using `set-ctx!`. As with all dynamic Clojure vars, "binding conveyance" applies when using futures, agents, etc. Tips: - Value may be (or may contain) an atom if you want mutable semantics. - Value may be of form {<scope-id> <data>} for custom scoping, etc. - Use `get-env` to set default (root) value based on environmental config.
Optional transform (fn [signal]) => ?modified-signal to apply to all signals. When transform returns nil, skips all handlers. Default (root) value is nil. Useful for dynamically filtering and/or modifying signals by signal data/content/etc. Re/bind dynamic value using `with-xfn`, `with-xfn+`, `binding`. Modify root (default) value using `set-xfn!`. As with all dynamic Clojure vars, "binding conveyance" applies when using futures, agents, etc. Examples: ;; Filter all signals by returning nil: (t/set-xfn! (fn [signal] (when-not (:skip-me? signal) signal))) ;; Remove key/s from all signals: (t/set-xfn! (fn [signal] (dissoc signal :unwanted-key1 ...))) ;; Remove key/s from signals to specific handler: (t/add-handler! ::my-handler my-handler {:xfn (fn [signal] (dissoc signal :unwanted-key1 ...))}) ;; Dynamic transform for specific signal/s: (binding [*xfn* (fn [signal] ...)] (...)) Tips: - Compose multiple transform fns together with `comp-xfn`. - Use `get-env` to set default (root) value based on environmental config.
(add-handler! handler-id handler-fn)
(add-handler! handler-id handler-fn dispatch-opts)
Registers given signal handler and returns {<handler-id> {:keys [dispatch-opts handler-fn]}} for all handlers now registered. If an old handler already existed under the same id, stop it. `handler-fn` should be a fn of exactly 2 arities: [signal] ; Single argument Called asynchronously or synchronously (depending on dispatch options) to do something useful with the given signal. Example actions: Save data to disk or db, `tap>`, log, `put!` to an appropriate `core.async` channel, filter, aggregate, use for a realtime analytics dashboard, examine for outliers or unexpected data, etc. [] ; No arguments Called exactly once when stopping handler to provide an opportunity for handler to flush buffers, close files, etc. May just noop. NB you should always call `stop-handlers!` somewhere appropriate - usually near the end of your `-main` or shutdown procedure, AFTER all other code has completed that could create signals. See `help:handler-dispatch-options` for handler filters, etc.
(call-on-shutdown! f)
Registers given nullary fn as a JVM shutdown hook. (f) will be called sometime during shutdown. While running, it will attempt to block shutdown.
(capture-time! id nano-secs-elapsed)
(capture-time! pdata id nano-secs-elapsed)
Low-level primitive for advanced users. Useful when tracking time across thread boundaries and/or for async jobs / callbacks / etc. See `new-pdata` for more info on low-level primitives. See also `capture-time!*`.
(capture-time!* id nano-secs-elapsed)
(capture-time!* pdata id nano-secs-elapsed)
Like `capture-time!` but a function and does not collect callsite location info.
(comp-xfn fs)
(comp-xfn f1 f2)
(comp-xfn f1 f2 f3)
(comp-xfn f1 f2 f3 & fs)
Returns a composite unary signal transform fn (xfn). Like `core/comp` but takes only unary fns, applies functions left->right(!), and composite immediately returns nil if any given fn returns nil.
(defnp name doc-string? attr-map? [params*] prepost-map? body)
(defnp name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?)
Like `defn` but wraps fn bodies with `p` macro.
(defnp- name doc-string? attr-map? [params*] prepost-map? body)
(defnp- name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?)
Like `defn-` but wraps fn bodies with `p` macro.
(fnp name? ([params*] prepost-map? body) +)
(fnp name? [params*] prepost-map? body)
Like `fn` but wraps fn bodies with `p` macro.
(format-grouped-pstats m)
(format-grouped-pstats m
{:keys [group-sort-fn format-pstats-opts]
:or {group-sort-fn (fn [m]
(get-in m [:clock :total] 0))}})
Alpha, subject to change. Takes a map of {<profiling-id> <pstats>} and formats a combined output string using `format-pstats`. See also example Clj project.
(format-id-abbr-fn)
(format-id-abbr-fn n-full)
Returns a cached (fn [id]) => abbreviated id with at most `n-full` unabbreviated namespace parts. Example: ((format-id-abbr 0) :foo.bar/baz) => :f.b/baz ((format-id-abbr 1) 'foo.bar/baz) => 'f.bar/baz ((format-id-abbr 2) "foo.bar/baz") => "foo.bar/baz"
(format-pstats ps)
(format-pstats ps
{:keys [incl-newline? columns sort-fn format-id-fn max-id-width]
:as opts
:or {incl-newline? true
columns default-format-columns
sort-fn (fn [ss] (get (enc/force-ref ss) :sum))
format-id-fn (fn [id] (str id))}})
Formats given `pstats` to a string table. Accounted < Clock => Some work was done that wasn't tracked by any `p` forms. Accounted > Clock => Nested `p` forms, and/or parallel threads. Options include: `:incl-newline?` - Include terminating system newline? (default true) `:columns` ------- Default [:n :min #_:p25 :p50 #_:p75 :p90 :p95 :p99 :max :mean :mad :clock :sum]
(format-signal-fn)
(format-signal-fn
{:keys [incl-newline? format-inst-fn format-pstats-opts incl-keys]
:or {incl-newline? true format-inst-fn (enc/format-inst-fn)}})
Alpha, subject to change. Returns a (fn format [signal]) that: - Takes a Tufte profiling signal (map). - Returns a human-readable signal string. Options: `:incl-newline?` ------ Include terminating system newline? (default true) `:format-inst-fn` ----- (fn format [instant]) => string (default ISO8601) `:format-pstats-opts` - Opts map provided to `format-pstats` (default nil) `:incl-keys` ---------- Subset of profiling signal keys to retain from those otherwise excluded by default: #{:host :thread}
(get-env {:keys [as default return] :or {as :str return :value}} spec)
Flexible cross-platform environmental value util. Given a compile-time id (keyword/string) or vector of desc-priority ids, parse and return the first of the following that exists, or nil: 1. JVM property value for id 2. Environment variable value for id 3. Classpath resource content for id Ids may include optional platform tag for auto replacement, e.g.: `<.platform>` -> ".clj", ".cljs", or nil Clj/s: if resulting value is a single symbol, it will be evaluated. Cljs: resulting value will be embedded in code during macro expansion! Options: `:as` - Parse encountered value as given type ∈ #{:str :bool :edn} (default `:str`). `:default` - Fallback to return unparsed if no value found during search (default `nil`). `:return` - Return type ∈ #{:value :map :explain} (default `:value`). Use `:explain` to verify/debug, handy for tests/REPL! Example: (get-env {:as :edn} [:my-app/id1<.platform> :my-app/id2]) will parse and return the first of the following that exists, or nil: id1 with platform: `my-app.id1.clj` JVM property value `MY_APP_id1_CLJ` environment variable value `my-app.id1.clj` classpath resource content id1 without platform: `my-app.id1` JVM property value `MY_APP_id1` environment variable value `my-app.id1` classpath resource content id2 with platform: ... id2 without platform: ...
(get-handlers)
Returns ?{<handler-id> {:keys [dispatch-opts handler-fn handler-stats_]}} for all registered signal handlers.
(get-handlers-stats)
Alpha, subject to change. Returns ?{<handler-id> {:keys [handling-nsecs counts]}} for all registered signal handlers that have the `:track-stats?` dispatch option enabled (it is by default). Stats include: `:handling-nsecs` - Summary stats of nanosecond handling times, keys: `:min` - Minimum handling time `:max` - Maximum handling time `:mean` - Arithmetic mean handling time `:mad` - Mean absolute deviation of handling time (measure of dispersion) `:var` - Variance of handling time (measure of dispersion) `:p50` - 50th percentile of handling time (50% of times <= this) `:p90` - 90th percentile of handling time (90% of times <= this) `:p99` - 99th percentile of handling time `:last` - Most recent handling time ... `:counts` - Integer counts for handler outcomes, keys (chronologically): `:dropped` - Noop handler calls due to stopped handler `:back-pressure` - Handler calls that experienced (async) back-pressure (possible noop, depending on back-pressure mode) `:sampled` - Noop handler calls due to sample rate `:filtered` - Noop handler calls due to kind/ns/id/level/when filtering `:rate-limited` - Noop handler calls due to rate limit `:disallowed` - Noop handler calls due to sampling/filtering/rate-limiting `:allowed` - Other handler calls (no sampling/filtering/rate-limiting) `:suppressed` - Noop handler calls due to nil transform (xfn) result `:handled` - Handler calls that completed successfully `:errors` - Handler calls that threw an error Note that for performance reasons returned counts are not mutually atomic, e.g. `:sampled` count may be incremented before `:disallowed` count is. Useful for understanding/debugging how your handlers behave in practice, especially when they're under stress (high-volumes, etc.). Handler stats are tracked from the time each handler is last registered (e.g. with an `add-handler!` call).
(get-min-levels)
(get-min-levels ns)
Returns current ?{:keys [compile-time runtime]} minimum call levels for given/current namespace.
(handler:accumulating sacc)
Alpha, subject to change. Returns a signal handler that: - Takes a Tufte profiling signal (map). - Merges the signal's `pstats` into the given accumulator. See `stats-accumulator` for more info.
(handler:console)
(handler:console {:keys [stream output-fn]
:or {stream :out output-fn (format-signal-fn)}})
Alpha, subject to change. Returns a signal handler that: - Takes a Tufte profiling signal (map). - Writes the signal as a string to specified stream. A general-purpose `println`-style handler that's well suited for outputting signals as human or machine-readable (edn, JSON) strings. Options: `:output-fn` - (fn [signal]) => string, see `format-signal-fn`. `:stream` ---- `java.io.writer` (default `*out*`).
(handler:console)
(handler:console {:keys [output-fn] :or {output-fn (format-signal-fn)}})
Alpha, subject to change. If `js/console` exists, returns a signal handler that: - Takes a Tufte profiling signal (map). - Writes the signal as a string to JavaScript console. A general-purpose `println`-style handler that's well suited for outputting signals as human or machine-readable (edn, JSON) strings. Options: `:output-fn` - (fn [signal]) => string, see `format-signal-fn`.
Tufte supports extensive environmental config via JVM properties, environment variables, or classpath resources. Environmental filter config includes: 1. Minimum level (see signal `:level`): a. JVM property: `taoensso.tufte.rt-min-level` b. Env variable: `TAOENSSO_TUFTE_RT_MIN_LEVEL` c. Classpath resource: `taoensso.tufte.rt-min-level` 2. Namespace filter (see signal `:ns`): a. JVM property: `taoensso.tufte.rt-ns-filter` b. Env variable: `TAOENSSO_TUFTE_RT_NS_FILTER` c. Classpath resource: `taoensso.tufte.rt-ns-filter` 3. Id filter (see signal `:id`): a. JVM property: `taoensso.tufte.rt-id-filter` b. Env variable: `TAOENSSO_TUFTE_RT_ID_FILTER` c. Classpath resource: `taoensso.tufte.rt-id-filter` Config values are parsed as edn, examples: `taoensso.tufte.rt-min-level` => ":info" `TAOENSSO_TUFTE_RT_NS_FILTER` => "{:disallow \"taoensso.*\"}" `taoensso.tufte.rt-id-filter.cljs` => "#{:my-id1 :my-id2}" `TAOENSSO_TUFTE_RT_ID_FILTER_CLJ` => "nil" Runtime vs compile-time filters The above filters (1..4) all apply at RUNTIME ("rt"). This is typically what you want, since it allows you to freely adjust filtering (making it less OR MORE permissive) through later API calls like `set-min-level!`. As an advanced option, you can instead/additionally ELIDE (entirely omit) filtered callsites at COMPILE-TIME ("ct") by replacing "rt"->"ct" / "RT"->"CT" in the config ids above. Compile-time filters CANNOT be made MORE permissive at runtime. Tips: - The above config ids will affect both Clj AND Cljs. For platform-specific filters, use ".clj" / "_CLJ" or ".cljs" / "_CLJS" suffixes instead. e.g. "taoensso.tufte.rt-min-level.cljs". - To get the right edn syntax, first set your runtime filters using the standard utils (`set-min-level!`, etc.). Then call `get-filters` and serialize the relevant parts to edn with `pr-str`. - All environmental config uses `get-env` underneath. See the `get-env` docstring for more/advanced details. - Classpath resources are files accessible on your project's classpath. This usually includes files in your project's `resources/` dir.
Tufte profiling is activated using the `profiled` or `profile` macros: - `profiled` returns [<body-result> <?pstats>]. Handy when you want to consume profiling results directly at the callsite. - `profile` returns <body-result> and dispatches a profiling signal (map) to all registered handlers. This map includes `:pstats` and other info. Handy when you want to consume profiling results later/elsewhere. Profiling is activated only when ALL of the following are true: 1. Call filters pass (relevant for both `profile/d`): a. Compile-time: sample rate, kind, ns, id, level, when form, rate limit b. Runtime: sample rate, kind, ns, id, level, when form, rate limit 2. Handler filters pass (relevant only for `profile`): a. Compile-time: not applicable b. Runtime: sample rate, kind, ns, id, level, when fn, rate limit (Relevant only for `profile`): 3. Call transform (fn [signal]) => ?modified-signal returns non-nil 4. Handler transform (fn [signal]) => ?modified-signal returns non-nil Transform fns provides a flexible way to modify and/or filter signals by arbitrary signal data/content conditions (return nil to skip handling). Config: To set call filters (1a, 1b): Use: `set-kind-filter!`, `with-kind-filter` `set-ns-filter!`, `with-ns-filter` `set-id-filter!`, `with-id-filter` `set-min-level!`, `with-min-level` or see `help:environmental-config`. To set handler filters (2b) or transform (4): Provide relevant opts when calling `add-handler!` or `with-handler/+`. See `help:handler-dispatch-options` for details. Note: call filters (1a, 1b) should generally be AT LEAST as permissive as handler filters (2b) since they're always applied first. To set call transform (3): use `set-xfn!`, `with-xfn`. Compile-time vs runtime filtering: Compile-time filters are an advanced feature that can be tricky to set and use correctly. Most folks will want ONLY runtime filters. Compile-time filters works by eliding (completely removing the code for) disallowed calls. This means zero performance cost for these calls, but also means that compile-time filters are PERMANENT once applied. So if you set `:info` as the compile-time minimum level, that'll REMOVE CODE for every signal call below `:info` level. To decrease that minimum level, you'll need to rebuild. Compile-time filters can be set ONLY with environmental config (see `help:environmental-config` for details). Signal and handler sampling is multiplicative: Both calls and handlers can have independent sample rates, and these MULTIPLY! If a signal is created with 20% sampling and a handler handles 50% of received signals, then 10% of possible signals will be handled (50% of 20%). When sampling is active, the final (combined multiplicative) rate is helpfully reflected in each signal's `:sample` rate value ∈ℝ[0,1]. If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
Dispatch options can be provided for each signal handler when calling `add-handler!` or `with-handler/+`. These options will be merged over the defaults specified by `default-handler-dispatch-opts`. All handlers support the same dispatch options, including: `:async` (Clj only) - may be `nil` (=> synchronous) or map with options: `:buffer-size` (default 1024) Size of request buffer, and the max number of pending requests before configured back-pressure behaviour is triggered (see `:mode`). `:mode` (default `:blocking`) Back-pressure mode ∈ #{:blocking :dropping :sliding}. Controls what happens when a new request is made while request buffer is full: `:blocking` => Blocks caller until buffer space is available `:dropping` => Drops the newest request (noop) `:sliding` => Drops the oldest request `:n-threads` (default 1) Number of threads to use for executing fns (servicing request buffer). NB handling order guaranteed to be be sequential iff n=1. `:drain-msecs` (default 6000 msecs) Maximum time (in milliseconds) to try allow pending execution requests to complete when stopping handler. nil => no maximum. `:priority` (default 100) Optional handler priority ∈ℤ. Handlers will be called in descending priority order (larger ints first). `:track-stats?` (default true) Should handler track statistics (e.g. handling times) for reporting by `get-handlers-stats`? `:sample` (default nil => no sampling) Optional sample rate ∈ℝ[0,1], or (fn dyamic-sample-rate []) => ℝ[0,1]. When present, handle only this (random) proportion of signals: 1.0 => handle 100% of signals (same as nil rate, default) 0.0 => hanel 0% of signals (noop all) 0.5 => handle 50% of signals (random) `:kind-filter` - Kind filter as in `set-kind-filter!` (when relevant) `:ns-filter` - Namespace filter as in `set-ns-filter!` `:id-filter` - Id filter as in `set-id-filter!` (when relevant) `:min-level` - Minimum level as in `set-min-level!` `:when-fn` (default nil => always allow) Optional NULLARY (fn allow? []) that must return truthy for handler to be called. When present, called *after* sampling and other filters, but before rate limiting. Useful for filtering based on external state/context. See `:xfn` for an alternative that takes a signal argument! `:limit` (default nil => no rate limit) Optional rate limit spec as provided to `taoensso.encore/rate-limiter`, {<limit-id> [<n-max-calls> <msecs-window>]}. Examples: {"1/sec" [1 1000]} => Max 1 call per 1000 msecs {"1/sec" [1 1000] "10/min" [10 60000]} => Max 1 call per 1000 msecs, and 10 calls per 60 secs `:xfn` (default nil => no transform) Optional transform (fn [signal]) => ?modified-signal to apply before handling signal. When transform returns nil, skips handler. Compose multiple transform fns together with `comp-some`. `:error-fn` - (fn [{:keys [handler-id signal error]}]) to call on handler error. `:backp-fn` - (fn [{:keys [handler-id ]}]) to call on handler back-pressure. If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
Signal handlers process created signals to do something with them (analyse them, write them to console/file/queue/db, etc.). Manage handlers with: `get-handlers` - Returns info on registered handlers (dispatch options, etc.) `get-handlers-stats` - Returns stats for registered handlers (handling times, etc.) `add-handler!` - Registers given handler `remove-handler!` - Unregisters given handler `with-handler` - Executes form with ONLY the given handler registered `with-handler+` - Executes form with the given handler (also) registered `stop-handlers!` - Stops registered handlers NB you should always call `stop-handlers!` somewhere appropriate - usually near the end of your `-main` or shutdown procedure, AFTER all other code has completed that could create signals. See the relevant docstrings for details. See `help:handler-dispatch-options` for handler filters, etc. If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs!
Profiling stats (`pstats`) When profiling (activated by `profile/d`) is complete, you'll get back a profiling stats (`pstats`) object that can be derefed and/or merged with other `pstats`: - @pstats => {:clock {:keys [t0 t1 total]}, :stats {<id> {:keys [n sum ...]}}} - @(merge-pstats ps1 ps2) => {:clock {:keys [t0 t1 total]}, :stats {<id> {:keys [n sum ...]}}} Full set of keys in the above `:stats` maps: :n :min :max :mean :mad :sum :p25 :p50 :p75 :p90 :p95 :p99 :loc :last All values are numerical (longs or doubles), except for `:loc` which is a map of `p` callsite location information, or set of such maps, e.g.: #{{:ns \"my-ns\", :line 122, :column 21}}
Tufte profiling signals are maps with {:keys [inst id ns level data ...]}, though they can be modified by call and/or handler transforms (xfns). Default signal keys: `:schema` ----------- Int version of signal schema (current: 1) `:inst` ------------- Platform instant [1] when `profile` called, monotonicity depends on system clock `:ns` --------------- ?str namespace of `profile` callsite `:coords` ----------- ?[line column] of `profile` callsite `:level` ------------ Profiling level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...} `:id` --------------- Profiling ?id (usu. keyword) `:data` ------------- Arb app-level data ?val (usu. a map) given to `profile` call `:ctx` -------------- ?val of `*ctx*` (arb app-level state) when `profile` was called `:body-result` ------ Return value of the body wrapped by `profile` call `:pstats` ----------- Profiling stats object that can be derefed and/or merged (see `help:pstats-content`) `:format-pstats-fn` - Cached (fn [pstats]) => formatted table string function `:host` ------------- (Clj only) {:keys [name ip]} info for network host `:thread` ----------- (Clj only) {:keys [name id group]} info for thread that called `profile` `:sample` ----------- Sample ?rate ∈ℝ[0,1] for combined call AND handler sampling (0.75 => allow 75% of signals, nil => allow all) If anything is unclear, please ping me (@ptaoussanis) so that I can improve these docs! [1] `java.time.Instant` or `js/Date`
(keep-callsite inner-form)
CLJ-865 means that it's not possible for an inner macro to access `&form` metadata (incl. {:keys [line column]}) of a wrapping outer macro: (defmacro inner [] (meta &form)) (defmacro outer [] `(inner)) (outer) => nil This util offers a workaround for authors of the outer macro, preserving the outer `&form` metadata for the inner macro: (defmacro inner [] (meta &form)) (defmacro outer [] (keep-callsite `(inner))) (outer) => {:keys [line column ...]}
(merge-pstats)
(merge-pstats ps0)
(merge-pstats ps0 ps1)
(merge-pstats nmax ps0 ps1)
Merges given `pstats`, compacting as necessary. Merged statistics are lossless unless data to merge are very large.
(new-pdata)
(new-pdata {:keys [dynamic? nmax] :or {dynamic? true nmax default-nmax}})
(new-pdata dynamic? nmax)
Low-level primitive for advanced users. Returns a new pdata object for use with `with-profiling` and/or `capture-time!`. Deref to get pstats: (let [pd (new-pdata) t0 (System/nanoTime)] (with-profiling pd {} (p :foo (Thread/sleep 100)) (capture-time! pd :bar (- t0 (System/nanoTime)))) (deref pd)) Dynamic (thread-safe) by default. *WARNING*: don't change this default unless you're very sure the resulting pdata object will not be concurrently modified across threads. Concurrent modification will lead to bad data and/or exceptions!
(p id & body)
(p {:keys [id level]} & body)
Profiling spy. Use this macro to wrap forms that should be timed during active profiling. A unique form id (keyword) must be provided, e.g.: (p ::my-form (do-something)) (p {:id ::my-form} (do-something)) (p {:id ::my-form :level :debug} (do-something)) `p` will ALWAYS execute its body and return the body's result, even when profiling isn't active. Options include: `:id` ---- Unique id for this form in resulting `pstats` (e.g. `::my-fn-call`) `:level` - Profiling level ∈ #{<int> :trace :debug :info :warn :error :fatal :report ...} (default `:info`)
(profile {:keys [dynamic? nmax elidable? sample ns id level when limit limit-by
ctx ctx+ data xfn xfn+]}
&
body)
Use this to conditionally activate profiling for given body: - ALWAYS executes body and returns <body-result>. - When filtering conditions are met (see `help:filters`), records execution times of all `p` forms in body and dispatches a profiling signal map (see `help:signal-content`) to any registered handlers (see `help:handlers`). Handy when you want to consume profiling results asynchronously and/or away from the callsite, otherwise prefer `profile`. Options include: `:dynamic?` --- Perform multi-threaded profiling with binding conveyance? (default false) `:level` ------ Profiling level (default `:info`), must be >= active minimum level to profile `:id` --------- Profiling ?id for filtering, etc. (e.g. `::my-profiling-id`) `:data` ------- Arb app-level ?data to incl. in signal: usu. a map `:ctx` -------- Custom ?val to override auto (dynamic `*ctx*`) in signal, as per `with-ctx` `:ctx+` ------- Custom ?val to update auto (dynamic `*ctx*`) in signal, as per `with-ctx+` `:xfn` -------- Optional transform (fn [signal]) => ?modified-signal to apply when signal is created, as per `with-xfn` `:xfn+` ------- Optional extra transform (fn [signal]) => ?modified-signal to apply when signal is created, as per `with-xfn+` `:sample` ----- Sample rate ∈ℝ[0,1], profile only this random proportion of calls (0.75 => 75%, nil => all) `:when` ------- Arb ?form; when present, form must return truthy to allow profiling `:limit` ------ Rate limit ?spec given to `taoensso.tufte/rate-limiter`, see its docstring for details `:limit-by` --- When present, rate limits will be enforced independently for each value (any Clojure value!) `:nmax` ------- Max captures per `p` id before compaction (default 8e5). `:elidable?` -- Should signal be subject to compile-time elision? (default true) Laziness in body: Lazy seqs and other forms of laziness (e.g. delays) in body will only contribute to profiling results if/when EVALUATION ACTUALLY OCCURS. This is intentional and a useful property. Compare: (profile {} (delay (Thread/sleep 2000))) ; Doesn't count sleep (profile {} @(delay (Thread/sleep 2000))) ; Does count sleep Async code in body: Execution time of any code in body that runs asynchronously on a different thread will generally NOT be automatically captured by default. `:dynamic?` can be used to support capture in cases where Clojure's binding conveyance applies (e.g. futures, agents, pmap). Just make sure that all work you want to capture has COMPLETED before the `profile` form ends- for example, by blocking on pending futures. In other advanced cases (notably core.async `go` blocks), please see `with-profiling` and `capture-time!`. `core.async` warning: `core.async` code can be difficult to profile correctly without a deep understanding of precisely what it's doing under-the-covers. Some general recommendations that can help keep things simple: - Try minimize the amount of code + logic in `go` blocks. Use `go` blocks for un/parking to get the data you need, then pass the data to external fns. Profile these fns (or in these fns), not in your `go` blocks. - In particular: you MUST NEVER have parking calls inside `(profile {:dynamic? false} ...)`. This can lead to concurrency exceptions. If you must profile code within a go block, and you really want to include un/parking times, use `(profile {:dynamic? true} ...)` instead.
(profiled {:keys [dynamic? nmax elidable? sample ns id level when limit
limit-by]}
&
body)
Use this to conditionally activate profiling for given body: - ALWAYS executes body and returns [<body-result> <?pstats>]. - When filtering conditions are met (see `help:filters`), records execution times of all `p` forms in body and includes resulting `pstats` object in return value. Handy when you want to consume profiling results directly at the callsite, otherwise prefer `profile`. Options include: `:dynamic?` --- Perform multi-threaded profiling with binding conveyance? (default false) `:level` ------ Profiling level (default `:info`), must be >= active minimum level to profile `:id` --------- Profiling ?id for filtering, etc. (e.g. `::my-profiling-id`) `:sample` ----- Sample rate ∈ℝ[0,1], profile only this random proportion of calls (0.75 => 75%, nil => all) `:when` ------- Arb ?form; when present, form must return truthy to allow profiling `:limit` ------ Rate limit ?spec given to `taoensso.tufte/rate-limiter`, see its docstring for details `:limit-by` --- When present, rate limits will be enforced independently for each value (any Clojure value!) `:nmax` ------- Max captures per `p` id before compaction (default 8e5). `:elidable?` -- Should profiling be subject to compile-time elision? (default true) Laziness in body: Lazy seqs and other forms of laziness (e.g. delays) in body will only contribute to profiling results if/when EVALUATION ACTUALLY OCCURS. This is intentional and a useful property. Compare: (profiled {} (delay (Thread/sleep 2000))) ; Doesn't count sleep (profiled {} @(delay (Thread/sleep 2000))) ; Does count sleep Async code in body: Execution time of any code in body that runs asynchronously on a different thread will generally NOT be automatically captured by default. `:dynamic?` can be used to support capture in cases where Clojure's binding conveyance applies (e.g. futures, agents, pmap). Just make sure that all work you want to capture has COMPLETED before the `profiled` form ends- for example, by blocking on pending futures. In other advanced cases (notably core.async `go` blocks), please see `with-profiling` and `capture-time!`. `core.async` warning: `core.async` code can be difficult to profile correctly without a deep understanding of precisely what it's doing under-the-covers. Some general recommendations that can help keep things simple: - Try minimize the amount of code + logic in `go` blocks. Use `go` blocks for un/parking to get the data you need, then pass the data to external fns. Profile these fns (or in these fns), not in your `go` blocks. - In particular: you MUST NEVER have parking calls inside `(profiled {:dynamic? false} ...)`. This can lead to concurrency exceptions. If you must profile code within a go block, and you really want to include un/parking times, use `(profiled {:dynamic? true} ...)` instead.
(rate-limiter spec)
(rate-limiter opts spec)
Takes a spec of form [ [<n-max-reqs> <msecs-window>] ...] or ; Unnamed limits {<limit-id> [<n-max-reqs> <msecs-window>]} ; Named limits and returns stateful (fn a-rate-limiter [] [req-id] [command req-id]). Call the returned limiter fn with a request id (any Clojure value!) to enforce limits independently for each id. For example, (limiter-fn <ip-address-string>) will return: - Falsey when allowed (all limits pass for given IP), or - Truthy when disallowed (any limits fail for given IP): [<worst-limit-id> <worst-backoff-msecs> {<limit-id> <backoff-msecs>}] Or call the returned limiter fn with an extra command argument: (limiter-fn :rl/peek <req-id) - Check limits WITHOUT incrementing count (limiter-fn :rl/reset <req-id) - Reset all limits for given req-id
(refer-tufte)
(require '[taoensso.tufte :as tufte :refer [defnp p profiled profile]])
(remove-handler! handler-id)
Stops and deregisters signal handler with given id, and returns ?{<handler-id> {:keys [dispatch-opts handler-fn]}} for all handlers still registered.
(set-ctx! root-ctx)
Set `*ctx*` var's default (root) value. See `*ctx*` for details.
(set-id-filter! id-filter)
Sets call id filter based on given `id-filter` spec. `id-filter` may be: - A regex pattern of id/s to allow - A str/kw/sym to allow, with "*" and "(.*)" as wildcards: "foo.*" will allow "foo.bar" "foo(.*)" will allow "foo.bar" and "foo" - A set/vector of above (allow on any match) - A map, {:allow <spec> :disallow <spec>} with specs as above: If present, `:allow` spec MUST match, AND If present, `:disallow` spec MUST NOT match.
(set-ns-filter! ns-filter)
Sets call namespace filter based on given `ns-filter` spec. `ns-filter` may be: - A namespace. - A regex pattern of namespaces/s to allow - A str/kw/sym to allow, with "*" and "(.*)" as wildcards: "foo.*" will allow "foo.bar" "foo(.*)" will allow "foo.bar" and "foo" - A set/vector of above (allow on any match) - A map, {:allow <spec> :disallow <spec>} with specs as above: If present, `:allow` spec MUST match, AND If present, `:disallow` spec MUST NOT match.
(set-var-root! var-sym root-val)
Sets root binding (value) of the var identified by given symbol, and returns the new value. Cross-platform. See also `update-var-root!`.
(set-xfn! ?root-xfn)
Set `*xfn*` var's default (root) value. See `*xfn*` for details.
(stats-accumulator)
Experimental, subject to change. Feedback welcome! Small util to help merge `pstats` from multiple runs and/or threads. Returns a stateful `StatsAccumulator` (`sacc`) with: - (sacc <profiling-id> <pstats>) ; Merges given pstats under given profile id - @sacc ; Drains accumulator and returns drained ; {<profiling-id> <merged-pstats>} Note that for performance reasons, you'll likely want some kind of async/buffer/serialization mechanism in front of merge calls. One common pattern using `handler:accumulating` is to create a system-wide accumulator that you deref every n minutes/etc. to get a view of system-wide performance over the period, e.g.: (defonce my-sacc (stats-accumulator) ; Create an accumulator (add-handler! :my-sacc (handler:accumulating my-sacc)) ; Register handler (defonce my-sacc-drainer ;; Drain and print formatted stats every minute (future (while true (when-let [m (not-empty @my-sacc)] (println (format-grouped-pstats m))) (Thread/sleep 60000)))) (profile ...) ; Used elsewhere in your application, e.g. ; wrapping relevant Ring routes in a web application. See example clj project for more details.
(stop-handlers!)
Stops registered signal handlers in parallel by calling each handler-fn with no arguments. This gives each handler the opportunity to flush buffers, close files, etc. Each handler will immediately stop accepting new signals, nooping if called. Blocks to return ?{<handler-id> {:keys [okay error]}}, honouring each handler's `:drain-msecs` value (see `help:handler-dispatch-options`). NB you should always call `stop-handlers!` somewhere appropriate - usually near the end of your `-main` or shutdown procedure, AFTER all other code has completed that could create signals.
(update-var-root! var-sym update-fn)
Updates root binding (value) of the var identified by given symbol, and returns the new value: (update-var-root! my-var (fn [old-root-val] <new-root-val>)) => <new-root-val> Similar to `alter-var-root` but cross-platform and takes a symbol rather than a var. See also `set-var-root!`.
(with-ctx ctx form)
Evaluates given form with given `*ctx*` value. See `*ctx*` for details.
(with-ctx+ update-map-or-fn form)
Evaluates given form with updated `*ctx*` value. `update-map-or-fn` may be: - A map to merge with current `*ctx*` value, or - A unary fn to apply to current `*ctx*` value See `*ctx*` for details.
(with-handler handler-id handler-fn form)
(with-handler handler-id handler-fn dispatch-opts form)
Executes form with ONLY the given signal handler registered. Stops handler after use. Useful for tests/debugging. See `help:handler-dispatch-options` for handler filters, etc. See also `with-handler+`.
(with-handler+ handler-id handler-fn form)
(with-handler+ handler-id handler-fn dispatch-opts form)
Executes form with the given signal handler (also) registered. Stops handler after use. Useful for tests/debugging. See `help:handler-dispatch-options` for handler filters, etc. See also `with-handler`.
(with-id-filter id-filter form)
Executes form with given call id filter in effect. See `set-id-filter!` for details.
(with-ns-filter ns-filter form)
Executes form with given call namespace filter in effect. See `set-ns-filter!` for details.
(with-profiling pdata {:as opts :keys [dynamic?]} & body)
Low-level primitive for advanced users. Executes body with profiling active, and returns <body-result>. If `:dynamic?` is false (default), body's evaluation MUST begin and end without interruption on the same thread. This means that body CANNOT contain any parking IoC style (e.g. `core.async`) macros. See `new-pdata` for more info on low-level primitives.
(with-xfn ?xfn form)
Evaluates given form with given `*xfn*` value, see `*xfn*` for details.
(with-xfn+ ?xfn form)
Evaluates given form with composed `*xfn*` value. Same as (with-xfn (comp-xfn *xfn* ?xfn) ...). See `*xfn*` for details.
(without-filters form)
Executes form without any runtime call filters.
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close