Skip to content

Commit 480dc71

Browse files
committed
Log configurer. Support now log42j
1 parent 7c313ea commit 480dc71

File tree

10 files changed

+240
-41
lines changed

10 files changed

+240
-41
lines changed

docs/asciidoc/configuration.adoc

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,117 @@ To skip/ignore Jooby loading and precedence mechanism, just instantiate and set
257257
IMPORTANT: Custom configuration is very flexible. You can reuse Jooby mechanism or provide your own.
258258
The only thing to keep in mind is that environment setting must be done at very early stage, before
259259
installing extensions.
260+
261+
=== Logging
262+
263+
Jooby uses https://www.slf4j.org[Slf4j] for logging which give you some flexibility for choosing
264+
the logging framework.
265+
266+
==== Logback
267+
268+
The https://logback.qos.ch/manual/index.html[Logback] is probably the first alternative for
269+
https://www.slf4j.org[Slf4j] due its natively implements the SLF4J API. Follow the next steps to use
270+
logback in your project:
271+
272+
1) Add dependency
273+
274+
[dependency, artifactId="logback-classic"]
275+
276+
2) Creates a `logback.xml` file in the `conf` directory:
277+
278+
.logback.xml
279+
[source, xml]
280+
----
281+
<?xml version="1.0" encoding="UTF-8"?>
282+
<configuration>
283+
284+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
285+
<encoder>
286+
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
287+
</encoder>
288+
</appender>
289+
290+
<root level="INFO">
291+
<appender-ref ref="STDOUT" />
292+
</root>
293+
</configuration>
294+
----
295+
296+
That's all! https://www.slf4j.org[Slf4j] is going to redirect log message to logback.
297+
298+
==== Log4j2
299+
300+
The https://logging.apache.org/log4j[Log4j2] project is another good alternative for logging. Follow
301+
the next steps to use logback in your project:
302+
303+
1) Add dependencies
304+
305+
[dependency, artifactId="log4j-slf4j-impl, log4j-core"]
306+
307+
2) Creates a `log4j.xml` file in the `conf` directory:
308+
309+
.log4j.xml
310+
[source, xml]
311+
----
312+
<?xml version="1.0" encoding="UTF-8"?>
313+
<Configuration>
314+
<Appenders>
315+
<Console name="stdout">
316+
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable" />
317+
</Console>
318+
</Appenders>
319+
<Loggers>
320+
<Root level="INFO" additivity="true">
321+
<AppenderRef ref="stdout" />
322+
</Root>
323+
</Loggers>
324+
</Configuration>
325+
----
326+
327+
All these extensions are considered valid: `.xml`, `.propertines`, `.yaml` and `.json`. As well as `log4j2` for file name.
328+
329+
==== Environment logging
330+
331+
Logging is integrated with the environment names. So it is possible to have a file name:
332+
333+
- `logback[.name].xml` (for loggback)
334+
- `log4j[.name].xml` (for log4j2)
335+
336+
Jooby favors the environment specific logging configuration file over regular/normal logging configuration file.
337+
338+
.Example
339+
[source, bash]
340+
----
341+
conf
342+
└── logback.conf
343+
└── logback.prod.conf
344+
----
345+
346+
To use `logback.prod.conf`, start your application like:
347+
348+
`java -jar myapp.jar application.env=prod`
349+
350+
[IMPORTANT]
351+
====
352+
The logging configuration file per environment works as long you don't use *static* loggers
353+
before application has been start it. The next example won't work:
354+
355+
[source, java]
356+
----
357+
public class App extends Jooby {
358+
private static final Logger log = ...
359+
360+
public static void main(String[] args) {
361+
runApp(args, App::new);
362+
}
363+
}
364+
----
365+
366+
The `runApp` method is the one who configures the logging framework. Adding a static logger force
367+
the logging framework to configure without taking care the environment setup.
368+
369+
There are a couple of solution is for this:
370+
371+
- use an instance logger
372+
- use the getLog() method of Jooby
373+
====

docs/asciidoc/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Thank you {love}
7474
- MVC routes. Annotation and byte code generation. Similar to Spring and JAX-RS
7575
- Reactive responses: Completable futures, RxJava, Reactor and Kotlin coroutines
7676
- Configuration using https://github.com/lightbend/config[config] library
77-
- https://www.slf4j.org[slf4j] logging
77+
- https://www.slf4j.org[Slf4j] logging
7878
- Multi Server: https://https://www.eclipse.org/jetty[Jetty], https://netty.io[Netty], http://undertow.io[Undertow]
7979
- Extension system for commons technical tasks: connection pool, json, template engine, etc...
8080

