taoensso / telemere

Structured telemetry library for Clojure/Script
https://www.taoensso.com/telemere
Eclipse Public License 1.0
200 stars 5 forks source link

filename is missing when tel/trace! statements are emitted by a macro #22

Closed johanatan closed 2 months ago

johanatan commented 2 months ago

hi, i have the following macro to sprinkle tel/trace! statements throughout an expression. however the signals that are being emitted from it are missing the "filename" field. shouldn't this code get expanded within the referencing source file / consuming code and thus still have its filename ?

(ns options.macros
  (:require [clojure.walk :as walk]))

(defn- quoted? [form]
  (and (seq? form) (= (first form) 'quote)))

(defn- wrap-with-trace [form]
  (if (and (list? form)
           (not (quoted? form))
           (not= 'taoensso.telemere/trace! (first form)))
    `(taoensso.telemere/trace! ~form)
    form))

(defmacro deep-trace [body]
  (walk/postwalk wrap-with-trace body))
johanatan commented 2 months ago

fwiw, the ns field does indeed match the macro consuming / referencing file.

johanatan commented 2 months ago

line and col are also nil.

ptaoussanis commented 2 months ago

@johanatan Hi Johanatan, this isn't actually a problem on Telemere's side - but is unfortunately a result of a long-standing Clojure issue (CLJ-865). The Jira URL's changed a few times, but you can find some info on it by Googling.

Basically- when a macro wraps another, the inner macro (trace! in this case) loses access to callsite info (line, col, and file info).

There's two ways to work around this:

  1. Try taoensso.encore/keep-callsite (see its docstring for usage), or
  2. Mod your macro to manually provide callsite info to trace!

For (2), you'll need something like this (untested, not at a REPL - feel free to ping if you need assistance):

(defn- wrap-with-trace [location form]
  (if (and (list? form)
           (not (quoted? form))
           (not= 'taoensso.telemere/trace! (first form)))
    `(taoensso.telemere/trace! {:location* ~location} ~form)
    form))

(defmacro deep-trace [body]
  (let [location (taoensso.encore/get-source &form & env)]
    (walk/postwalk wrap-with-trace location body)))

Hope that helps!

johanatan commented 2 months ago

yes, that helps. thank you! i assume that the {:location ...} map should be metadata on the form rather than just a raw map?

ptaoussanis commented 2 months ago

You're welcome 👍

i assume that the {:location ...} map should be metadata on the form rather than just a raw map?

If you're talking about the {:location* ...} map provided to tel/trace!, that's just a regular map - there's no need to mess with metadata.

Telemere's API is designed to make this kind of location injection easy.

For example: (:ns (tel/with-signal (tel/log! {:location {:ns "foo"}} "hello"))) => "foo".

I.e. :location is just a standard option that can be provided to all signal creators.

BTW either :location or :location* would work in your case. :location* sets the default, but would allow individual signal calls to still override with :location. This distinction probably doesn't matter to you.

johanatan commented 2 months ago

Ah nice! Got it. Thanks

ptaoussanis commented 2 months ago

Closing for now, but feel free to ping if you run into any trouble getting this to work with your macros.