Update
I've made a mistake that I overlooked throughout. My logging.properties file had a trailing space in the filename that I was not aware of. I've no clue how it got in there, but once I deleted that space everything worked. My problem was that I was providing the wrong name i.e. the filename without the trailing space.
I don’t understand how java.util.logging works. I’m trying to replicate the sample code provided at:
Java Practices -> Logging messages
I first created an empty Java project in Eclipse. I created a class SimpleLogger.java in the package myapp.business. Under resources, I put logging.properties. I don’t have any compilation problems and I can step through the code, but I cannot figure out where does the output go?
SimpleLogger.java looks like:
package myapp.business;
import java.util.logging.Level;
import java.util.logging.Logger;
public final class SimpleLogger {
public static void main(String... args) {
SimpleLogger thing = new SimpleLogger();
thing.doSomething();
}
public void doSomething() {
// Log messages, one for each level
// The actual logging output depends on the configured
// level for this package. Calls to "inapplicable"
// messages are inexpensive.
fLogger.finest("this is finest");
fLogger.finer("this is finer");
fLogger.fine("this is fine");
fLogger.config("this is config");
fLogger.info("this is info");
fLogger.warning("this is a warning");
fLogger.severe("this is severe");
// In the above style, the name of the class and
// method which has generated a message is placed
// in the output on a best-efforts basis only.
// To ensure that this information is always
// included, use the following "precise log"
// style instead :
fLogger.logp(Level.INFO, this.getClass().toString(), "doSomething", "blah");
// For the very common task of logging exceptions, there is a
// method which takes a Throwable :
Throwable ex = new IllegalArgumentException("Some exception text");
fLogger.log(Level.SEVERE, "Some message", ex);
// There are convenience methods for exiting and
// entering a method, which are at Level.FINER :
fLogger.exiting(this.getClass().toString(), "doSomething");
// Display user.home directory, if desired.
// (This is the directory where the log files are generated.)
// System.out.println("user.home dir: " +
// System.getProperty("user.home") );
}
// PRIVATE
// This style has no hard-coded literals, and requires the logger
// to be non-static.
private final Logger fLogger = Logger.getLogger(this.getClass().getPackage().getName());
// This style lets the logger be static, but hard-codes a class literal.
// private static final Logger fLogger =
// Logger.getLogger(SimpleLogger.class.getPackage().getName())
// ;
// This style uses a hard-coded literal and should likely be avoided:
// private static final Logger fLogger = Logger.getLogger("myapp.business");
}
My logging.properties which is in the resources directory looks like:
# Properties file which configures the operation of the JDK
# logging facility.
# The system will look for this config file, first using
# a System property specified at startup:
#
# >java -Djava.util.logging.config.file=myLoggingConfigFilePath
#
# If this property is not specified, then the config file is
# retrieved from its default location at:
#
# JDK_HOME/jre/lib/logging.properties
# Global logging properties.
# ------------------------------------------
# The set of handlers to be loaded upon startup.
# Comma-separated list of class names.
# (? LogManager docs say no comma here, but JDK example has comma.)
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# Default global logging level.
# Loggers and Handlers may override this level
.level=INFO
# Loggers
# ------------------------------------------
# Loggers are usually attached to packages.
# Here, the level for each package is specified.
# The global level is used by default, so levels
# specified here simply act as an override.
myapp.ui.level=ALL
myapp.business.level=CONFIG
myapp.data.level=SEVERE
# Handlers
# -----------------------------------------
# --- ConsoleHandler ---
# Override of global logging level
java.util.logging.ConsoleHandler.level=SEVERE
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
# --- FileHandler ---
# Override of global logging level
java.util.logging.FileHandler.level=ALL
# Naming style for the output file:
# (The output file is placed in the directory
# defined by the "user.home" System property.)
java.util.logging.FileHandler.pattern=%h/java%u.log
# Limiting size of output file in bytes:
java.util.logging.FileHandler.limit=50000
# Number of output files to cycle through, by appending an
# integer to the base file name:
java.util.logging.FileHandler.count=1
# Style of output (Simple or XML):
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
In my run configuration in Eclipse, I have my Main class as myapp.business.SimpleLogger and my VM arguments as -Djava.util.logging.config.file=resources/logging.properties
I don't see anything on the console nor can I locate any *.log file. I'm running this on Ubuntu 16.10 if that helps.
Edit: In response to pvg
I've attempted to change the VM argument in Eclipse to: -Djava.util.logging.config.file=/home/myusername/EclipseWorkspace/Temp/resources/logging.properties
I've also tried to call it via the command line in the bin directory:
java -Djava.util.logging.config.file=/home/myusername/EclipseWorkspace/Temp/resources/logging.properties -cp . myapp.business.SimpleLogger
This too does not work i.e. I don't see any output, nor do I see a *.log file anywhere.
For me, it works only if I put the whole path in Eclipse VM arguments:
-Djava.util.logging.config.file=/whole/path/of/logging.properties
Then, the output file will be created according to what's configured in logging.properties file. In this case:
# Naming style for the output file:
# (The output file is placed in the directory
# defined by the "user.home" System property.)
java.util.logging.FileHandler.pattern=%h/java%u.log
The output file will be created in your user's home directory. In my case, the filename created was java0.log - %u means "unique number to resolve conflicts" (a.k.a. an auto-generated number to avoid having files with the same name; in my case, it was 0).
...but I cannot figure out where does the output go?
Use the following code to get the working directory and list the environment which can tell you the home directory. This example also tries to create a file handler using the LogManager settings.
public static void main(String[] args) throws IOException {
System.out.println("Working directory=" + new File(".").getCanonicalPath());
for (Map.Entry<String, String> e : System.getenv().entrySet()) {
System.out.println(e);
}
new FileHandler().close();
}
Related
With the same code, I want to run it both as a real mode and a dev mode.
So I added a service-mode argument, and it decides the port, database name, etc.
I also want to make a log file for a dev mode.
But using log4j, it seems that configuration about logging is decided when app is started.
Is there a way to change the log file path depending on a given argument?
I expected something like:
// Main.java
if (args.serviceMode is real) {
setLogging(log4j.properties)
}
else {
setLogging(log4j.properties.dev)
}
or
// log4j.properties
log4j.appender.file.File=/path/to/real/log
log4jdev.appender.file.File=/path/to/dev/log
// Main.java
if (args.serviceMode is real) {
setLogPath(log4j.appender.file.File)
}
else {
setLogPath(log4jdev.appender.file.File)
}
Any link or comment appreciated.
You can add spring.profiles in your application.yml and set new log-name
spring.profiles: dev
logging.file: new-app.log
More information you can find get here
I am compiling this Java file and I get one class file.
My task is to change the Static content "Hello" in the Class file and replace with "Hi".
How to read the Class file first, and how to replace the static content?
public class test {
public static void main(String[] args) {
System.out.println("Hello");
}
}
Is there any standard code(A template) for that ?
You need to use some Java bytecode instrumentation libraries like ASM. Good to start to read links are:
A Guide to Java Bytecode Manipulation with ASM
How To Modify Constant Pool Using ASM?
Does your solution need to be in Java? You can use Jawa to accomplish the same in Python with a lot less boilerplate than ASM and its equivalents.
Install it: pip install jawa
Then:
from jawa.constants import String
from jawa.classloader import ClassLoader
# Create a ClassLoader for the current directory
# and load your "test" class.
test = ClassLoader('.')['test']
# Find the first String with the value "Hello"
# in the constant pool.
constant = test.constants.find_one(
type_=String,
f=lambda c: c.string.value == 'Hello'
)
# Change it to your new value
constant.string.value = 'Hi'
# ... and save the new class.
with open('test.class', 'wb') as new_test:
test.save(new_test)
Result:
$ java test
Hi
Full documentation is at http://jawa.tkte.ch. Regardless of what tool you end up using it's absolutely required to read the JVM ClassFile specification or you won't really understand what's going on. You can find it at https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html.
I'm using the SimpleLogger binding for SLF4J 1.7.5. As per the docs I can use the org.slf4j.simpleLogger.logFile property in my simplelogger.properties file to specify a log file OR System.out OR System.err.
However I want to send log messages to BOTH System.out AND a log file. Does anyone know how to achieve this using SimpleLogger please? (I'm using Windows so cannot use tail -f simply to follow the log file in a console window; nor do I want to get a third party utility which emulates 'tail -f' in Windows.)
Short Answer You can't do that with SimipleLogger.
More AnswerGive up on SimpleLogger and move on to something else. You have options:
Instead of using slf4j-simple-1.x.x.jar get logback (logback-classic.jar and logback-core.jar). With logback you can define two appenders; one for the file output and one for console (also-known-as System.out) output.
Instead of using slf4j-simple.1.x.x.jar get xxx (substitute any logging system supported by slf4j) and blah blah blah (do the same as in 1 obove).
SLF4j is open source; derive your own logger (lets call it TeeLogger) that logs to System.out and a file.
Create a logging class that that sits in front of SLF4j (in your application). Have it take a Logger and a messasge then have it write the message to System.out and the Logger.
Something that I have not though about.
Here is a (super simplistic) example of #4 above:
import org.slf4j.Logger;
public class LoggyLoo
{
public static void logZoreInfo(
final Logger logger,
final String message)
{
System.out.println(message);
logger.info(message);
}
}
api 1.7 and slf4j-simple as implementation. I just can't find how to configure the logging level with this combination.
Can anyone help out?
It's either through system property
-Dorg.slf4j.simpleLogger.defaultLogLevel=debug
or simplelogger.properties file on the classpath
see https://www.slf4j.org/api/org/slf4j/simple/SimpleLogger.html for details
This is a sample simplelogger.properties which you can place on the classpath (uncomment the properties you wish to use):
# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.
# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
#org.slf4j.simpleLogger.defaultLogLevel=info
# Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used.
#org.slf4j.simpleLogger.log.xxxxx=
# Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup.
#org.slf4j.simpleLogger.showDateTime=false
# The date and time format to be used in the output messages.
# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat.
# If the format is not specified or is invalid, the default format is used.
# The default format is yyyy-MM-dd HH:mm:ss:SSS Z.
#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
# Set to true if you want to output the current thread name.
# Defaults to true.
#org.slf4j.simpleLogger.showThreadName=true
# Set to true if you want the Logger instance name to be included in output messages.
# Defaults to true.
#org.slf4j.simpleLogger.showLogName=true
# Set to true if you want the last component of the name to be included in output messages.
# Defaults to false.
#org.slf4j.simpleLogger.showShortLogName=false
In a Maven or Gradle project, a convenient place "on the classpath" is src/main/resources/simplelogger.properties.
You can programatically change it by setting the system property:
public class App {
public static void main(String[] args) {
// for the code below to work, it must be executed before the
// logger is created. see note below
System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE");
org.slf4j.Logger log = LoggerFactory.getLogger(App.class);
log.trace("trace");
log.debug("debug");
log.info("info");
log.warn("warning");
log.error("error");
}
}
The log levels are ERROR > WARN > INFO > DEBUG > TRACE.
Please note that once the logger is created the log level can't be changed. If you need to dynamically change the logging level you might want to use log4j with SLF4J.
I noticed that Eemuli said that you can't change the log level after they are created - and while that might be the design, it isn't entirely true.
I ran into a situation where I was using a library that logged to slf4j - and I was using the library while writing a maven mojo plugin.
Maven uses a (hacked) version of the slf4j SimpleLogger, and I was unable to get my plugin code to reroute its logging to something like log4j, which I could control.
And I can't change the maven logging config.
So, to quiet down some noisy info messages, I found I could use reflection like this, to futz with the SimpleLogger at runtime.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;
try
{
Logger l = LoggerFactory.getLogger("full.classname.of.noisy.logger"); //This is actually a MavenSimpleLogger, but due to various classloader issues, can't work with the directly.
Field f = l.getClass().getSuperclass().getDeclaredField("currentLogLevel");
f.setAccessible(true);
f.set(l, LocationAwareLogger.WARN_INT);
}
catch (Exception e)
{
getLog().warn("Failed to reset the log level of " + loggerName + ", it will continue being noisy.", e);
}
Of course, note, this isn't a very stable / reliable solution... as it will break the next time the maven folks change their logger.
I don't know why. I use simplelogger.properties and org.slf4j.simpleLogger.showDatetime, it's not working.
I lookup the SimpleLogger class source code and got this part of the code
static {
// Add props from the resource simplelogger.properties
InputStream in = (InputStream)AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
ClassLoader threadCL = Thread.currentThread().getContextClassLoader();
if (threadCL != null) {
return threadCL.getResourceAsStream(CONFIGURATION_FILE);
} else {
return ClassLoader.getSystemResourceAsStream(CONFIGURATION_FILE);
}
}
});
if(null != in) {
try {
simpleLoggerProps.load(in);
in.close();
} catch(java.io.IOException e) {
// ignored
}
}
showLogName = getBooleanProperty(systemPrefix + "showlogname", showLogName);
showShortName = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
showDateTime = getBooleanProperty(systemPrefix + "showdatetime", showDateTime);
showThreadName = getBooleanProperty(systemPrefix + "showthreadname", showThreadName);
dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat", dateTimeFormat);
if(showDateTime) {
try {
dateFormatter = new SimpleDateFormat(dateTimeFormat);
} catch(IllegalArgumentException e) {
Util.report("Bad date format in " + CONFIGURATION_FILE + "; reverting to default", e);
// If the format pattern is invalid - use the default format
dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
dateFormatter = new SimpleDateFormat(dateTimeFormat);
}
}
}
systemPrefix + "showdatetime" is org.slf4j.simplelogger.showdatetime
When I try write org.slf4j.simplelogger.showdatetime=true to simplelogger.properties, It works normally. I hope it can help some people.
I want to add some log.debug statements to a class I'm working on, and I'd like to see that in output when running the test. I'd like to override the log4j properties on the command line, with something like this:
-Dlog4j.logger.com.mypackage.Thingie=DEBUG
I do this kind of thing frequently. I am specifically only interested in a way to pass this on the command line. I know how to do it with a config file, and that doesn't suit my workflow.
As part of your jvm arguments you can set -Dlog4j.configuration=file:"<FILE_PATH>". Where FILE_PATH is the path of your log4j.properties file.
Please note that as of log4j2, the new system variable to use is log4j.configurationFile and you put in the actual path to the file (i.e. without the file: prefix) and it will automatically load the factory based on the extension of the configuration file:
-Dlog4j.configurationFile=/path/to/log4jconfig.{ext}
These answers actually dissuaded me from trying the simplest possible thing! Simply specify a threshold for an appender (say, "console") in your log4j.configuration like so:
log4j.appender.console.threshold=${my.logging.threshold}
Then, on the command line, include the system property -Dlog4j.info -Dmy.logging.threshold=INFO. I assume that any other property can be parameterized in this way, but this is the easiest way to raise or lower the logging level globally.
With Log4j2, this can be achieved using the following utility method added to your code.
private static void setLogLevel() {
if (Boolean.getBoolean("log4j.debug")) {
Configurator.setLevel(System.getProperty("log4j.logger"), Level.DEBUG);
}
}
You need these imports
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
Now invoke the setLogLevel method in your main() or whereever appropriate and pass command line params -Dlog4j.logger=com.mypackage.Thingie and -Dlog4j.debug=true.
log4j does not support this directly.
As you do not want a configuration file, you most likely use programmatic configuration. I would suggest that you look into scanning all the system properties, and explicitly program what you want based on this.
Based on Thorbjørn Ravn Andersens suggestion I wrote some code that makes this work
Add the following early in the main method and it is now possible to set the log level from the comand line. This have been tested in a project of mine but I'm new to log4j and might have made some mistake. If so please correct me.
Logger.getRootLogger().setLevel(Level.WARN);
HashMap<String,Level> logLevels=new HashMap<String,Level>();
logLevels.put("ALL",Level.ALL);
logLevels.put("TRACE",Level.TRACE);
logLevels.put("DEBUG",Level.DEBUG);
logLevels.put("INFO",Level.INFO);
logLevels.put("WARN",Level.WARN);
logLevels.put("ERROR",Level.ERROR);
logLevels.put("FATAL",Level.FATAL);
logLevels.put("OFF",Level.OFF);
for(String name:System.getProperties().stringPropertyNames()){
String logger="log4j.logger.";
if(name.startsWith(logger)){
String loggerName=name.substring(logger.length());
String loggerValue=System.getProperty(name);
if(logLevels.containsKey(loggerValue))
Logger.getLogger(loggerName).setLevel(logLevels.get(loggerValue));
else
Logger.getRootLogger().warn("unknown log4j logg level on comand line: "+loggerValue);
}
}
In my pretty standard setup I've been seeing the following work well when passed in as VM Option (commandline before class in Java, or VM Option in an IDE):
-Droot.log.level=TRACE
Based on #lijat, here is a simplified implementation. In my spring-based application I simply load this as a bean.
public static void configureLog4jFromSystemProperties()
{
final String LOGGER_PREFIX = "log4j.logger.";
for(String propertyName : System.getProperties().stringPropertyNames())
{
if (propertyName.startsWith(LOGGER_PREFIX)) {
String loggerName = propertyName.substring(LOGGER_PREFIX.length());
String levelName = System.getProperty(propertyName, "");
Level level = Level.toLevel(levelName); // defaults to DEBUG
if (!"".equals(levelName) && !levelName.toUpperCase().equals(level.toString())) {
logger.error("Skipping unrecognized log4j log level " + levelName + ": -D" + propertyName + "=" + levelName);
continue;
}
logger.info("Setting " + loggerName + " => " + level.toString());
Logger.getLogger(loggerName).setLevel(level);
}
}
}