docs/src/main/java/io/jooby/adoc/DocGenerator.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,21 @@ private static void languageTab(Document doc) {
180180
}
181181

182182
private static void tocItems(Document doc) {
183+
doc.select("#mvc-api-2").forEach(it -> it.attr("id", "#mvc-api"));
184+
doc.select("a")
185+
.forEach(it -> {
186+
if (it.attr("href").equals("#mvc-api-2")) {
187+
it.attr("href", "#mvc-api");
188+
}
189+
});
183190
int l = 3;
184191
while (l > 1) {
185192
Elements items = doc.body().select(".sectlevel" + l);
186193
for (Element it : items) {
187194
String parent = it.parent().select("a").first().attr("href").replace("#", "");
188195
for (Element e : it.select("li>a")) {
189-
String key = e.attr("href").replace("#", "");
196+
String key = e.attr("href").replace("#", "")
197+
.replace("mvc-api-2", "mvc-api");
190198
String newkey = Stream.of((parent + "-" + key).split("-"))
191199
.distinct()
192200
.collect(Collectors.joining("-"));

jooby/pom.xml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,6 @@
187187
<artifactId>slf4j-api</artifactId>
188188
</dependency>
189189

190-
<dependency>
191-
<groupId>ch.qos.logback</groupId>
192-
<artifactId>logback-classic</artifactId>
193-
</dependency>
194-
195190
<!-- ASM -->
196191
<dependency>
197192
<groupId>org.ow2.asm</groupId>
@@ -282,6 +277,12 @@
282277
<artifactId>okhttp</artifactId>
283278
<scope>test</scope>
284279
</dependency>
280+
281+
<dependency>
282+
<groupId>ch.qos.logback</groupId>
283+
<artifactId>logback-classic</artifactId>
284+
<scope>test</scope>
285+
</dependency>
285286
</dependencies>
286287

287288
</project>

jooby/src/main/java/io/jooby/Environment.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434

3535
public class Environment {
3636

37-
public static final String KEY = "application.env";
38-
3937
private final List<String> actives;
4038

4139
private final Config conf;

jooby/src/main/java/io/jooby/EnvironmentOptions.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
*/
1616
package io.jooby;
1717

18-
import org.jetbrains.annotations.NotNull;
19-
2018
import javax.annotation.Nonnull;
2119
import java.nio.file.Files;
2220
import java.nio.file.Path;

jooby/src/main/java/io/jooby/Jooby.java

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -491,12 +491,7 @@ public static void runApp(@Nonnull String[] args, @Nonnull ExecutionMode executi
491491
parseArguments(args).forEach(System::setProperty);
492492

493493
/** Fin application.env: */
494-
String env = System.getProperty(
495-
Environment.KEY, System.getenv().getOrDefault(Environment.KEY, "dev"))
496-
.toLowerCase()
497-
.split(",")[0];
498-
499-
logback(env.trim().toLowerCase());
494+
LogConfigurer.configure(new EnvironmentOptions().getActiveNames());
500495

501496
Jooby app = provider.get();
502497
if (app.mode == null) {
@@ -534,29 +529,6 @@ static Map<String, String> parseArguments(String... args) {
534529
return conf;
535530
}
536531

537-
public static void logback(@Nonnull String env) {
538-
String logfile = System
539-
.getProperty("logback.configurationFile", System.getenv().get("logback.configurationFile"));
540-
if (logfile != null) {
541-
System.setProperty("logback.configurationFile", logfile);
542-
} else {
543-
Path userdir = Paths.get(System.getProperty("user.dir"));
544-
Path conf = userdir.resolve("conf");
545-
String logbackenv = "logback." + env + ".xml";
546-
String fallback = "logback.xml";
547-
Stream.of(
548-
/** Environment specific inside conf or userdir: */
549-
conf.resolve(logbackenv), userdir.resolve(logbackenv),
550-
/** Fallback inside conf or userdir: */
551-
conf.resolve(fallback), userdir.resolve(fallback)
552-
).filter(Files::exists)
553-
.findFirst()
554-
.map(Path::toAbsolutePath)
555-
.ifPresent(
556-
logback -> System.setProperty("logback.configurationFile", logback.toString()));
557-
}
558-
}
559-
560532
private static void ensureTmpdir(Path tmpdir) {
561533
try {
562534
if (!Files.exists(tmpdir)) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*
14+
* Copyright 2014 Edgar Espina
15+
*/
16+
package io.jooby;
17+
18+
import javax.annotation.Nonnull;
19+
import java.nio.file.Files;
20+
import java.nio.file.Path;
21+
import java.nio.file.Paths;
22+
import java.util.ArrayList;
23+
import java.util.LinkedHashMap;
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.Optional;
27+
28+
import static java.util.Arrays.asList;
29+
30+
public final class LogConfigurer {
31+
private LogConfigurer() {
32+
}
33+
34+
public static void configure(@Nonnull String... names) {
35+
String[] keys = {"logback.configurationFile", "log4j.configurationFile"};
36+
for (String key : keys) {
37+
String file = property(key);
38+
if (file != null) {
39+
// found, reset and exit
40+
System.setProperty(key, file);
41+
return;
42+
}
43+
}
44+
Path userdir = Paths.get(System.getProperty("user.dir"));
45+
Map<String, List<Path>> options = new LinkedHashMap<>();
46+
options.put("logback.configurationFile", logbackFiles(userdir, names));
47+
options.put("log4j.configurationFile", log4jFiles(userdir, names));
48+
for (Map.Entry<String, List<Path>> entry : options.entrySet()) {
49+
Optional<Path> logfile = entry.getValue().stream().filter(Files::exists)
50+
.findFirst()
51+
.map(Path::toAbsolutePath);
52+
if (logfile.isPresent()) {
53+
System.setProperty(entry.getKey(), logfile.get().toString());
54+
break;
55+
}
56+
}
57+
}
58+
59+
private static List<Path> logbackFiles(Path basedir, String[] env) {
60+
return logFile(basedir, env, "logback", ".xml");
61+
}
62+
63+
private static List<Path> logFile(Path basedir, String[] names, String name, String ext) {
64+
Path confdir = basedir.resolve("conf");
65+
List<Path> result = new ArrayList<>();
66+
for (String env : names) {
67+
String envlogfile = name + "." + env + ext;
68+
result.add(confdir.resolve(envlogfile));
69+
result.add(basedir.resolve(envlogfile));
70+
}
71+
String logfile = name + ext;
72+
result.add(confdir.resolve(logfile));
73+
result.add(basedir.resolve(logfile));
74+
return result;
75+
}
76+
77+
private static List<Path> log4jFiles(Path basedir, String[] names) {
78+
List<Path> result = new ArrayList<>();
79+
String[] extensions = {".properties", ".xml", ".yaml", ".yml", ".json"};
80+
for (String extension : extensions) {
81+
result.addAll(logFile(basedir, names, "log4j", extension));
82+
result.addAll(logFile(basedir, names, "log4j2", extension));
83+
}
84+
return result;
85+
}
86+
87+
private static String property(String name) {
88+
return System.getProperty(name, System.getenv(name));
89+
}
90+
}

pom.xml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232

3333
<!-- logging -->
3434
<logback-classic.version>1.2.3</logback-classic.version>
35-
<slf4j.version>1.7.25</slf4j.version>
35+
<log4j.version>2.11.2</log4j.version>
36+
<slf4j.version>1.7.26</slf4j.version>
3637

3738
<!-- ASM -->
3839
<asm.version>7.0</asm.version>
@@ -390,6 +391,18 @@
390391
<version>${logback-classic.version}</version>
391392
</dependency>
392393

394+
<dependency>
395+
<groupId>org.apache.logging.log4j</groupId>
396+
<artifactId>log4j-core</artifactId>
397+
<version>${log4j.version}</version>
398+
</dependency>
399+
400+
<dependency>
401+
<groupId>org.apache.logging.log4j</groupId>
402+
<artifactId>log4j-slf4j-impl</artifactId>
403+
<version>${log4j.version}</version>
404+
</dependency>
405+
393406
<!-- commons-io -->
394407
<dependency>
395408
<groupId>commons-io</groupId>

tests/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
<artifactId>kotlin-reflect</artifactId>
7272
</dependency>
7373

74+
<dependency>
75+
<groupId>ch.qos.logback</groupId>
76+
<artifactId>logback-classic</artifactId>
77+
</dependency>
78+
7479
<!-- Test dependencies -->
7580
<dependency>
7681
<groupId>org.junit.jupiter</groupId>

0 commit comments

Comments
 (0)