I would like to understand why my Ring application is attempting to open the log file at compile time.
I have a webservice in Compojure and Ring. The application works without issue but whenever I compile the application to an uberwar or run lein ring server I get the below error from slf4j.
I don't understand what is causing the Clojure compiler to attempt to access this log file during compilation. The only clue which I have is that it only started happening when I included the Korma library.
If I create the directory I can get rid of the error, however, I want to understand why the application is attempting to open the log file at compile time.
log4j:ERROR setFile(null,true) call failed.
java.io.FileNotFoundException: /var/log/ege/myservice/myservice.log (No such file or directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
at java.io.FileOutputStream.<init>(FileOutputStream.java:133)
at org.apache.log4j.FileAppender.setFile(FileAppender.java:294)
at org.apache.log4j.RollingFileAppender.setFile(RollingFileAppender.java:207)
at org.apache.log4j.FileAppender.activateOptions(FileAppender.java:165)
at org.apache.log4j.config.PropertySetter.activate(PropertySetter.java:307)
at org.apache.log4j.config.PropertySetter.setProperties(PropertySetter.java:172)
at org.apache.log4j.config.PropertySetter.setProperties(PropertySetter.java:104)
at org.apache.log4j.PropertyConfigurator.parseAppender(PropertyConfigurator.java:809)
at org.apache.log4j.PropertyConfigurator.parseCategory(PropertyConfigurator.java:735)
at org.apache.log4j.PropertyConfigurator.configureRootCategory(PropertyConfigurator.java:615)
at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:502)
log4j configuration is a runtime configuration of your app. Korma uses log4j configuration for its usage in runtime. So, it's not surprising that when running lein ring server, korma uses log4j configuration for its logging and the log4j logging throws error cause it can not find the log directory.
As for the error thrown during lein check or lein ring war. Maybe you have some forms which attempts to connect to database or even do some query to your database in top-level of some of your namespaces.
maybe you have something like this:
(ns my-app.db
(:use korma.db)
(:use korma.core))
(def db (create-db (mysql {:db "my_db"
:user "root"
:password ""})))
;; this form below attempts to get a db connection
(def conn (get-connection db))
(defentity users)
;; this form below attempts to query the database
(select users)
when clojure compiles, it executes every form, thus, clojure connects to db or query the db. In other words, clojure runs a part of your app during compilation.
unrelated note
Everything above aside, you should separate the log4j configuration or any other runtime-configs for development environment and production environment.
I usually separate development environment and production environment configuration using :profiles in project.clj.
:profiles
{:production
{:ring
{:open-browser? false, :stacktraces? false, :auto-reload? false}
:source-paths ["prod-config"]}
:dev
{:dependencies [[ring-mock "0.1.5"] [ring/ring-devel "1.3.1"]]
:source-paths ["dev-config"]}}
I put my development's log4j.properties in dev-config dir and production's log4j.properties in prod-config dir.
Related
I would like to add some configuration to Jetty Adapter and run it with the lein-ring plugin, but i could not find any information.
I can run this configured from the main function by using lein run.
(jet/run-jetty main-handler {:port 8080 :join? false})
But I want to set those configurations in the project.clj so I can use "lein ring server".
The lein-ring documentation suggests you can put a map of options for your ring adapter in the project.clj file, like this:
:ring {:handler hello-world.core/handler
:adapter {:join? false
:port 8080}}
Though you probably do not want to use :join? false I'd think.
Leiningen provides a default directory for 'main' code, main resources, and test code, but nothing for test resources.
Coming from a maven background this is something I'd expect.
In that case, where should test resources live? Or a larger question, what's the philosophical reason why it wouldn't need a test resources directory
Test resources in Leiningen are managed using profiles. To set up a directory with test resources, you would add its path to the :resource-paths property of the :test profile (to make it available only to the test task) or :dev profile (to make it available to all dev tasks, e.g. test, run, repl, etc.)
Sample project.clj for a Maven-like project structure:
(defproject myproject "0.0.1-SNAPSHOT"
:source-paths ["src/main/clj"]
:test-paths ["src/test/clj"]
:resource-paths ["src/main/resources"]
:dev {:resource-paths ["src/test/resources"]})
When the :dev profile is active, its :resource-paths values are merged with the :resource-paths from the base project, giving you what you're looking for.
See the Leiningen docs for more information on profiles.
I have a Ring app that through deploys to production as an uberwar thing; myservice.war. In production the WAR file gets tossed into Jetty where it runs in a context that follows its name
$ curl -i -X GET http://myservice.qa1.example.com:8080/myservice/healthz
HTTP/1.1 200 OK
...
When I run locally through lein ring, I need it to run in the same context; myservice.
$lein ring server-headless
2015-10-14 14:04:03,457 level=INFO [main] Server:271 - jetty-7.6.13.v20130916
2015-10-14 14:04:03,482 level=INFO [main] AbstractConnector:338 - Started SelectChannelConnector#0.0.0.0:10313
Started server on port 10313
But the same curl goes all 404 on me locally.
$ curl -i -X GET http://localhost:10313/myservice/healthz
HTTP/1.1 404 Not Found
...
The lein ring thing deployed it onto the root context.
$ curl -i -X GET http://localhost:10313/healthz
HTTP/1.1 200 OK
...
Whats up with that? How do I direct lein ring to deploy into a context name of my choosing? I need curl -i -X GET http://localhost:10313/myservice/healthz to work from lein ring
One way to work around this issue is to create the second (standalone) set of routes for your app. You also create the second handler for the standalone case. Then you can use Leiningen profiles to specify different handlers for the standalone case and the uberwar case. The default profile is used when running the app standalone. The :uberjar profile is used when the uberwar is created. As a result, your standalone handler going to be used with lein ring server-headless and your regular handler is going to be used when the war is deployed into a container.
Not much additional code needed to create the second set of routes. You can just wrap the existing routes in a context of your choosing. Suppose the following are your routes and ring handler:
(defroutes app-routes
(GET "/healthz" [] "Hello World")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
Additional routes and handler for the standalone case would look like this:
(defroutes standalone-routes
(context "/myservice" req app-routes)
(route/not-found "Not Found"))
(def standalone-app
(wrap-defaults standalone-routes site-defaults))
Now, onto lein-ring configuration in project.clj. We want the default ring handler to point to standalone-app. The ring handler for the uberwar should point to app. The :ring entry in the project map in project.clj should look like this (adjust for your actual namespace):
:ring {:handler myservice.handler/standalone-app}
Also, merge the following into your :profiles map in project.clj:
:uberjar {:ring {:handler myservice.handler/app}}
Please be sure to use the latest version of the lein-ring plugin. Version 0.9.7 worked for me. Earlier versions, like 0.8.3, did not work because they did not use the :uberjar profile when running the uberwar task.
If you do all this, and assuming your war file is called myservice.war, the context part of the URI is going to be the same whether your app is started with lein ring server-headless or if the war file is deployed in Jetty.
$ curl http://localhost:[port]/myservice/healthz
I have a Clojure app that I can use both from the command-line, and as a Compojure app.
I did that by putting a ring handler and a main function (clojure.tools.cli) in my leiningen project.clj.
{...
:main my-app.core
:ring {:handler my-app.handler/handler }}
The handler is defined (defroutes handler ...).
Now if I want to run the CLI app, I can run lein uberjar and then java -jar arguments.
And I can also run the Compojure app via lein ring server PORT.
Now, how do I deploy the thing as a Compojure app (and not a CLI app) in a production server ? (Bonus points for explaining how lein ring server works.)
Note : I already use nginx if that can help, and I'm flexible on the container to be used.
Here's kind of the default template I use for new projects. It allows you to do dependency injection into ring apps and run the app from the command line as an uberjar. You can read more here: http://www.sparxeng.com/blog/software/improved-clojure-workflow
; handler.clj
(defn wrap-inject-deps
"Ring middleware that injects the dependencies into each ring request map"
[handler deps]
(fn [req]
(handler (assoc req :deps deps))))
(defn create-handler
"Similar to the default ring 'handler', but with a parameter to let you inject dependencies"
[deps]
(-> (routes
api-routes
web-routes
(route/resources "/"))
(kwp/wrap-keyword-params)
(params/wrap-params)
(json/wrap-json-params)
(json/wrap-json-response)
(wrap-inject-deps deps))) ; this injects dependencies
(defn start-jetty
"Launch the Jetty instance"
[deps]
(let [port (Integer. (or (-> system :env :port) 5000))
handler (create-handler deps)]
(jetty/run-jetty handler {:port port :join? false})))
; system.clj
(defn get-env
"Initialize configuration vars"
[]
{:aws-access-key-id (System/getenv "AWS_ACCESS_KEY_ID")
:aws-secret-access-key (System/getenv "AWS_SECRET_ACCESS_KEY")
:mongo-url (System/getenv "MONGO_URL"))
(defn start
"Launch dependencies such as DB connection and Jetty. Return these as a map, for use in REPL"
[& [env]]
(let [env (or env (get-env))
deps {:env env
:monger (db/init env)}
jetty (handler/start-jetty deps)]
(assoc deps :jetty jetty)))
; program.clj
(defn -main [& [port]]
"App entrypoint"
(let [env (system/get-env) ; "env" is just a map of config variables, that can be hard-coded, read from file, or whatever you want.
env (if port (assoc env :port port) env)]
(system/start env)))
You can then use leiningen profiles if you need to create multiple apps with different entrypoints from your codebase.
lein ring server fires up jetty to serve the web application you've built. It does this by starting the server with the options you specify in :ring. If you specify LEIN_NO_DEV in your environment, the server will run in production mode. Whether jetty is the right fit for your productions needs or not, is up to you to decide.
If you would like to run your web application on a different server, there are multiple options. One of them is running on Tomcat. Take another look at the documentation for lein ring, in particular the section on war files. Basically, you would call lein war or lein uberwar to generate a war file. You can then e.g. drop the war file into the webapps directory of Tomcat.
Another option would be using immutant, which has substantial documentation on installation and running applications.
I am using monger to fetch and save some data in MongoDb from my Clojure simple app. I have strong Ruby on Rails background so I am familiar with database settings per environment (development, test, production). I want to have something similar in Clojure. How can I add the environment to my code? I want to do it in Clojure-way, code as data, without any yaml files. I am using Leiningen if it changes something.
You can use Leiningen profiles feature.
In your project.clj define your profiles (most cases you need dev and prod)
:profiles {:dev {:resource-paths ["resource-dev"]}
:prod {:resource-paths ["resource-prod"]}}
Now create 2 directories resource-dev and resource-prod and create config.clj file in both of them which will have define a map to store configuration. Something like:
(ns myapp.config)
(def config {:database "dev"})
Then in your app code you can use below snippet to load the config file (only once) and access the config map:
(use 'clojure.java.io)
(def config (delay (load-file (.getFile (resource "config.clj")))))
(defn get-config []
#(force config))
Now you can use get-config function to access the config map.
Have a look at clj-boilerplate, a sample web app I created.
There's info in the README about how it understands environments out of the box and an example environment file can be seen here - but it looks something like this:
(def config
(let [env (or (System/getenv "ENVIRONMENT") "development")]
((keyword env)
{:development
{:database-url "postgres://lborges:#localhost/clj-boilerplate"}
:test
{:database-url "postgres://lborges:#localhost/clj-boilerplate-test"
:production
{:database-url (System/getenv "DATABASE_URL")}})))
I have since evolved this approach but this should get you started.
Hope this helps.
Have a look at using Confijulate (plug for a personal project!):
https://github.com/bbbates/confijulate
It allows you to define environment specific configuration maps, and specify which one to use via JVM system properties.