How to customize logging for Google App Engine Java? - java

Google App Engine Java uses java.util.logging to create logging messages. I want to modify the log messages, that are displayed in Developers Console - Monitoring - Logs. The idea is to add some additional output like username without putting it in each log message manually:
log.info("user action");
should result in an logging output like
user "testuser": user action
Therefore I created an own Formatter:
public class TestFormatter extends Formatter {
#Override
public String format(LogRecord record) {
// find out username..
return "user " + username + ": " + record.getMessage();
}
}
Setting this as formatter for the ConsoleHandler in the logging.properties has not effekt:
java.util.logging.ConsoleHandler.formatter = com.example.guestbook.TestFormatter
When deploying it in on the local machine, and trying to add it programmatically like this:
Logger rootLogger = Logger.getLogger("");
Handler[] handlers = rootLogger.getHandlers();
log.info("Handler[] size: " + handlers.length);
for(Handler h : handlers) {
log.info(h.toString());
h.setFormatter(new TestFormatter());
}
I get 2 handler, one ConsoleHandler and one DevLogHandler. But setting the formatter results in the fact that no further logs are displayed. On GAE instead I get 0 handler.
When trying to acces Logger.getGlobal() instead of Logger.getLogger(""), I get 0 Handler on the local instance and a SecurityException: No permission to modify global on GAE. This exception already arises when trying to get the list of Handlers.
Now my question: Is there a way to modify the logs of developer console in such a way? If yes, how?

As I reply I got in the past from a Google ticket I opened for a similar question
I would discourage tampering with the Loggers/Handlers used
internally by GAE.
Besides that, the Global Logger cannot be customized, you can try to it with a Logger with a custom name

Related

No logging in confluence web application

I am trying to enable logging for the below class in Confluence:
https://bitbucket.org/mryall/confluence-siteminder-authenticator/src/142de32b6be86321c9791df5dfced607314ed17d/src/main/java/com/atlassian/confluence/authenticator/siteminder/SiteMinderAuthenticator.java?at=default&fileviewer=file-view-default&_ga=2.161258619.735769127.1516003521-2111736425.1515526840
E.g. I know this line:
#SuppressWarnings("unchecked")
public Principal getUser(final HttpServletRequest request, HttpServletResponse response) {
log.info("Starting SiteMinder Authentication for: {}", request.getRequestURI());
Principal loggedInUser = getUserFromSession(request);
if (loggedInUser != null) {
log.debug("{} is already logged in.", loggedInUser.getName());
return loggedInUser;
}
is executed but nothing is written to catalina.out. In log4j.properties I have:
log4j.rootLogger=DEBUG, confluencelog, errorlog
log4j.appender.confluencelog=com.atlassian.confluence.loggin‌​g.ConfluenceHomeLogA‌​ppender
Any ideas why nothing is printed to catalina.out from the above class?
Confluence manual says:
In order to unify logging across different application servers, Confluence uses the atlassian-confluence.log as its primary log, not the application server log.
Once the initial startup sequence is complete, all logging will be to <confluence-home>/logs/atlassian-confluence.log. For example: c:/confluence/data/logs/atlassian-confluence.log.
So look for your entries from that file, not catalina.out.
Your rootLogger level is WARN. It doesn't print lower level logs like INFO, DEBUG
You can set it to DEBUG which covers INFO level.
log4j.rootLogger=DEBUG, confluencelog, errorlog
But it will print a lot including these two.
>log.info("Starting SiteMinder Authentication for: {}", request.getRequestURI());,
>log.debug("{} is already logged in.", loggedInUser.getName());
The best solution is to set package log level to DEBUG. You can add a line into your log4j.properties
log4j.logger.com.atlassian.confluence.authenticator.siteminder=DEBUG, confluencelog

Log4j2, set log level at runtime for Thread specific

We have a webserver and multiple users log in to it. We generally put log level to ERROR or INFO level. But sometimes, for debugging purpose, we need to see logs. There is one way to set it at runtime, but this process is not so good in case of loads of traffic. Important logs will be missed and also we don't know for how much time we need to keep it that way. I have written a wrapper in log4j v1.2, which just ignores the level check if userid belongs to some TestUsersList. So, it opens all logs for a particular user[a thread] only. A snippet is below-
public void trace(Object message) {
Object diagValue = MDC.get(LoggerConstants.IS_ANALYZER_NUMBER);
if (valueToMatch.equals(diagValue)) { // Some condition to check test number
forcedLog(FQCN, Level.TRACE, message, null);
return;
}
if (repository.isDisabled(Level.TRACE_INT))
return;
if (Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel()))
forcedLog(FQCN, Level.TRACE, message, null);
}
But now I have moved to log4j2, I don't want to write this wrapper again. Is there any inbuilt functionality which log4j2 provides for this?
This can be done with filters. Add a logger to the configuration that logs all the messages you want, then add a ThreadContextMapFilter that has a KeyValuePair for each user you want to log.
Then put the user ids in the Thread Context within the code.

