How to access log4j variables in log4j.properties file - clojure

I am unable to access variables in my log4j.properties file from a clojure.clj file.
Specifically, I need to be able to use some clojure logic to see what level my log4j.rootLogger level is set to. Is there a way to reference/import/:require my log4j file within my clj file so that I can use some logic on the variables setup in the log4jfile?
I have the following in my log4j.properties file.
log4j.rootLogger=INFO, stdout
I want to be able to be able to use an if statement in my exceptions.clj file to be able to confirm when the log4j.rootlogger value in my log4j.properties file is set to INFO or OFF or DEBUG.
(if (log4j.rootlogger=INFO) (prn "rootlogger is set to info") (prn "rootlogger is set to something else"))

I gave it a quick try assuming the following:
If you are configuring Log4j using a log4j.properties it means you are using Log4j 1.2 (which has reached end-of-life BTW)
I created a new project with lein new app log4jdemo, added a dependency on the Log4j JAR (got the artifact name from this page):
project.clj
(defproject log4jdemo "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.0"]
[log4j/log4j "1.2.17"]]
:main ^:skip-aot log4jdemo.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
Next, I modified the file core.clj generated from the template with the following:
src/log4jdemo/core.clj
(ns log4jdemo.core
(:gen-class)
(:import [org.apache.log4j Logger]))
(defn get-logging-level []
(-> (Logger/getRootLogger)
.getEffectiveLevel
str))
(defn -main
[& args]
(println "Effective level:" (get-logging-level)))
Finally, let's add a configuration file. Note that the properties file needs to be placed in the src folder so that when we bundle the whole thing, it can be found as a resource (I'm using the WARN log level, but you can try any other Level):
src/log4j.properties
log4j.rootLogger=WARN, stdout
Finally, we build the project as an Uberjar and run it:
$ lein uberjar
Compiling log4jdemo.core
Created /tmp/log4jdemo/target/uberjar/log4jdemo-0.1.0-SNAPSHOT.jar
Created /tmp/log4jdemo/target/uberjar/log4jdemo-0.1.0-SNAPSHOT-standalone.jar
$ java -jar target/uberjar/log4jdemo-0.1.0-SNAPSHOT-standalone.jar
log4j:ERROR Could not find value for key log4j.appender.stdout
log4j:ERROR Could not instantiate appender named "stdout".
Effective level: WARN
... and that is correct, the Log4j properties file had been set to WARN in the example (lein run would also work, BTW).

Related

Hugsql can not read my sql file

I am truely lost here. I have a very simple application. All it does is to insert a user into the user table in my Database. I using Postgres. The code is
(ns signupper.db (:require [hugsql.core :as hugsql]))
(hugsql/def-db-fns "sql/q.sql")
Inside the direvtory where db.clj is I made a directory called sql and inside of it there is a file called q.sql.
When I ran my REPL and type (require '[signupper.db :as db]) I get the following error message:
CompilerException clojure.lang.ExceptionInfo: Can not read file: sql/q.sql {}, compiling:(signupper/db.clj:4:1)
Any one has any idea?
Thanks.
Your sql directory needs to be on the path. Please check your project.clj file, under resource-paths, and verify your sql directory is accessible via one of the paths there stated.
If not, you might either move your sql directory or include the path into the resource-paths entry.
If you are using Leiningen you should add your sql folder under the :resource-paths key to your project.clj like this:
(defproject test-project "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.8.0"]]
:main ^:skip-aot test-project.core
:target-path "target/%s"
:resource-paths ["sql" "resources"] ; <-- here
:profiles {:uberjar {:aot :all}})
Hugsql expects the path relative to the directory on the classpath. (This is the same as Clojure namespaces.)
So, your code should look like this:
(ns signupper.db (:require [hugsql.core :as hugsql]))
(hugsql/def-db-fns "signupper/db/sql/q.sql")
See the example in the docs: https://www.hugsql.org/#start-sql
My experience :
If your path is like this :
"/home/user/Desktop/sql/q.sql"
project.clj must be like this -->
:resource-paths ["/home/user/Desktop/sql" "other resources"]
and core.clj -->
(hugsql/def-db-fns "q.sql")
But if you write in core.clj
(hugsql/def-db-fns "/home/user/Desktop/sql/q.sql")
Computer sees like this :
"/home/user/Desktop/sql/home/user/Desktop/sql/q.sql"
Just had the same issue.
Apparently, the function def-db-fns starts the path from the folder src and not the root of the project.
I think one way you can solve this is putting your file inside the src directory, and call the function with
(hugsql/def-db-fns "q.sql")

Running a leiningen Clojure project in LightTable

I'm trying to use Clojure to run my Leiningen project. Even though LightTable says it's connected in the connections pane, it won't execute unless I call the main function manually.
project.clj:
(defproject lein-test "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]]
:main lein-test.core)
core.clj:
(ns lein-test.core)
(defn -main [& args]
(println "asdf"))
When I eval the entire file with ctrl+shift+enter, I see nil by the ns and nothing in the console. If I add
(-main)
then the console shows 'asdf'. lein run in the command prompt yields the expected behavior. What am I missing?
Control+Shift+Enter evaluates the namespace.
Evaluating a namespace should not run any of its functions, unless you call them at the top level. In a given codebase there should ideally be only one function that is called at the top level (conventionally, the -main function), and one should set this up not by calling it in the namespace code, but as you have, via configuration.
Everything is working as expected here. You can put a call to (-main) in a commented block or a temporary section of the file for convenience while developing, or call it directly from the repl interface.

