-
Notifications
You must be signed in to change notification settings - Fork 1
Add line numbers #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,15 @@ | |
| [c k & args] | ||
| (apply (k c) c args)) | ||
|
|
||
| (defmacro calling-meta | ||
| "Used by other macros to get the namespace, file and line of the log call. | ||
| Builds a def with a generated symbol to expand and evaluate for the info." | ||
| [] | ||
| `(let [meta# (meta (def sym#))] | ||
| (-> meta# | ||
| (select-keys [:ns :file :line]) | ||
| (update :ns str)))) | ||
|
|
||
| ;; # Log-levels | ||
|
|
||
| (defn set-valid-levels | ||
|
|
@@ -52,12 +61,15 @@ | |
| {:message data})) | ||
|
|
||
| (defn prepare-data-for-logging | ||
| "Prepare the `data` structure being logged by setting the `:level` | ||
| and `:date` values" | ||
| [config data level] | ||
| "Prepare the `data` structure being logged by setting the `:level`, | ||
| `:date`, `:ns`, `:file` and `:line` values" | ||
| [config data level calling-meta] | ||
| (->> data | ||
| normalize-data | ||
| (merge {:date (call config :date-fn) | ||
| :ns (:ns calling-meta) | ||
| :file (:file calling-meta) | ||
| :line (:line calling-meta) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this could be I'm wondering if the calling-meta should be a) bundled in a sub-key, and/or b) passed within
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, good point r.e. the merge. I was actually thinking that maybe |
||
| :level level}))) | ||
|
|
||
| ;; # The logger configuration | ||
|
|
@@ -118,40 +130,62 @@ | |
|
|
||
| ;; # Main logging functions | ||
|
|
||
| (defn log | ||
| "Log a record with the config, level, and data provided" | ||
| [c level data] | ||
| (let [record (invoke c :prepare-fn data level)] | ||
| (defn log! | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the exclamation mark?
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, if just to distinguish from the log macro, I've seen people use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was just to distinguish the macro from the function, yes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It shouldn't be called
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As in https://github.com/bbatsov/clojure-style-guide#changing-state-fns-with-exclamation-mark ? That only says STM-unsafe should end with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm open to other suggestions. I basically nicked this pattern from |
||
| "Log a record with the config, level, form metadata and data provided" | ||
| [c calling-meta level data] | ||
| (let [record (invoke c :prepare-fn data level calling-meta)] | ||
| (when (invoke c :log?-fn record) | ||
| (->> record | ||
| (call c :format-fn) | ||
| (call c :output-fn))))) | ||
|
|
||
| (defn spy-with | ||
| (defmacro log | ||
| "Log a record with the config, level, and data provided." | ||
| [c level data] | ||
| `(log! ~c (calling-meta) ~level ~data)) | ||
|
|
||
| (defmacro spy-with->> | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good spot re Also, why a macro?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So it can use the It's why I tried to leave all the complicated stuff in the log function, and have each macro do the minimum necessary to pass the function what it needs. Then there's minimal faffing with macro escaping. |
||
| "Log a record about a data value, first applying the transform | ||
| supplied. Returns the original, untransformed value. Designed | ||
| to be used in threaded pipelines. For example: | ||
| to be used in thread-last pipelines. For example: | ||
|
|
||
| (->> [1 2 3 4 5] | ||
| (map inc) | ||
| (spy-with->> logger #(str \"List contains \" (pr-str %)) :info) | ||
| (map inc))" | ||
| [c transform level data] | ||
| `(doto ~data | ||
| (->> (~transform) | ||
| (log ~c ~level)))) | ||
|
|
||
| (defmacro spy-with-> | ||
| "Log a record about a data value, first applying the transform | ||
| supplied. Returns the original, untransformed value. Designed | ||
| to be used in thread-first pipelines. For example: | ||
|
|
||
| (-> {:counter 1} | ||
| (update :counter inc) | ||
| (spy-with #(str \"The number is now \" (:counter %))) | ||
| (spy-with-> logger #(str \"The number is now \" (:counter %)) :info) | ||
| (update :counter inc))" | ||
| [c transform level data] | ||
| (doto data | ||
| (->> transform | ||
| (log c level)))) | ||
| [data c transform level] | ||
| `(spy-with->> ~c ~transform ~level ~data)) | ||
|
|
||
| (defn spy | ||
| "Log a record about a data value as per `spy-with`, but with no transformation." | ||
| (defmacro spy->> | ||
| "Log a record about a data value as per `spy-with->>`, but with no transformation." | ||
| [c level data] | ||
| (spy-with c identity level data)) | ||
| `(spy-with->> ~c identity ~level ~data)) | ||
|
|
||
| (defmacro spy-> | ||
| "Log a record about a data value as per `spy-with->`, but with no transformation." | ||
| [data c level] | ||
| `(spy-with->> ~c identity ~level ~data)) | ||
|
|
||
| ;; # Common format configs | ||
|
|
||
| (defn format-plain | ||
| "Simple plain string formatter" | ||
| [{:keys [:date :level :message]}] | ||
| (str date " [" level "] " message)) | ||
| [{:keys [:ns :line :date :level :message]}] | ||
| (str date " [" ns ":" line "] [" (name level) "] " message)) | ||
|
|
||
| (def plain-config | ||
| "Config for a plain text log message format" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an implementation detail so possibly doesn't need to be in docstring. Interesting though - is there any issue with having def's everywhere there's logging? And should it be a
defonceto avoid being reevaluated in loops?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defoncewould probably be better for loops, yes.As for the defs everywhere where logging, I did spend quite a while in query console and writing extra output from the test file to check the current vars in
(ns-publics (all-ns))and anywhere else I could think to check. I couldn't find these defs cluttering up the namespace anywhere. The use of(gen-sym)also means that the defs are unique and won't clash with user defs, so even if they do stay around somewhere I don't think have one additional randomly named def with no value per log call is an issue.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done some testing with this.
defoncerequires a value, because it basically says "If the symbol has a value, leave it as-is. Otherwise set the symbol to arg #2".(def sym#)isn't actually setting the symbol to have a value, it's just defining the symbol as present. You can't do(defonce sym#).