├── .VERSION_PREFIX
├── .circleci
└── config.yml
├── .dir-locals.el
├── .gitignore
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── bb.edn
├── bin
├── kaocha
└── proj
├── deps.edn
├── dev.cljs.edn
├── interaction
└── walkthrough.cljs
├── pom.xml
├── resources
└── clj-kondo.exports
│ └── com.lambdaisland
│ └── glogi
│ └── config.edn
├── src
└── lambdaisland
│ ├── glogc.cljc
│ ├── glogi.clj
│ ├── glogi.cljs
│ └── glogi
│ ├── console.cljs
│ ├── demo.cljs
│ └── print.cljs
├── test
└── lambdaisland
│ ├── glogc_test.cljs
│ └── glogi_test.cljs
└── tests.edn
/.VERSION_PREFIX:
--------------------------------------------------------------------------------
1 | 1.3
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | clojure: lambdaisland/clojure@0.0.8
5 |
6 | commands:
7 | checkout_and_run:
8 | steps:
9 | - checkout
10 | - clojure/with_cache:
11 | cache_version: "1"
12 | steps:
13 | - run: mkdir -p test-results/kaocha
14 | - run: |
15 | bin/kaocha \
16 | --reporter documentation \
17 | --plugin junit-xml \
18 | --plugin kaocha.plugin.alpha/info \
19 | --print-env \
20 | --junit-xml-file test-results/kaocha/$(date +"%s").xml
21 | - store_artifacts:
22 | path: test-results
23 | - store_test_results:
24 | path: test-results
25 |
26 | jobs:
27 | java-16:
28 | executor: clojure/openjdk16
29 | steps: [{checkout_and_run: {}}]
30 | java-17:
31 | executor: clojure/openjdk17
32 | steps: [{checkout_and_run: {}}]
33 | java-19:
34 | executor: clojure/openjdk19
35 | steps: [{checkout_and_run: {}}]
36 |
37 | workflows:
38 | kaocha_test:
39 | jobs:
40 | - java-16
41 | - java-17
42 | - java-19
43 |
--------------------------------------------------------------------------------
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ((nil . ((cider-clojure-cli-global-options . "-A:dev:test:cljs844")
2 | (cider-default-cljs-repl . node))))
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .cpcache
2 | .nrepl-port
3 | target
4 | repl
5 | scratch.clj
6 | .cljs_node_repl
7 | node_modules
8 | package-lock.json
9 | .store
10 | out
11 | package.json
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Unreleased
2 |
3 | ## Added
4 |
5 | - clj-kondo config to warn when using an add number of arguments
6 | - `lambdaisland.glogi.console/timestamp` goog-define, set to `"true"` to prepend
7 | each log message with a timestamp
8 |
9 | # 1.3.169 (2023-03-27 / 109b82e)
10 |
11 | ## Changed
12 |
13 | - Modified an internal function so that code emitted by logging macros can be
14 | DCE'd when the user disables logging.
15 |
16 | # 1.2.164 (2022-11-25 / 9a89583)
17 |
18 | ## Added
19 |
20 | - Add support for logging multiple forms with `spy`.
21 |
22 | ## Fixed
23 |
24 | - Release under `com.lambdaisland` as well as `lambdaisland`, see https://github.com/lambdaisland/open-source/issues/9
25 |
26 | ## Changed
27 |
28 | - Version bumps
29 |
30 | # 1.1.144 (2021-12-14 / 6f83c7d)
31 |
32 | ## Fixed
33 |
34 | - Fix Closure compatibility issue
35 |
36 | # 1.0.136 (2021-10-01 / 40a1dbc)
37 |
38 | ## Changed
39 |
40 | - Set a default level of `:info` for the root logger
41 |
42 | # 1.0.128 (2021-07-19 / 576ceba)
43 |
44 | ## Fixed
45 |
46 | - Fix macro resolve in `ns`, which caused cljs logs to fail
47 |
48 | ## Changed
49 |
50 | # 1.0.116 (2021-04-26 / e1dbcb3)
51 |
52 | ## Fixed
53 |
54 | - Fixed an issue with advanced compilation when calling `level-value`, which is
55 | used by the console handler
56 |
57 | # 1.0.112 (2021-04-21 / a104211)
58 |
59 | ## Changed
60 |
61 | - Artifact name changed to `com.lambdaisland/glogi` in line with Clojars policy
62 |
63 | # 1.0.106 (2021-04-20 / 1128462)
64 |
65 | ## Fixed
66 |
67 | - Prevent single-arg `logger` from erasing the log level
68 |
69 | # 1.0.100 (2021-04-15 / 8a8ccf0)
70 |
71 | ## Fixed
72 |
73 | - More fixes to deal with upstream changes in Google Closure Library
74 |
75 | # 1.0.83 (2021-03-09 / d0f76e8)
76 |
77 | ## Added
78 |
79 | - With `lambdaisland.glogc` there is now an official way to use the
80 | Glogi/Pedestal-log combo in a consistent cross-platform way
81 |
82 | # 1.0.80 (2021-03-09 / 9c5ea2b)
83 |
84 | ## Fixed
85 |
86 | - Maintain compatibility with newer versions of the Google Closure library,
87 | which introduced breaking changes in v20210302
88 |
89 | ## Changed
90 |
91 | # 1.0.74 (2020-08-26 / acf8c48)
92 |
93 | ## Added
94 |
95 | - Export lambdaisland.glogi.set-levels so you can use it from the browser
96 | console (pass in an array of two-element arrays of strings)
97 |
98 | # 1.0.70 (2020-08-19 / df34f1a)
99 |
100 | ## Changed
101 |
102 | - We no longer pull in a specific Clojure/ClojureScript version, assuming that
103 | client consumers will already have specific versions declared for their project.
104 |
105 | # 1.0.63 (2020-05-05 / bcafca0)
106 |
107 | ## Added
108 |
109 | - the Closure constant `lambdaisland.glogi.console.colorize` can now take four
110 | possible values, `"true"` (use `console.log` CSS formatting), `"false"` (log
111 | plain text), `"raw"`, log objects directly (good for cljs-devtools), or
112 | `"auto"` (detect most suitable option)
113 |
114 | # 1.0-60 (2020-04-15 / 71bea10)
115 |
116 | ## Fixed
117 |
118 | - For for when goog.log.ENABLED is false (for use in prod builds)
119 |
120 | # 1.0-55 (2020-04-07 / 592208d)
121 |
122 | ## Fixed
123 |
124 | - Fix incorrect variable reference in `logger`
125 | - Honor goog.log.ENABLED, important for release builds
126 |
127 | # 1.0-47 (2020-04-02 / 35d7fff)
128 |
129 | ## Added
130 |
131 | - Print support for cljs.core.PersistentQueue
132 |
133 | # 1.0-44 (2020-04-02 / 5a377e7)
134 |
135 | ## Added
136 |
137 | - Added colored printing of objects and arrays
138 |
139 | ## Fixed
140 |
141 | - Fixed colored printing of seqs and vectors
142 |
143 | ## Changed
144 |
145 | - Better mapping of log levels to log methods and colors. `:trace` is now an
146 | alias for `:finer`, `:debug` for `:fine` (before `:trace` = `:fine` , `:debug`
147 | = `:config`)
148 |
149 | # 1.0-41 (2020-03-31 / ab9f97f)
150 |
151 | ## Added
152 |
153 | - Added colorization of Clojure data structures, for places where devtools is
154 | not available
155 | - Added the `config` macro, which logs to the corresponding log level
156 |
157 | # 0.0-36 (2020-03-30 / e2606fb)
158 |
159 | ## Added
160 |
161 | - Added `spy` macro, logs the expression and its return value, returns the value
162 | - Added logging macros corresponding with goog.log log levels: `shout`,
163 | `severe`, `fine`, `finer`, `finest`
164 |
165 | ## Fixed
166 |
167 | - Use the correct console log method (log, error, warn, info) based on the log
168 | level
169 |
170 | # 0.0-33 (2020-03-25 / cd9df6b)
171 |
172 | ## Added
173 |
174 | - Added the ability to use keywords or symbols to look up a logger, or the
175 | special `:glogi/root` to find the root logger.
176 |
177 | ## Fixed
178 |
179 | - Fixed `set-levels` to match its docstring. Takes a map.
180 |
181 | # 0.0-29 (2020-03-25 / 991866f)
182 |
183 | ## Fixed
184 |
185 | - Got rid of the `LogBuffer/CAPACITY` hack, to prevent issues with advanced
186 | compilation
187 |
188 | # 0.0-25 (2019-06-25 / 0e226a8)
189 |
190 | ## Added
191 |
192 | - Added `set-level` for convenience
193 |
194 | ## Fixed
195 |
196 | - Fix assertion in `set-level`
197 |
198 | # 0.0-22 (2019-06-11 / 0d56b02)
199 |
200 | ## Fixed
201 |
202 | - Fix glogi.console when devtools isn't available
203 |
204 | # 0.0-18 (2019-06-11 / a27f7fc)
205 |
206 | ## Added
207 |
208 | - `lambdaisland.glogi.console` provides an alternative to `goog.debug.Console`,
209 | with the main benefit that it will log full data structures, thus playing
210 | nicely with cljs-devtools
211 | - Added an `info` macro for compat with pedestal.
212 |
213 | ## Changed
214 |
215 | - The default formatter is now `identity` instead of `pr-str`. This way we
216 | preserve full data structures until the last minute. Note that goog.debug.Logger
217 | will stringify the message, unless care is taken for it not to.
218 |
219 | # 0.0-13 (2019-06-10 / 0668ebe)
220 |
221 | First release
222 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Glögi
2 |
3 |
4 | [](https://circleci.com/gh/com.lambdaisland/glogi) [](https://cljdoc.org/d/com.lambdaisland/glogi) [](https://clojars.org/com.lambdaisland/glogi)
5 |
6 |
7 | A wrapper around `goog.log` inspired by `pedestal.log`.
8 |
9 | For more info see the accompanying blog post: [ClojureScript logging with goog.log](https://lambdaisland.com/blog/2019-06-10-goog-log), and [Logging in Practice with Glögi and Pedestal](https://lambdaisland.com/blog/2020-09-28-logging-in-practice-glogi-pedestal).
10 |
11 | ## Installation
12 |
13 | > **WARNING**: There are two packages with different artefact IDs published on clojars. One is `com.lambdaisland/glogi` and another is `lambdaisland/glogi` (without the `com.`). Please migrate your dependency to `com.lambdaisland/glogi` as this is the only one which recieves new updates.
14 | > We apologise for this confusion, but we had to make some changes to stay in accordance with the clojars policy.
15 |
16 | deps.edn
17 |
18 | ``` clojure
19 | com.lambdaisland/glogi {:mvn/version "1.3.169"}
20 | ```
21 |
22 | project.clj
23 |
24 | ``` clojure
25 | [com.lambdaisland/glogi "1.3.169"]
26 | ```
27 |
28 | ## Quickstart
29 |
30 | It is recommended to initialize Glögi at the top of your main namespace, so that
31 | no log messages are lost.
32 |
33 | ```clojure
34 | (ns my.app
35 | (:require [lambdaisland.glogi :as log]
36 | [lambdaisland.glogi.console :as glogi-console]))
37 |
38 | (glogi-console/install!)
39 |
40 | (log/set-levels
41 | {:glogi/root :info ;; Set a root logger level, this will be inherited by all loggers
42 | 'my.app.thing :trace ;; Some namespaces you might want detailed logging
43 | 'my.app.other :error ;; or for others you only want to see errors.
44 | })
45 |
46 | (log/info :hello {:message "Hello, world!"})
47 | ```
48 |
49 | Result in your browser console (but pretty with colors):
50 |
51 | ```
52 | [my.app] {:hello {:message "Hello, world!"}}
53 | ```
54 |
55 | Before you can start logging you need to install a handler that knows where to
56 | output the log messages (browser console, in a div, ...).
57 | `(glogi-console/install!)` is recommended. It contains some smarts so that your
58 | Clojure data is logged nicely. When cljs-devtools is active then it will pass
59 | data structures unchanged to `js/console.log` so devtools can render them. If
60 | not then it will stringify and colorize them so you get pretty EDN in your
61 | console, instead of seeing the implementation details of ClojureScript
62 | persistent data structures.
63 |
64 | Log functions take key/value pairs.
65 |
66 | ## Loggers and log levels
67 |
68 | In `goog.log`, which glogi is based on, loggers have hierarchical names, with
69 | segments separated by dots. If you use Glogi's logging macros then it will
70 | automatically get the logger based on the namespace name.
71 |
72 | When you call a logging function it will check the log level of the logger to
73 | decide if the message should be output ot not. To find this level the logger
74 | will go up the hierarchy until it finds a logger with an explicit level set.
75 | This is usually the root logger, but it doesn't have to be.
76 |
77 | So say you have these namespaces:
78 |
79 | ```
80 | my.app.ui.subs
81 | my.app.ui.events
82 | my.app.api
83 | my.app.api.routes
84 | some.lib.config
85 | some.lib.print
86 | ```
87 |
88 | You can set the level for individual namespaces
89 |
90 | ``` clojure
91 | (log/set-levels '{my.app.ui.subs :debug
92 | my.app.ui.events :config
93 | ...})
94 | ```
95 |
96 | But you can also set the level for a subtree of namespaces. So say you're debugging the API, which uses `some.lib`.
97 |
98 | ``` clojure
99 | (log/set-levels '{my.app.api :all
100 | some.lib :debug
101 | ...})
102 | ```
103 |
104 | This is really convenient and powerful. By sprinkling your code with logging at
105 | various levels you can easily get insight in what a particular part is doing, on
106 | demand.
107 |
108 | Instead of adding more and more print statements as you debug, and then deleting
109 | them afterwards, you can add increasingly detailed levels of logging instead.
110 | Later when you find yourself in the same waters you can dial the verbosity up or
111 | down as you see fit.
112 |
113 | Glogi is based on goog.log, and so it understand the log levels that goog.log
114 | provides. Glogi also aims to be at least partially API compatible with
115 | pedestal.log, and so we provide extra log levels that internally map to goog.log
116 | levels.
117 |
118 | | pedestal | goog.log | value | description |
119 | |----------|----------|----------|--------------------------------------------------------------|
120 | | | :off | Infinity | Special value to disable logging |
121 | | | :shout | 1200 | Critical error |
122 | | :error | :severe | 1000 | Serious failure |
123 | | :warn | :warning | 900 | Potential problem, but program continues |
124 | | :info | :info | 800 | Informational message, e.g. to make background tasks visible |
125 | | | :config | 700 | Configuration info |
126 | | :debug | :fine | 500 | Step-by-step debug messages |
127 | | :trace | :finer | 400 | More verbose, detailed tracing messages |
128 | | | :finest | 300 | Highly verbose and detailed tracing |
129 | | | :all | 0 | Special value to show all log messages |
130 |
131 | There are also two special levels, `:all` and `:off`, which you can use in
132 | `set-levels` to turn logging up to the maximum, or turn it off instead.
133 |
134 | It is recommended to use a consistent set of logging methods, for instance to
135 | use only the pedestal version, possibly augmented by the goog.log levels that
136 | don't have a pedestal equivalent.
137 |
138 | for instance:
139 |
140 | ``` clojure
141 | (log/shout ,,,)
142 | (log/severe ,,,)
143 | (log/error ,,,)
144 | (log/warn ,,,)
145 | (log/info ,,,)
146 | (log/config ,,,)
147 | (log/debug ,,,)
148 | (log/trace ,,,)
149 | (log/finest ,,,)
150 | ```
151 |
152 | If you are using `lambdaisland.glogi.console`, then these levels will also
153 | influence with `js/console` method is used, as well as the color used to print
154 | the namespace name.
155 |
156 | ### Spy
157 |
158 | There is a convenience macro `spy` which you can use to have a quick look at a
159 | value. It outputs the form, the value, and returns the value so you can simply
160 | wrap any expression you want to see the value. Spy expressions are logged at the
161 | `:debug` level.
162 |
163 | ``` clojure
164 | (let [x (spy (+ 1 1))
165 | y (spy (+ x x))]
166 | (+ x y))
167 | ;;=> 6
168 | ```
169 |
170 | ```
171 | [my.ns] {:spy (+ 1 1) :=> 2}
172 | [my.ns] {:spy (+ x x) :=> 4}
173 | ```
174 |
175 | As you may have noticed, unlike `js/console.log` or `prn`, `spy` returns
176 | the value of the expression so you can wrap expressions with it without
177 | disrupting the flow of your program.
178 |
179 | If you pass multiple values to spy, all will be printed:
180 |
181 | ``` clojure
182 | (fn [{:keys [host port path protocol] :as config}]
183 | (spy host port)
184 | ;;...
185 | )
186 | ```
187 |
188 | ```
189 | [my.ns] {:spy [host "localhost" port 8080]}
190 | ```
191 |
192 | This will be logged slighly diferently on Clojure. This is because Pedestal
193 | doesn't directly support multiple values to spy, and Glogc tries to be a
194 | thin wrapper:
195 |
196 | ```
197 | [my.ns] {:spy [host port] :value ["localhost" 8080]}
198 | ```
199 | If you call spy on multiple values, the last value will be returned, analogous
200 | to `do`.
201 |
202 | ### Special keys
203 |
204 | Two keywords have a special meaning in logging calls.
205 |
206 | - `:exception` use this if you want to log an exception, this will make sure you
207 | get a proper stacktrace in the browser console
208 | - `:lambdaisland.glogi/logger` name of the logger to use, defaults to `(str *ns*)`
209 |
210 | ### Controlling colorization
211 |
212 | When using `lambdaisland.glogi.console/install!` it will try to detect what the
213 | best logging strategy is for your environment.
214 |
215 | - if cljs-devtools is detected then it will log ClojureScript objects directly
216 | - if it detects a browser that is not pre-chromium IE/Edge, then it will use `console.log` `"%c"` CSS-based colorization
217 | - all other cases it logs plain text
218 |
219 | This behaviour can be changed by setting `lambdaisland.glogi.console.colorize`
220 | in `:closure-defines` in your compiler options.
221 |
222 | - `"auto"` the autodetect behavior described above (default)
223 | - `"raw"` always log ClojureScript objects directly
224 | - `"true"` format using CSS
225 | - `"false"` format as plain text
226 |
227 | ### Timestamps
228 |
229 | You can have each message get prefixed with a timestamp like this:
230 |
231 | ```
232 | :closure-defines
233 | {lambdaisland.glogi.console/timestamp "true"}
234 | ```
235 |
236 | ### Logging in production
237 |
238 | Production builds typically have `goog.DEBUG` set to `false`. This strips out
239 | some development checks, it also strips out logging. If you still want to see
240 | logs on production then add this to your ClojureScript compiler options:
241 |
242 | ``` clojure
243 | :closure-defines {goog.DEBUG false
244 | goog.debug.LOGGING_ENABLED true}
245 | ```
246 |
247 | ### Use with Pedestal
248 |
249 | The `lambdaisland.glogc` namespace provides a cross-platform (cljc) API, which
250 | uses Glogi on ClojureScript, and `io.pedestal.log` on Clojure. This way it's
251 | easy to do logging from `cljc` code, or just to have a consistent logging setup
252 | without having to wonder what kind of file you are in.
253 |
254 | Note that the pedestal.log dependency is "BYO" (bring your own), you need to add
255 | it explicitly to your dependencies.
256 |
257 | ``` clojure
258 | (ns my.ns
259 | (:require [lambdaisland.glogc :as log))
260 |
261 | (log/debug :foo :bar)
262 | ```
263 |
264 | `goog.log` has more distinct log levels than Pedestal. We provide macros for all
265 | of them, on Clojure they simply map to the nearest equivalent.
266 |
267 | - finest -> trace
268 | - finer -> trace
269 | - fine -> debug
270 | - config -> info
271 |
272 | ### Supported by Nextjournal
273 |
274 | Many thanks to [Nextjournal](https://nextjournal.com/) for coming up with an interesting problem, and giving me the opportunity to explore and solve it.
275 |
276 |
277 | ## Lambda Island Open Source
278 |
279 |
280 |
281 |
282 |
283 | glogi is part of a growing collection of quality Clojure libraries created and maintained
284 | by the fine folks at [Gaiwan](https://gaiwan.co).
285 |
286 | Pay it forward by [becoming a backer on our Open Collective](http://opencollective.com/lambda-island),
287 | so that we may continue to enjoy a thriving Clojure ecosystem.
288 |
289 | You can find an overview of our projects at [lambdaisland/open-source](https://github.com/lambdaisland/open-source).
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 | ## Contributing
298 |
299 | Everyone has a right to submit patches to glogi, and thus become a contributor.
300 |
301 | Contributors MUST
302 |
303 | - adhere to the [LambdaIsland Clojure Style Guide](https://nextjournal.com/lambdaisland/clojure-style-guide)
304 | - write patches that solve a problem. Start by stating the problem, then supply a minimal solution. `*`
305 | - agree to license their contributions as MPL 2.0.
306 | - not break the contract with downstream consumers. `**`
307 | - not break the tests.
308 |
309 | Contributors SHOULD
310 |
311 | - update the CHANGELOG and README.
312 | - add tests for new functionality.
313 |
314 | If you submit a pull request that adheres to these rules, then it will almost
315 | certainly be merged immediately. However some things may require more
316 | consideration. If you add new dependencies, or significantly increase the API
317 | surface, then we need to decide if these changes are in line with the project's
318 | goals. In this case you can start by [writing a pitch](https://nextjournal.com/lambdaisland/pitch-template),
319 | and collecting feedback on it.
320 |
321 | `*` This goes for features too, a feature needs to solve a problem. State the problem it solves, then supply a minimal solution.
322 |
323 | `**` As long as this project has not seen a public release (i.e. is not on Clojars)
324 | we may still consider making breaking changes, if there is consensus that the
325 | changes are justified.
326 |
327 |
328 |
329 | ## License
330 |
331 | Copyright © 2019-2021 Arne Brasseur and Contributors
332 |
333 | Licensed under the term of the Mozilla Public License 2.0, see LICENSE.
334 |
335 |
--------------------------------------------------------------------------------
/bb.edn:
--------------------------------------------------------------------------------
1 | {:deps
2 | {lambdaisland/open-source {:git/url "https://github.com/lambdaisland/open-source"
3 | :git/sha "78c2b3753aea55c31f581793580de2025b59fc1e"}}}
4 |
--------------------------------------------------------------------------------
/bin/kaocha:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -ex
4 |
5 | [ -d node_modules/ws ] || npm install ws
6 |
7 | clojure -A:dev:pedestal:test:cljs764 -M -m kaocha.runner "$@"
8 | clojure -A:dev:pedestal:test:cljs844 -M -m kaocha.runner "$@"
9 | clojure -A:dev:pedestal:test:cljs896 -M -m kaocha.runner "$@"
10 | clojure -A:dev:pedestal:test:cljs-latest -M -m kaocha.runner "$@"
11 |
--------------------------------------------------------------------------------
/bin/proj:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bb
2 |
3 | (ns proj (:require [lioss.main :as lioss]))
4 |
5 | (lioss/main
6 | {:license :mpl
7 | :inception-year 2019
8 | :description "A thin wrapper around `goog.log` inspired by `pedestal.log`."
9 | :aliases-as-optional-deps [:pedestal]
10 | :old-group-id "lambdaisland"
11 | :group-id "com.lambdaisland"})
12 |
13 | ;; Local Variables:
14 | ;; mode:clojure
15 | ;; End:
16 |
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src" "resources"]
2 | :deps {}
3 |
4 | :aliases
5 | {:dev
6 | {:extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.18"}}}
7 |
8 | ;; Before Closure logging API changes
9 | :cljs764
10 | {org.clojure/clojurescript {:mvn/version "1.10.764"}}
11 |
12 | ;; After Closure logging API changes
13 | :cljs844
14 | {org.clojure/clojurescript {:mvn/version "1.10.844"}}
15 |
16 | ;; After more Closure logging API changes
17 | :cljs896
18 | {org.clojure/clojurescript {:mvn/version "1.10.896"}}
19 |
20 | :cljs-latest
21 | {org.clojure/clojurescript {:mvn/version "RELEASE"}}
22 |
23 | :test
24 | {:extra-paths ["test"]
25 | :extra-deps {lambdaisland/kaocha {:mvn/version "1.71.1119"}
26 | com.lambdaisland/kaocha-cljs {:mvn/version "1.4.130"}
27 | lambdaisland/kaocha-junit-xml {:mvn/version "RELEASE"}}}
28 |
29 | :pedestal
30 | {:extra-deps {io.pedestal/pedestal.log {:mvn/version "0.5.10"}}}}}
31 |
--------------------------------------------------------------------------------
/dev.cljs.edn:
--------------------------------------------------------------------------------
1 | ^{:watch-dirs [#_"test" "src"]
2 | ;;:css-dirs ["resources/public/css"]
3 | ;;:auto-testing true
4 | }
5 | {:main lambdaisland.glogi.demo
6 | :closure-defines {goog.DEBUG false
7 | goog.debug.LOGGING_ENABLED true}}
8 |
--------------------------------------------------------------------------------
/interaction/walkthrough.cljs:
--------------------------------------------------------------------------------
1 | (ns walkthrough
2 | (:require [lambdaisland.glogi :as log]))
3 |
4 | (def captured (atom []))
5 |
6 | (log/add-handler (fn [record] (swap! captured conj record)))
7 |
8 | (log/debug :test :ing)
9 |
10 | (log/set-levels '{:glogi/root :off
11 | walkthrough :debug})
12 |
13 | @captured
14 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.lambdaisland
5 | glogi
6 | 1.3.169
7 | glogi
8 | A thin wrapper around `goog.log` inspired by `pedestal.log`.
9 | https://github.com/lambdaisland/glogi
10 | 2019
11 |
12 | Lambda Island
13 | https://lambdaisland.com
14 |
15 |
16 | UTF-8
17 |
18 |
19 |
20 | MPL-2.0
21 | https://www.mozilla.org/media/MPL/2.0/index.txt
22 |
23 |
24 |
25 | https://github.com/lambdaisland/glogi
26 | scm:git:git://github.com/lambdaisland/glogi.git
27 | scm:git:ssh://git@github.com/lambdaisland/glogi.git
28 | 57c3e36164bc43fdecccfa93265b685f245fa50e
29 |
30 |
31 |
32 | io.pedestal
33 | pedestal.log
34 | 0.5.10
35 | true
36 |
37 |
38 |
39 | src
40 |
41 |
42 | src
43 |
44 |
45 | resources
46 |
47 |
48 |
49 |
50 | org.apache.maven.plugins
51 | maven-compiler-plugin
52 | 3.8.1
53 |
54 | 1.8
55 | 1.8
56 |
57 |
58 |
59 | org.apache.maven.plugins
60 | maven-jar-plugin
61 | 3.2.0
62 |
63 |
64 |
65 | 57c3e36164bc43fdecccfa93265b685f245fa50e
66 |
67 |
68 |
69 |
70 |
71 | org.apache.maven.plugins
72 | maven-gpg-plugin
73 | 1.6
74 |
75 |
76 | sign-artifacts
77 | verify
78 |
79 | sign
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | clojars
89 | https://repo.clojars.org/
90 |
91 |
92 |
93 |
94 | clojars
95 | Clojars repository
96 | https://clojars.org/repo
97 |
98 |
99 |
--------------------------------------------------------------------------------
/resources/clj-kondo.exports/com.lambdaisland/glogi/config.edn:
--------------------------------------------------------------------------------
1 | {:linters
2 | {:type-mismatch
3 | {:namespaces
4 | {lambdaisland.glogi
5 | {info {:arities {:varargs {:args [{:op :rest :spec [:any :any]}]}}}
6 | error {:arities {:varargs {:args [{:op :rest :spec [:any :any]}]}}}
7 | warn {:arities {:varargs {:args [{:op :rest :spec [:any :any]}]}}}
8 | debug {:arities {:varargs {:args [{:op :rest :spec [:any :any]}]}}}}}}}}
9 |
--------------------------------------------------------------------------------
/src/lambdaisland/glogc.cljc:
--------------------------------------------------------------------------------
1 | (ns lambdaisland.glogc
2 | "Provide a logging API that you can use anywhere (clj or cljs), by using
3 | pedestal-log or glogi.
4 |
5 | See https://lambdaisland.com/blog/2020-09-28-logging-in-practice-glogi-pedestal
6 | for some usage tips.
7 | "
8 | #?(:cljs (:require-macros [lambdaisland.glogc]))
9 | (:require [lambdaisland.glogi :as glogi]
10 | #?(:clj [io.pedestal.log :as pedestal])))
11 |
12 | #?(:clj
13 | (do
14 |
15 | (defmacro target [& {:keys [cljs clj]}]
16 | `(if (:ns ~'&env) ~cljs ~clj))
17 |
18 | (defmacro finest [& keyvals] ;; goog.log
19 | (target :clj (#'pedestal/log-expr &form :trace keyvals)
20 | :cljs (#'glogi/log-expr &form :finest keyvals)))
21 |
22 | (defmacro finer [& keyvals] ;; goog.log
23 | (target :clj (#'pedestal/log-expr &form :trace keyvals)
24 | :cljs (#'glogi/log-expr &form :finer keyvals)))
25 |
26 | (defmacro trace [& keyvals]
27 | (target :clj (#'pedestal/log-expr &form :trace keyvals)
28 | :cljs (#'glogi/log-expr &form :trace keyvals)))
29 |
30 | (defmacro fine [& keyvals] ;; goog.log
31 | (target :clj (#'pedestal/log-expr &form :debug keyvals)
32 | :cljs (#'glogi/log-expr &form :fine keyvals)))
33 |
34 | (defmacro debug [& keyvals]
35 | (target :clj (#'pedestal/log-expr &form :debug keyvals)
36 | :cljs (#'glogi/log-expr &form :debug keyvals)))
37 |
38 | (defmacro config [& keyvals] ;; goog.log
39 | (target :clj (#'pedestal/log-expr &form :info keyvals)
40 | :cljs (#'glogi/log-expr &form :config keyvals)))
41 |
42 | (defmacro info [& keyvals]
43 | (target :clj (#'pedestal/log-expr &form :info keyvals)
44 | :cljs (#'glogi/log-expr &form :info keyvals)))
45 |
46 | (defmacro warn [& keyvals]
47 | (target :clj (#'pedestal/log-expr &form :warn keyvals)
48 | :cljs (#'glogi/log-expr &form :warn keyvals)))
49 |
50 | (defmacro error [& keyvals]
51 | (target :clj (#'pedestal/log-expr &form :error keyvals)
52 | :cljs (#'glogi/log-expr &form :error keyvals)))
53 |
54 | (defmacro spy
55 | ([expr]
56 | (target :clj `(pedestal/spy ~expr)
57 | :cljs `(glogi/spy ~expr)))
58 | ([expr & exprs]
59 | (target :clj `(last (pedestal/spy [ ~expr ~@exprs]))
60 | :cljs `(glogi/spy ~expr ~@exprs))))
61 |
62 | (defmacro with-context [ctx-map & body]
63 | `(pedestal/with-context ~ctx-map ~@body))
64 |
65 | (def format-name pedestal/format-name)
66 | (def counter pedestal/counter)
67 | (def gauge pedestal/gauge)
68 | (def histogram pedestal/histogram)
69 | (def meter pedestal/meter)))
70 |
--------------------------------------------------------------------------------
/src/lambdaisland/glogi.clj:
--------------------------------------------------------------------------------
1 | (ns lambdaisland.glogi)
2 |
3 | (defn- log-expr [form level keyvals]
4 | (let [keyvals-map (apply array-map keyvals)
5 | formatter (::formatter keyvals-map 'identity)]
6 | `(when ~(with-meta 'goog.debug.LOGGING_ENABLED {:tag 'boolean})
7 | (log ~(::logger keyvals-map (str *ns*))
8 | ~level
9 | (~formatter
10 | ~(-> keyvals-map
11 | (dissoc ::logger)
12 | (assoc :line (:line (meta form)))))
13 | ~(:exception keyvals-map)))))
14 |
15 | (defmacro shout [& keyvals]
16 | (log-expr &form :shout keyvals))
17 |
18 | (defmacro error [& keyvals]
19 | (log-expr &form :error keyvals))
20 |
21 | (defmacro severe [& keyvals]
22 | (log-expr &form :severe keyvals))
23 |
24 | (defmacro warn [& keyvals]
25 | (log-expr &form :warn keyvals))
26 |
27 | (defmacro info [& keyvals]
28 | (log-expr &form :info keyvals))
29 |
30 | (defmacro debug [& keyvals]
31 | (log-expr &form :debug keyvals))
32 |
33 | (defmacro config [& keyvals]
34 | (log-expr &form :config keyvals))
35 |
36 | (defmacro trace [& keyvals]
37 | (log-expr &form :trace keyvals))
38 |
39 | (defmacro fine [& keyvals]
40 | (log-expr &form :fine keyvals))
41 |
42 | (defmacro finer [& keyvals]
43 | (log-expr &form :finer keyvals))
44 |
45 | (defmacro finest [& keyvals]
46 | (log-expr &form :finest keyvals))
47 |
48 | (defmacro spy
49 | ([form]
50 | (let [res (gensym)]
51 | `(let [~res ~form]
52 | ~(log-expr &form :debug [:spy `'~form
53 | :=> res])
54 | ~res)))
55 | ([form & forms]
56 | ;; using cons to make explicit that it's a prepend
57 | (let [forms (cons form forms)
58 | syms (repeatedly (count forms) gensym)
59 | ;; map/mapcat take multiple collections
60 | bindings (mapcat list syms forms)
61 | spy-vec (vec (mapcat (fn [form sym]
62 | ;; or `'~form as above, but if you don't write a
63 | ;; lot of macros that's a bit harder to parse
64 | [(list 'quote form) sym])
65 | forms
66 | syms))]
67 | `(let [~@bindings]
68 | ~(log-expr &form :debug [:spy spy-vec])
69 | ~(last syms)))))
70 |
--------------------------------------------------------------------------------
/src/lambdaisland/glogi.cljs:
--------------------------------------------------------------------------------
1 | (ns lambdaisland.glogi
2 | (:require [goog.log :as glog]
3 | [goog.debug.Console :as Console]
4 | [goog.array :as array]
5 | [clojure.string :as str]
6 | [goog.object :as gobj])
7 | (:import [goog.debug Console FancyWindow DivConsole])
8 | (:require-macros [lambdaisland.glogi]))
9 |
10 | (def ^js Level
11 | (if (exists? glog/Level)
12 | glog/Level
13 | goog.debug.logger.Level))
14 |
15 | ;; Wrappers around goog.log methods which changed in Closure v20210302, so we
16 | ;; can retain backward compatibility. The static method call is the newer
17 | ;; version.
18 |
19 | (defn- goog-setLevel [^js logger ^js level]
20 | (if (exists? glog/setLevel)
21 | (^:cljs.analyzer/no-resolve glog/setLevel logger level)
22 | (.setLevel logger level)))
23 |
24 | (defn- goog-logRecord [^js logger ^js record]
25 | (if (exists? glog/publishLogRecord)
26 | (^:cljs.analyzer/no-resolve glog/publishLogRecord logger record)
27 | (.logRecord logger record)))
28 |
29 | (defn- goog-addHandler [^js logger ^js handler]
30 | (if (exists? glog/addHandler)
31 | (glog/addHandler logger handler)
32 | (.addHandler logger handler)))
33 |
34 | (defn- goog-removeHandler [^js logger ^js handler]
35 | (if (exists? glog/removeHandler)
36 | (glog/removeHandler logger handler)
37 | (.removeHandler logger handler)))
38 |
39 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
40 |
41 | (def ^private logger-handlers-prop "__glogi_handlers__")
42 |
43 | (defn name-str [x]
44 | (cond
45 | (= :glogi/root x)
46 | ""
47 |
48 | (string? x)
49 | x
50 |
51 | (simple-ident? x)
52 | (name x)
53 |
54 | (qualified-ident? x)
55 | (str (namespace x) "/" (name x))
56 |
57 | :else
58 | (str x)))
59 |
60 | (defn logger
61 | "Get a logger by name, and optionally set its level. Name can be a string
62 | keyword, or symbol. The special keyword :glogi/root returns the root logger."
63 | ([n]
64 | (glog/getLogger (name-str n) js/undefined))
65 | ([n level]
66 | (glog/getLogger (name-str n) level)))
67 |
68 | (def root-logger (logger ""))
69 |
70 | (defn- predefined-level [name]
71 | (when (exists? glog/Level)
72 | (glog/Level.getPredefinedLevel name)))
73 |
74 | (def levels
75 | ;; Hacky way to make this work across versions. (.-OFF Level) does not work in
76 | ;; the new version after advanced compilation, but they have added a lookup
77 | ;; function.
78 | {:off (or (.-OFF Level) (predefined-level "OFF"))
79 | :shout (or (.-SHOUT Level) (predefined-level "SHOUT"))
80 | :severe (or (.-SEVERE Level) (predefined-level "SEVERE"))
81 | :warning (or (.-WARNING Level) (predefined-level "WARNING"))
82 | :info (or (.-INFO Level) (predefined-level "INFO"))
83 | :config (or (.-CONFIG Level) (predefined-level "CONFIG"))
84 | :fine (or (.-FINE Level) (predefined-level "FINE"))
85 | :finer (or (.-FINER Level) (predefined-level "FINER"))
86 | :finest (or (.-FINEST Level) (predefined-level "FINEST"))
87 | :all (or (.-ALL Level) (predefined-level "ALL"))
88 |
89 | ;; pedestal style
90 | :trace (or (.-FINER Level) (predefined-level "FINER"))
91 | :debug (or (.-FINE Level) (predefined-level "FINE"))
92 | :warn (or (.-WARNING Level) (predefined-level "WARNING"))
93 | :error (or (.-SEVERE Level) (predefined-level "SEVERE"))})
94 |
95 | (defn level [lvl]
96 | (get levels lvl))
97 |
98 | (defn level-value
99 | "Get the numeric value of a log level (keyword)."
100 | [lvl]
101 | (.-value (level lvl)))
102 |
103 | (defn make-log-record [level message name exception]
104 | (let [LogRecord (if (exists? goog.debug.LogRecord)
105 | goog.debug.LogRecord
106 | goog.log.LogRecord)
107 | record (new LogRecord level message name)]
108 | (when exception
109 | (.setException record exception))
110 | record))
111 |
112 | (defn log
113 | "Output a log message to the given logger, optionally with an exception to be
114 | logged."
115 | ([name lvl message]
116 | (log name lvl message nil))
117 | ([name lvl message exception]
118 | (when glog/ENABLED
119 | (when-let [l (logger name)]
120 | (goog-logRecord l (make-log-record (level lvl) message name exception))))))
121 |
122 | (defn set-level
123 | "Set the level (a keyword) of the given logger, identified by name, or :root for
124 | the root logger."
125 | [name lvl]
126 | (assert (contains? levels lvl))
127 | (some-> (logger name) (goog-setLevel (level lvl))))
128 |
129 | (defn ^:export set-levels
130 | "Convenience function for setting several levels at one.
131 |
132 | Takes a map of logger name => level keyword. The logger name can be a string,
133 | keyword, or symbol. The keyword :glogi/root refers to the root logger and is
134 | equivalent to using an empty string.
135 |
136 | This function is exported so it is still available in optimized builds to set
137 | levels from the javascript console. In this case use nested arrays and
138 | strings. Use `:root` for the root logger.
139 |
140 | ``` javascript
141 | lambdaisland.glogi.set_levels([[\"\" \"debug\"] [\"lambdaisland\" \"trace\"]])
142 | ```
143 | "
144 | [lvls]
145 | (doseq [[logger level] lvls
146 | :let [level (if (string? level) (keyword level) level)]]
147 | (set-level logger level)))
148 |
149 | (defn enable-console-logging!
150 | "Log to the browser console. This uses goog.debug.Console directly,
151 | use [lambdaisland.glogi.console/install!] for a version that plays nicely with
152 | cljs-devtools."
153 | []
154 | (when-let [^js instance Console/instance]
155 | (.setCapturing instance true)
156 | (let [instance (Console.)]
157 | (set! Console/instance instance)
158 | (.setCapturing instance)))
159 | nil)
160 |
161 | (defn console-autoinstall!
162 | "Log to the browser console if the browser location contains Debug=true."
163 | []
164 | (Console/autoInstall)
165 | nil)
166 |
167 | (defn popup-logger-window!
168 | "Pop up a browser window which will display log messages. Returns the FancyWindow instance."
169 | []
170 | (doto (FancyWindow.)
171 | (.setEnabled true)))
172 |
173 | (defn log-to-div!
174 | "Log messages to an element on the page. Returns the DivConsole instance."
175 | [element]
176 | (doto (DivConsole. element)
177 | (.setCapturing true)))
178 |
179 | (defn- logger-glogi-handlers [logger]
180 | (gobj/get logger logger-handlers-prop))
181 |
182 | (defn- swap-handlers! [logger f & args]
183 | (gobj/set
184 | logger logger-handlers-prop
185 | (apply f (logger-glogi-handlers logger) args)))
186 |
187 | (defn add-handler
188 | "Add a log handler to the given logger, or to the root logger if no logger is
189 | specified. The handler is a function which receives a map as its argument.
190 |
191 | A given handler-fn is only added to a given logger once, even when called
192 | repeatedly."
193 | ([handler-fn]
194 | (add-handler "" handler-fn))
195 | ([name handler-fn]
196 | (let [logger (logger name)
197 | log-record-handler
198 | (fn [^goog.log/LogRecord record]
199 | (handler-fn {:sequenceNumber (.-sequenceNumber_ record)
200 | :time (.-time_ record)
201 | :level (keyword (str/lower-case (.-name (.-level_ record))))
202 | :message (.-msg_ record)
203 | :logger-name (.-loggerName_ record)
204 | :exception (.-exception_ record)}))]
205 | (when logger
206 | (when-let [handler (get (logger-glogi-handlers logger) handler-fn)]
207 | (goog-removeHandler logger handler))
208 | (swap-handlers! logger assoc handler-fn log-record-handler)
209 | (some-> logger (goog-addHandler log-record-handler))))))
210 |
211 | (defn remove-handler
212 | ([handler-fn]
213 | (remove-handler "" handler-fn))
214 | ([name handler-fn]
215 | (let [logger (logger name)]
216 | (when logger
217 | (when-let [handler (get (logger-glogi-handlers logger) handler-fn)]
218 | (goog-removeHandler logger handler))
219 | (swap-handlers! logger dissoc handler-fn)))))
220 |
221 | ;; Retained for backward compatibility, but we don't add the same handler twice
222 | ;; to the same logger.
223 | (def add-handler-once add-handler)
224 |
225 | (set-level :root :info)
226 |
--------------------------------------------------------------------------------
/src/lambdaisland/glogi/console.cljs:
--------------------------------------------------------------------------------
1 | (ns lambdaisland.glogi.console
2 | (:require [lambdaisland.glogi :as glogi]
3 | [lambdaisland.glogi.print :as print]
4 | [goog.object :as gobj]
5 | [goog.debug.Console :as Console]))
6 |
7 | ;; By default we do CSS colorization on non-IE browsers only. You can change
8 | ;; this in your build with :closure-defines. Possible values: "auto" "true" "false"
9 | (goog-define colorize "auto")
10 | (goog-define timestamp "false")
11 |
12 | (defn log-method [level]
13 | (condp #(>= %2 %1) (glogi/level-value level)
14 | (glogi/level-value :severe) "error"
15 | (glogi/level-value :warning) "warn"
16 | (glogi/level-value :info) "info"
17 | (glogi/level-value :config) "log"
18 | "log"))
19 |
20 | (defn timestamp-now []
21 | (subs (.toISOString (js/Date.)) 11 23))
22 |
23 | (defn format-raw [{:keys [level logger-name message exception]}]
24 | [(str "[" logger-name "]") message])
25 |
26 | (defn format-css [{:keys [level logger-name message exception]}]
27 | (if (= "true" timestamp)
28 | (-> ["" []]
29 | (print/add (timestamp-now) :white :black)
30 | (print/add " " :black :white)
31 | (print/format level logger-name message))
32 | (print/format level logger-name message)))
33 |
34 | (defn format-plain [{:keys [level logger-name message exception]}]
35 | [(str (when (= "true" timestamp)
36 | (str (timestamp-now) " "))
37 | "[" logger-name "]")
38 | (pr-str message)])
39 |
40 | (defn make-console-log [format]
41 | (fn [{:keys [logger-name level exception] :as record}]
42 | (let [method-name (log-method level)
43 | method (or (gobj/get js/console method-name)
44 | js/console.log)]
45 | (apply method (format record))
46 | (when exception
47 | (method (str "[" logger-name "]") (str exception) "\n" (.-stack exception))))))
48 |
49 | (defonce console-log-raw (make-console-log format-raw))
50 | (defonce console-log-css (make-console-log format-css))
51 | (defonce console-log-plain (make-console-log format-plain))
52 |
53 | ;; backward compatibility
54 | (defonce format format-plain)
55 | (defonce console-log console-log-plain)
56 |
57 | (defn devtools-installed? []
58 | (and (exists? js/devtools.core.installed_QMARK_)
59 | (js/devtools.core.installed_QMARK_)))
60 |
61 | (defn- browser? []
62 | (exists? js/window))
63 |
64 | (defn- ie? []
65 | (and (browser?)
66 | (exists? js/window.navigator)
67 | (exists? js/window.navigator.userAgent)
68 | ;; IE and pre-chromium EDGE don't support %c
69 | (or (> (.indexOf js/window.navigator.userAgent "MSIE") -1)
70 | (> (.indexOf js/window.navigator.userAgent "Trident") -1))))
71 |
72 | (defn select-handler []
73 | (case colorize
74 | "auto"
75 | (cond
76 | (devtools-installed?) console-log-raw
77 | (and (browser?) (not (ie?))) console-log-css
78 | :else console-log-plain)
79 | "raw"
80 | console-log-raw
81 | "true"
82 | console-log-css
83 | "false"
84 | console-log-plain))
85 |
86 | (defn install! []
87 | ;; Disable goog.debug.Console if it's been enabled (e.g. by Figwheel), we do
88 | ;; console logging now
89 | (when-let [^js instance Console/instance]
90 | (.setCapturing instance false))
91 |
92 | (glogi/add-handler-once (select-handler)))
93 |
--------------------------------------------------------------------------------
/src/lambdaisland/glogi/demo.cljs:
--------------------------------------------------------------------------------
1 | (ns lambdaisland.glogi.demo
2 | (:require [lambdaisland.glogi :as glogi]
3 | [lambdaisland.glogi.console :as console]))
4 |
5 | (console/install!)
6 | ;; (glogi/enable-console-logging!)
7 |
8 | (glogi/warn :msg "oh no!" )
9 |
10 | (try
11 | (throw (js/Error. "oh no!"))
12 | (catch js/Error e
13 | (glogi/warn :msg "so far so good"
14 | :exception e)))
15 |
--------------------------------------------------------------------------------
/src/lambdaisland/glogi/print.cljs:
--------------------------------------------------------------------------------
1 | (ns lambdaisland.glogi.print
2 | (:require [lambdaisland.glogi :as glogi]
3 | [goog.object :as gobj]))
4 |
5 | ;; https://github.com/chriskempson/base16-tomorrow-scheme/blob/master/tomorrow.yaml
6 |
7 | (def colors
8 | {:white "#ffffff"
9 | :gray1 "#e0e0e0"
10 | :gray2 "#d6d6d6"
11 | :gray3 "#8e908c"
12 | :gray4 "#969896"
13 | :gray5 "#4d4d4c"
14 | :gray6 "#282a2e"
15 | :black "#1d1f21"
16 | :red "#c82829"
17 | :orange "#f5871f"
18 | :yellow "#eab700"
19 | :green "#718c00"
20 | :turqoise "#3e999f"
21 | :blue "#4271ae"
22 | :purple "#8959a8"
23 | :brown "#a3685a"})
24 |
25 | (defn level-color [level]
26 | (condp <= (glogi/level-value level)
27 | (glogi/level-value :severe) :red
28 | (glogi/level-value :warning) :orange
29 | (glogi/level-value :info) :blue
30 | (glogi/level-value :config) :green
31 | (glogi/level-value :fine) :yellow
32 | (glogi/level-value :finer) :gray3
33 | (glogi/level-value :finest) :gray4
34 | :gray2))
35 |
36 | (defn add
37 | ([[res res-css] s]
38 | [(str res s) res-css])
39 | ([[res res-css] s color]
40 | [(str res "%c" (str s) "%c") (conj res-css (str "color:" (get colors color)) "color:black")])
41 | ([[res res-css] s fg bg]
42 | [(str res "%c" (str s) "%c") (conj res-css
43 | (str "color:" (get colors fg)
44 | ";background-color:" (get colors bg))
45 | "color:black;background-color:inherit")]))
46 |
47 | (defn print-console-log-css [res value]
48 | (cond
49 | (= ::comma value)
50 | (add res ", " :gray2)
51 |
52 | (= ::space value)
53 | (add res " ")
54 |
55 | (keyword? value)
56 | (add res value :blue)
57 |
58 | (symbol? value)
59 | (add res value :green)
60 |
61 | (string? value)
62 | (add res (pr-str value) :turqoise)
63 |
64 | (map-entry? value)
65 | (-> res
66 | (print-console-log-css (key value))
67 | (add " ")
68 | (print-console-log-css (val value)))
69 |
70 | (or (instance? cljs.core/PersistentArrayMap value)
71 | (instance? cljs.core/PersistentHashMap value))
72 | (as-> res %
73 | (add % "{" :purple)
74 | (reduce print-console-log-css % (interpose ::comma value))
75 | (add % "}" :purple))
76 |
77 | (map? value) ;; non-standard map implementation
78 | (as-> res %
79 | (add % (str "#" (let [t (type value)
80 | n (.-name t)]
81 | (if (empty? n)
82 | (pr-str t)
83 | n)) " ") :brown)
84 | (add % "{" :purple)
85 | (reduce print-console-log-css % (interpose ::comma value))
86 | (add % "}" :purple))
87 |
88 | (set? value)
89 | (as-> res %
90 | (add % "#{" :purple)
91 | (reduce print-console-log-css % (interpose ::space value))
92 | (add % "}" :purple))
93 |
94 | (vector? value)
95 | (as-> res %
96 | (add % "[" :purple)
97 | (reduce print-console-log-css % (interpose ::space value))
98 | (add % "]" :purple))
99 |
100 | (instance? cljs.core.PersistentQueue value)
101 | (-> res
102 | (add "#queue " :brown)
103 | (recur (into [] value)))
104 |
105 | (seq? value)
106 | (as-> res %
107 | (add % "(" :brown)
108 | (reduce print-console-log-css % (interpose ::space value))
109 | (add % ")" :brown))
110 |
111 | (satisfies? IAtom value)
112 | (-> res
113 | (add "#atom " :brown)
114 | (recur @value))
115 |
116 | (uuid? value)
117 | (-> res
118 | (add "#uuid " :brown)
119 | (recur (str value)))
120 |
121 | (object? value)
122 | (-> res
123 | (add "#js " :brown)
124 | (recur (reduce #(assoc %1 (keyword %2) (gobj/get value %2)) {} (js/Object.keys value))))
125 |
126 | (array? value)
127 | (-> res
128 | (add "#js " :brown)
129 | (recur (into [] value)))
130 |
131 | :else
132 | (add res (pr-str value) :gray5)))
133 |
134 | (defn format
135 | ([level logger-name value]
136 | (format ["" []] level logger-name value))
137 | ([init level logger-name value]
138 | (let [color (level-color level)
139 | [res res-css] (-> init
140 | (add "[" :white color)
141 | (add logger-name :white color)
142 | (add "]" :white color)
143 | (add " ")
144 | (print-console-log-css value))]
145 | (cons res res-css))))
146 |
--------------------------------------------------------------------------------
/test/lambdaisland/glogc_test.cljs:
--------------------------------------------------------------------------------
1 | (ns lambdaisland.glogc-test
2 | (:require [lambdaisland.glogc :as logc]
3 | [lambdaisland.glogi :as log]
4 | [clojure.test :refer [deftest testing is are use-fixtures run-tests join-fixtures]]))
5 |
6 | (enable-console-print!)
7 |
8 | (deftest smoke-test
9 | (let [captured (atom [])
10 | handler (fn [record] (swap! captured conj record))
11 | example-val 5]
12 | (log/add-handler handler)
13 | (log/set-levels '{lambdaisland.glogc-test :info})
14 | (logc/warn :get :these :log :messages)
15 | (logc/fine :not :you)
16 | (logc/info :to :show :up :here)
17 | (is (= example-val (logc/spy example-val)))
18 | (is (= example-val (logc/spy :spy1 example-val)))
19 | (is (= [{:sequenceNumber 0
20 | :level :warning
21 | :message {:get :these :log :messages :line 14}
22 | :logger-name "lambdaisland.glogc-test"
23 | :exception nil}
24 | {:sequenceNumber 0
25 | :level :info
26 | :message {:to :show :up :here :line 16}
27 | :logger-name "lambdaisland.glogc-test" :exception nil}]
28 | (map #(dissoc % :time) @captured)))
29 | (log/remove-handler handler)))
30 |
31 |
--------------------------------------------------------------------------------
/test/lambdaisland/glogi_test.cljs:
--------------------------------------------------------------------------------
1 | (ns lambdaisland.glogi-test
2 | (:require [lambdaisland.glogi :as log]
3 | [clojure.test :refer [deftest testing is are use-fixtures run-tests join-fixtures]]))
4 |
5 | (enable-console-print!)
6 |
7 | (deftest smoke-test
8 | (let [captured (atom [])
9 | handler (fn [record] (swap! captured conj record))
10 | example-val 5]
11 | (log/add-handler handler)
12 | (log/set-levels '{lambdaisland.glogi-test :info})
13 | (log/warn :get :these :log :messages)
14 | (log/fine :not :you)
15 | (log/info :to :show :up :here)
16 | (is (= [{:sequenceNumber 0
17 | :level :warning
18 | :message {:get :these :log :messages :line 13}
19 | :logger-name "lambdaisland.glogi-test"
20 | :exception nil}
21 | {:sequenceNumber 0
22 | :level :info
23 | :message {:to :show :up :here :line 15}
24 | :logger-name "lambdaisland.glogi-test" :exception nil}]
25 | (map #(dissoc % :time) @captured)))
26 | (log/set-levels '{lambdaisland.glogi-test :debug}) ;Debug covers spy
27 | (log/spy example-val)
28 | (log/spy :spy1 example-val)
29 | (log/remove-handler handler)
30 | (is (= (log/level-value :warn) 900))))
31 |
32 |
--------------------------------------------------------------------------------
/tests.edn:
--------------------------------------------------------------------------------
1 | #kaocha/v1
2 | {:tests [{:id :cljs
3 | :type :kaocha.type/cljs}]}
4 |
--------------------------------------------------------------------------------