How does one pre-load a clojure file in the leiningen repl?

I have some clojure functions that I would like pre-loaded when I start the clojure REPL. The functions aren't much use unless you are using them within the context of a REPL.
If it helps, I generally use leiningen to start a clojure REPL for me.
How can I tell clojure (or leiningen, if it's not available through flat clojure) to pre-load a clojure file containing these definitions for me?
There are several ways to do this described in the leiningen sample project
one of my favorite methods is so put the code you want in the default repl namespace into
/path/to/project/dev/user.clj:
(ns user)
(def foo 42)
and add a line like this into the project.clj file:
(defproject hello "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]]
:source-paths ["dev"])
This makes it clear that this is for dev while still getting it loaded into the default namespace.
When you run nrepl-jack-in form emacs or "lein repl" form the shell, you should be greeted with a user> namespace with your code loaded:
; nREPL 0.1.6
user> foo
42

Project-level Leiningen Plugin

In Leiningen versions 1.x.x I was able to define a lein foo task valid only in a single project by putting the following in that project's project.clj:
(defproject tester "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.4.0"]])
;; Create a task, "foo"
(ns leiningen.foo
(:require (leiningen [uberjar :as uberjar])))
(defn foo [project & args]
(println "Do something here first, then make the uberjar.")
(uberjar/uberjar project))
You can get a little more information about this here:
http://nakkaya.com/2010/02/25/writing-leiningen-plugins-101/
In 2.x.x, I'm not able to do this anymore (i.e., I get 'foo' is not a task. It seems way, way overkill for me to have to start a separate project for this task. Is it still possible to define a task within project.clj for leiningen 2.x.x?
The short answer is "no", but it is still fairly easy to define a project level task: Add :eval-in-leiningen true to your defproject definition and move the task definition to src/leiningen/foo.clj.
You can do this by using .lein-classpath to point to a directory outside of src containing the tasks. For example, if you have the plugin in src/leiningen/foo.clj, you can do, at the project root:
$ mkdir tasks
$ mv src/leiningen tasks/
$ echo tasks > .lein-classpath
The reason you might want to avoid :eval-in-leiningen true is that it has some funny behaviors when you're trying to do AOT compilation for a main class. Specifically, you get:
Compilation failed: java.io.IOException: No such file or directory, compiling:(testproj/core.clj:1)
When trying to compile/run a even a simple test example. More information at:
https://github.com/technomancy/leiningen/issues/769

Standalone clojure app

I'm a beginner with clojure, only starting it yesterday.
I have gathered that a simple way to create a standalone app is with leiningen lein new foo.
I tried to create a hello world test project with leiningen. I added :main and :aot directives to project.clj, added :gen-class to the core.clj file and tried lein run, but I get errors about class definition not found.
Exception in thread "main" java.lang.NoClassDefFoundError:
Caused by: java.lang.ClassNotFoundException:
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
The core.clj file
(ns test.core
(:gen-class))
(defn -main [& args] (println "Hello main"))
And the project.clj file
(defproject test "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:main test.core
:aot [test.core]
:dependencies [[org.clojure/clojure "1.2.1"]])
Edit: After further testing, it seems like copying the project to my desktop works as is, which I think points to that the environment on my laptop is somehow borked, but I don't know how.
The environment on desktop is clojure from repositories and leiningen from AUR. On laptop the clojure is from clojure.org and leining is from github.
[UPDATE April 2013]
Leiningen 2, which has been officially released for some time, includes the concept of project templates. By default, Leiningen provides an app template that provides what you need out of the box. Try:
lein new app my-project
You will see that Leiningen creates the familiar project template, but also includes:
The default namespace of my-project.core as the :main entry in your project.clj file
The :gen-class form in the namespace declaration of my-project.core
A default -main function in the my-project.core namespace
For those who cannot yet use Leiningen 2, the lein-newnew plugin provides an equivalent experience under Leiningen 1.
[/UPDATE]
To build a project that, when run, prints "Hello World!", you'd do as follows (revision of your process above):
Setup
lein new my-project
cd my-project
lein deps
You should now have a basic structure in place and the Clojure jar in your lib folder.
Write a Function
Now edit src/my_project/core.clj with your editor of choice, adding the following below the (ns ...) form:
(defn -main []
(println "Hello World!"))
This function is inside your my-project.core namespace. To ensure this gets run as your main, let's add a gen-class parameter to your namespace definition at the top, so that it now looks like this at the top of core.clj:
(ns my-project.core
(:gen-class :main true))
So all together, your core.clj file looks like this:
(ns my-project.core
(:gen-class :main true))
(defn -main []
(println "Hello World!"))
Configure it as the Main Function
Once you've got src/my_project/core.clj edited as above, you need to tell Leiningen (the build tool) where the "main" function for your project lives. Here's an example defproject form that does this:
(defproject my-project "1.0.0-SNAPSHOT"
:description "My Project"
:dependencies [[org.clojure/clojure "1.2.1"]]
:main my-project.core)
Now the -main function inside my-project.core becomes the entry-point for your program.
Run It
You can now have two options for running this project:
Use lein run at the command-line while at the root of your my-project project
Create a standalone jar file by running lein uberjar. You can then run the resultant jar file by running java -jar my-project-1.0.0-SNAPSHOT-standalone.jar
Figured it out. I had the latest leiningen from git, which was borked somehow. I checked out the 1.6.1 tag and ran self-install, and now it works.
I missed it, You named your project test, you can't do that change the name to something else it will work.
You say above
lein new foo
what you mean is
lein new test