Best technique to print text to console - java

I am writing a command line tool that performs a number of tests to our servers and reports an output to screen.
I am currently using log4j to print to screen and to a log file.
However, I was wondering if there was a better technique to manage all the printing from one class instead of having the "print" commands scattered all over my code.
e.g.
logger.info("Connecting to environment: " + envName);
if (cmd.hasOption(OPTION_CHECK_PRIMARY)) {
//print primary leg
String primaryLegName = env.getPrimaryLeg().getLegName();
String message = "is primary";
logger.info(String.format("%s : %-4s %s", envName, primaryLegName, message));
}
This is an example of the output that is now scattered across all my code.
Would it be best to have a Formatter class that handles all the printing?
What is the best approach to create it?
What do you think about something like:
Formatter pf = new PlainFormatter();
...
pf.printEnvironment(envName);
if (cmd.hasOption(OPTION_CHECK_PRIMARY)) {
//print primary leg
String primaryLegName = env.getPrimaryLeg().getLegName();
pf.printPrimary(envName, primaryLegName);
}

Have you tried using log4j.properties to format the text. I think if you are trying to format uniquely for each class, then this is still possible using log4j.properties.
Here is a simple example of log4j.properties
log4j.rootLogger=INFO, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
You can use Pattern in properties like so:
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

log4j has appenders to redirect log messages to different locations. quote" Multiple appenders exist for the console, files, GUI components, remote socket servers, JMS, NT Event Loggers, and remote UNIX Syslog daemons "unquote

Related

How to modify selected log messages to print to a different log file?

I use log4j to print messages to log files in my Java project.
My current log file is set to log.INFO level and this builds a log file called logFile1.log
I want to print the same set of messages to a different log file (logFile2.log), with a selected number of messages slightly modified.
Code eg:
log.info("Customer created");
log.info("Customer name:" +customerName);
<instead of the above log message, here I want to print "ABC" in logFile2.log>
log.info("Phone number added");
log.info("Phone number:" +phoneNumber);
<instead of the above log message, here I want to print "DEF" in logFile2.log>
Here is how logFile1.log would look:
Customer created
Customer name:Bob
Phone number added
Phone number:123-456-7890
Here is how I want my logFile2.log to look like:
Customer created
ABC
Phone number added
DEF
What is the most efficient way to achieve this?
There are a couple of ways to handle this that I can think of. Both assume you are using a PatternLayout of Log4j2.
The first option is to use replace attribute of the PatternLayout. However, I am not aware that it is possible to replace multiple strings with different values from a single regex. This is more useful if you are trying to mask certain fields and replace them all with "****" or similar.
The second option would be to use a PatternSelector. In this option you would either use the ScriptPatternSelector or create a custom PatternSelector to compare the message text and choose the pattern to use. This would look like:
<PatternLayout>
<ScriptPatternSelector defaultPattern="%m%n">
<Script name="BeanShellSelector" language="bsh"><![CDATA[
if (logEvent.getMessage() != null) {
String msg = logEvent.getMessage().getFormattedMessage();
if (msg.startsWith("Customer name:")) {
return "CustomerName";
} else if (msg.startsWith("Phone number:")) {
return "PhoneNumber";
}
return null;
} else {
return null;
}]]>
</Script>
<PatternMatch key="CustomerName" pattern="ABC%n"/>
<PatternMatch key="PhoneNumber" pattern="DEF%n"/>
</ScriptPatternSelector>
</PatternLayout>
log4j.logger.logFile1 =DEBUG, file1Appender
log4j.logger.logFile2= DEBUG, file2Appender
log4j.additivity.logFile1=false
log4j.additivity.logFile2=false
log4j.appender.file1Appender=org.apache.log4j.RollingFileAppender
log4j.appender.file1Appender.File=<path>/logFile1.log
log4j.appender.file1Appender.MaxFileSize=1MB
log4j.appender.file1Appender.MaxBackupIndex=1
log4j.appender.file1Appender.layout=org.apache.log4j.PatternLayout
log4j.appender.file1Appender.layout.ConversionPattern=<specify your pattern>
log4j.appender.file2Appender=org.apache.log4j.RollingFileAppender
log4j.appender.file2Appender.File=<path>/logFile2.log
log4j.appender.file2Appender.MaxFileSize=1MB
log4j.appender.file2Appender.MaxBackupIndex=1
log4j.appender.file2Appender.layout=org.apache.log4j.PatternLayout
log4j.appender.file2Appender.layout.ConversionPattern=<specify your pattern>
And then you can log to different logger the messages? Have your own extended appender and modify the message before raising the event