Disable unwanted INFO-Messages from SOAP Logger

In question Filter unwanted INFO-Messages from Logger it is proposed to disable unwanted SOAP INFO logging by raising the respective log level to WARNING like so:
// Disable SOAP-internal INFO logging
Logger.getLogger("javax.enterprise.resource.webservices.jaxws.server").setLevel(Level.WARNING)
URL url = new URL("http://localhost:9999/ws/SoapControl?wsdl");
QName qname = new QName("http://example.ch/", "SoapControlImplService");
Service service = Service.create(url, qname);
SoapControl soapControl = service.getPort(SoapControl.class); // Unwanted logging happens here
This usually works, but unfortunately not all the time, i.e. the behavior is not deterministic.
Any ideas? Thanks!
Looks like you might be running into garbage collection of loggers. Pin the soap logger with a static final reference or you can add an entry into your logging.properties file to control the level on demand. Every time the logger is recreated the log level is read from the properties file.

Which Appenders should be used in distributed system? How to configure them?

I am trying to add logging component to distributed system. It is written in AspectJ to avoid chaining current source-code. I use socket appender to send logs, but I'd like to try something more effective.
I've heard I should use JMSAppender and AsyncAppender, but I failed to configure it. Should I create Receiver which gathers logs and pass them to database and to GUI (I use ChainSaw)?
I tried to follow turorial1 and tutorial2 , but they aren't clear enough.
Edit:
In a small demo I've prepared I sent 6 logs for a request (simulation of 3 components)
[2012-08-08 15:40:28,957] [request1344433228957] [Component_A] [start]
[2012-08-08 15:40:32,050] [request1344433228957] [Component_B] [start]
[2012-08-08 15:40:32,113] [request1344433228957] [Component_C] [start]
[2012-08-08 15:40:32,113] [request1344433228957] [Component_C] [end - throwing]
[2012-08-08 15:40:32,144] [request1344433228957] [Component_B] [end]
[2012-08-08 15:40:32,175] [request1344433228957] [Component_A] [end]
Using socket Appender. So my log4j.properties is:
log4j.rootLogger=DEBUG, server
log4j.appender.server=org.apache.log4j.net.SocketAppender
log4j.appender.server.Port=4712
log4j.appender.server.RemoteHost=localhost
log4j.appender.server.ReconnectionDelay=1000
so I run
>java -classpath log4j-1.2.17.jar org.apache.log4j.net.SimpleSocketServer 4712 log4j-server.properties
with configuration
log4j.rootLogger=DEBUG, CA, FA
#
log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern=[%d] [%t] [%c] [%m]%n
#
log4j.appender.FA=org.apache.log4j.FileAppender
log4j.appender.FA.File=report.log
log4j.appender.FA.layout=org.apache.log4j.PatternLayout
log4j.appender.FA.layout.ConversionPattern=[%d] [%t] [%c] [%m]%n
Then I send my logs from file to Chainsaw:
It is absolutely basic, but I want to learn how to do it better. First of all, I'd like to send logs asynchronously. Then create very simple Receiver, which e.g. can pass logs to a file.
I tried to follow tutorials I listed above, but I failed. So question is: could you provide some example configuration? Example of Receiver.java and log4.properties files?
I would use NFS or CDFS and mount a drive on all the machines. Have each application instance write to a different file. You will be able to find all the logs in one directory (or drive) no matter how many machines you use.
I wouldn't use NFS or CDFS over a global WAN with a high latency e.g. > 50 ms round trip. In this cause I have used JMS (but I didn't use log4j)
My two cents.. Whatever you do, make sure that you use asynchronous mechanism to deliver your logs to the receiver, otherwise it will eventually stall your apps. Another point, to deliver logs reliably you should consider a fail over mechanism built into the appender itself - receivers may go offline for short or long time, if you care for the logs, the fail over is definitely required. We have built similar system you describe (sorry for the add), but if you like you can use our appender (look in downloads), it's free and has the sources. There is also a video tutorial. It has fail over and flexible asynchronous mechanism plus a backup fall back.
How many appenders should you use? One appender per jvm will do all right. Config files should probably be per jvm, not sure how you intend to implement the receiver, in any case the appenders need to find your receiver which is usually host port pair at least. Regarding the database, my experience is very sour with RDBMS (we are moving to nosql) but if you don't go above couple of hundred million records, most commercial databases will do with some effort. Not a simple task I must say, took us couple of years to build commercial quality system you just drawn with few skinny rectangles :)
Finally I've found how to configure it. I put 2 files into src folder.
jndi.properties
topic.logTopic=logTopic
and log4j-jms.properties
log4j.rootLogger=INFO, stdout, jms
## Be sure that ActiveMQ messages are not logged to 'jms' appender
log4j.logger.org.apache.activemq=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=
## Configure 'jms' appender. You'll also need jndi.properties file in order to make it work
log4j.appender.jms=org.apache.log4j.net.JMSAppender
log4j.appender.jms.InitialContextFactoryName=org.apache.activemq.jndi.ActiveMQInitialContextFactory
log4j.appender.jms.ProviderURL=tcp://localhost:61616
log4j.appender.jms.TopicBindingName=logTopic
log4j.appender.jms.TopicConnectionFactoryBindingName=ConnectionFactory
Then I run my program with VM argument
-Dlog4j.configuration=log4j-jms.properties
and receive logs in class Receiver.java
public class Receiver implements MessageListener {
PrintWriter pw = new PrintWriter("result.log");
Connection conn;
Session sess;
MessageConsumer consumer;
public Receiver() throws Exception {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection conn = factory.createConnection();
Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
conn.start();
MessageConsumer consumer = sess.createConsumer(sess.createTopic("logTopic"));
consumer.setMessageListener(this);
}
public static void main(String[] args) throws Exception {
new Receiver();
}
public void onMessage(Message message) {
try {
LoggingEvent event = (LoggingEvent) ((ActiveMQObjectMessage) message).getObject();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
String nowAsString = df.format(new Date(event.getTimeStamp()));
pw.println("["+ nowAsString + "]" +
" [" + event.getThreadName()+"]" +
" ["+ event.getLoggerName() + "]" +
" ["+ event.getMessage()+"]");
pw.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I'd recommend syslog and the built in syslog appender. Use TCP for reliable logging (+Asyc appender maybe) or UDP for fire-and-forget logging.
I have a rsyslog config if you need.

Log4j: Events appear in the wrong logfile

To be able to log and trace some events I've added a LoggingHandler class to my java project. Inside this class I'm using two different log4j logger instances - one for logging an event and one for tracing an event into different files. The initialization block of the class looks like this:
public void initialize()
{
System.out.print("starting logging server ...");
// create logger instances
logLogger = Logger.getLogger("log");
traceLogger = Logger.getLogger("trace");
// create pattern layout
String conversionPattern = "%c{2} %d{ABSOLUTE} %r %p %m%n";
try
{
patternLayout = new PatternLayout();
patternLayout.setConversionPattern(conversionPattern);
}
catch (Exception e)
{
System.out.println("error: could not create logger layout pattern");
System.out.println(e);
System.exit(1);
}
// add pattern to file appender
try
{
logFileAppender = new FileAppender(patternLayout, logFilename, false);
traceFileAppender = new FileAppender(patternLayout, traceFilename, false);
}
catch (IOException e)
{
System.out.println("error: could not add logger layout pattern to corresponding appender");
System.out.println(e);
System.exit(1);
}
// add appenders to loggers
logLogger.addAppender(logFileAppender);
traceLogger.addAppender(traceFileAppender);
// set logger level
logLogger.setLevel(Level.INFO);
traceLogger.setLevel(Level.INFO);
// start logging server
loggingServer = new LoggingServer(logLogger, traceLogger, serverPort, this);
loggingServer.start();
System.out.println(" done");
}
To make sure that only only thread is using the functionality of a logger instance at the same time each logging / tracing method calls the logging method .info() inside a synchronized-block. One example looks like this:
public void logMessage(String message)
{
synchronized (logLogger)
{
if (logLogger.isInfoEnabled() && logFileAppender != null)
{
logLogger.info(instanceName + ": " + message);
}
}
}
If I look at the log files, I see that sometimes a event appears in the wrong file. One example:
trace 10:41:30,773 11080 INFO masterControl(192.168.2.21): string broadcast message was pushed from 1267093 to vehicle 1055293 (slaveControl 1)
trace 10:41:30,784 11091 INFO masterControl(192.168.2.21): string broadcast message was pushed from 1156513 to vehicle 1105792 (slaveControl 1)
trace 10:41:30,796 11103 INFO masterControl(192.168.2.21): string broadcast message was pushed from 1104306 to vehicle 1055293 (slaveControl 1)
trace 10:41:30,808 11115 INFO masterControl(192.168.2.21): vehicle 1327879 was pushed to slave control 1
10:41:30,808 11115 INFO masterControl(192.168.2.21): string broadcast message was pushed from 1101572 to vehicle 106741 (slaveControl 1)
trace 10:41:30,820 11127 INFO masterControl(192.168.2.21): string broadcast message was pushed from 1055293 to vehicle 1104306 (slaveControl 1)
I think that the problem occures everytime two event happen at the same time (here: 10:41:30,808). Does anybody has an idea how to solve my problem? I already tried to add a sleep() after the method call, but that doesn't helped ...
BR,
Markus
Edit:
logtrace 11:16:07,75511:16:07,755 1129711297 INFOINFO masterControl(192.168.2.21): string broadcast message was pushed from 1291400 to vehicle 1138272 (slaveControl 1)masterControl(192.168.2.21): vehicle 1333770 was added to slave control 1
or
log 11:16:08,562 12104 INFO 11:16:08,562 masterControl(192.168.2.21): string broadcast message was pushed from 117772 to vehicle 1217744 (slaveControl 1)
12104 INFO masterControl(192.168.2.21): vehicle 1169775 was pushed to slave control 1
Edit 2:
It seems like the problem only occurs if logging methods are called from inside a RMI thread (my client / server exchange information using RMI connections). ...
Edit 3:
I solved the problem by myself: It seems like log4j is NOT completely thread-save. After synchronizing all log / trace methods using a separate object everything is working fine. Maybe the lib is writing the messages to a thread-unsafe buffer before writing them to file?
You don't need to synchronize on your logger but on the output stream.
If you use log4j, the output should be correctly synchronized. The only way to get what you see is that two threads write to the same file at the same time.
Is it possible that you configured two appenders with the same output file? Don't do that; every appender must have it's own, distinct file name.
If you're 100% sure that every appender writes to a different file, the only option left is that you're sometimes using the wrong logger.
Could it be that somehow the initialize method is called more than once? Every call would add two new appenders that write into the same file as the existing appenders.
This question is 4 years old but I just came across this same problem in 2013 and I think I've fixed it by creating a new PatternLayout for each Appender. Hope it helps anyone else with the same issue in the future.

Categories