Why are my logs being removed?

I have an app in java using log4j as my logging jar, but when I try to debug my app and open the log file I find it short and with only the last lines I logged and the rotation files don't show any of the logs that I have seen before.
Is there something wrong with my configuration file? I have multiple projects pointing to the same log file, Could this be the problem?
Here is my log4j.properties:
log4j.rootLogger=ERROR,logfile
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=/opt/tomcat7/logs/mylogs.log
log4j.appender.logfile.MaxFileSize=500KB
# Keep one backup file
log4j.appender.logfile.MaxBackupIndex=2
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %5p (%F:%L) - %m%n
Thanks
You specified level of root logger to be ERROR, while in most cases there won't be a lot of ERROR messages - maybe one-two per many INFO level messages, while you suppressed them: ERROR level allows only ERROR and FATAL messages to be logged. And it filders out any INFO, WARN, DEBUG, TRACE level. Usually you don't use such setup in development. And in production you usually suppress only some verbose classes/packages, but not everything like in config you posted (meaning here ERROR level applies to every class).
So I suppose you should replace ERROR with INFO in your config.
UPDATE: Also likely log4j simply doesn't see your config file. I encountered this issues several times personally and each time I had a workaround like this:
public class Log4JProperties {
public static void setupLog4j(){
String log4jPathFile = Log4JProperties.class.getResource("/log4j.properties").getFile();
Properties props = new Properties();
try {
InputStream configStream = new FileInputStream(log4jPathFile);
props.load(configStream);
configStream.close();
} catch (IOException e) {
System.out.println("log4j configuration file not found");
}
LogManager.resetConfiguration();
PropertyConfigurator.configure(props);
}
}
This is not necessary when I run code from inside Intellij, but when I run using Maven, logging doesn't work without calling this function in the beginning of the execution.
When you specify MaxBackupIndex=2 it will only keep last 2 backups, if log file fills up quickly old backup files will be overridden.
You might have to increase the MaxFileSize or MaxBackupIndex based on your file logging behaviour…
https://logging.apache.org/log4php/docs/appenders/rolling-file.html

Console output of Eclipse with timestamp

I am using Eclipse for my selenium webdriver scripts.Is there any settings/tools/plugins/process so that i can see console output with timestamps.
I am using application log using log4j framework.But there is a requirement from my client that they need console output with timestamps.I have thought of using printing of time before every command,but its not an effective way of doing so.
Thanks,
Manash
Use:
log4j.appender.ConsoleAppender.layout.ConversionPattern=[%-5p] %d %c - %m%n
You have info on the ConversionPattern's printing performance here: http://www.codejava.net/coding/common-conversion-patterns-for-log4js-patternlayout
Not sure if this answer will be valid, unfamiliar with selenium and I dislike log4j (so I will give an answer that works without it).
The documentation for System can be useful, particularly setErr and setOut. You can change the output streams to streams that attach the timestamp before directing the given string to the console. Or instead of doing System.setOut & System.setErr you write a class ABC with public static printstreams like System. When you call the ABC.out.print or ABC.err.print methods, the class adds the timestamp then directs it to System.out & System.err respectively.
Can you please try the below code. The below code is working fine for me.
log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%d{dd/MM/yyyy HH:mm:ss}:
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=applicationlog path
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{dd/MM/yyyy HH:mm:ss}:
Thanks,
Sudhansu, Mindfiresolutions
http://www.mindfiresolutions.com/

Formatting slf4j to log message types with colors

I am using slf4j for logging in my Java Application. It involves a lot logging and log monitoring.
Sometimes it is really difficult to read and find information from logs when the entire log is printed in the black color with.
Just to make it more readable, is it possible to log the different kinds of messages in different colors?
For example all Error level messages in Red color or a different font size and all Info level messages in blue color and a different font size.
Any suggestions or help is welcome.
Thnx.
It's not possible to change colors of slf4j logging, because there are no formatters. SLF4J is a middleware between your application and some logging facility, for example, Log4j or Logback.
You can change colors in Log4j output, as explained here. I would recommend to use MulticolorLayout from jcabi-log
Something you have to bear in mind.
First, SLF4J is only a logging facade. How the actual log message is handled depends on the binding it used. Therefore your question is invalid, instead, you should quote which implementation you want to use (LogBack? Log4J? etc)
Second, "Coloring" is not something meaningful in most case. For example, if you are referring a plain text log file, there is nothing that we can control the coloring because they are all plain text (unless your editor have special syntax highlighting built-in for your log message format). It may be meaningful if you want to see the color in the console/terminal, or if you are outputting your log into file format that allow you to contain color information (e.g. HTML).
With these two idea in mind, here is my suggestion.
LogBack has built-in support for coloring http://logback.qos.ch/manual/layouts.html#coloring in console output. If you are looking for way to see color in console output, and you are allowed to use LogBack, this is what you are looking for.
Two solutions come to my mind. They are not colors, but alternative solutions:
In your File Appender configuration, you can configure the pattern to include the log level (error, warn, etc). Then you can grep the file, to filter messages by level.
You can configure two file appenders (for two separate log files) with different level threshold. For instance, one would log all the logs above debug level (so info, warn, error) into let's say logs.txt and the other would log only the errors logs into errors.txt
Hope it helps.
Add next appender into logback.xml to colorize logs output:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<Pattern>%d %highlight(%-5level) [%thread] %cyan(%logger{15}): %msg%n</Pattern>
</encoder>
</appender>
In addition to using a colored ConsoleAppender, you could also color the statement itself. Note this has limitations and may not work with all systems. It worked well for internal projects, particularly when you want to log that a major task in a chain is complete or simply while debugging errors.
To quote an external link:
System.out.println("\u001B[31m" + "the text is red" + "\u001B[0m");
\u001B[31m is the ANSI code to color the output red and \u001B[31m is the ANSI reset command.
public class ColorLogger {
private static final Logger LOGGER = LoggerFactory.getLogger(ColorLogger.class);
public void logDebug(String logging) {
LOGGER.debug("\u001B[34m" + logging + "\u001B[0m");
}
public void logInfo(String logging) {
LOGGER.info("\u001B[32m" + logging + "\u001B[0m");
}
public void logError(String logging) {
LOGGER.error("\u001B[31m" + logging + "\u001B[0m");
}
}
In your case, it sounds like you can try to write the contents of the aggregated log out and optionally color each statement by finding each string that concerns you.
This can alternatively just be done by printing it to an HTML page and using CSS selectively on your text.
I use filters for both logging level and package. This example comes from Spring boot application.properties
logging.level.root=warn
logging.level.org.springframework=warn
logging.level.org.hibernate=warn
logging.level.org.starmonkey.brown=DEBUG
This way I see only messages I want to see

Log4j trim common category prefix

I have a project that uses Apache Commons Logging & log4j with a large number of classes doing logging. 95% of my logs show up with the same prefix
log4j.appender.MyCompany.layout.ConversionPattern=[%d][%-5p][%c] %m%n
[2010-08-05 11:44:18,940][DEBUG][com.mycompany.projectname.config.XMLConfigSource] Loading config from [filepath]
[2010-08-05 12:05:52,715][INFO ][com.mycompany.projectname.management.ManagerCore] Log entry 1
[2010-08-05 12:05:52,717][INFO ][com.mycompany.projectname.management.ManagerCore] Log entry 2
I know with %c{1}, I can just show the last part of the category (i.e. the class name), but is there a way to trim off the common portion 'com.mycompany.projectname' from each log under that package, considering how much room it takes up on each line?
If you are using Log4j 1.2.16, you can change your layout to EnhancedPatternLayout, which lets you specify a negative value for the category parameter. From the docs:
For example, for the category name "alpha.beta.gamma" ... %c{-2} will remove two elements [from the front] leaving "gamma"
Here is a more complete example:
log4j.appender.C.layout=org.apache.log4j.EnhancedPatternLayout
log4j.appender.C.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%t] [%c{-3}] %m%n
which in your case should chop off com.mycompany.projectname.
However, this will apply to every message that is logged, even if it didn't come from your code. In other words, the category org.hibernate.engine.query.HQLQueryPlan would be trimmed to query.HQLQueryPlan, which may not be what you want.
If you need absolute control over this (i.e. you want to specifically strip out the text "com.mycompany.projectname" from every message), then you will need to implement your own Layout class. Something like this should do it:
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;
public class MyPatternLayout extends PatternLayout
{
#Override
public String format(LoggingEvent event)
{
String msg = super.format(event);
msg = msg.replace("com.mycompany.projectname", "");
return msg;
}
}
Good luck!
Use %c{2} or %C{2}. The number specifies the number of right-most components to keep.
Refer to http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html.

Categories