Newish to JVM debugging.
I've worked on supporting other products based on VxWorks written in C/C++. Within that environment we were able to do symbol lookups on a live system and peek or poke memory to get an idea what the software was doing or to alter it when other "normal" configuration options weren't available.
Now I'm supporting java applications. For issues that aren't readily reproducible within our labs, we are reduced to recompiling with additional instrumentation and performing binary replacements to gather more information to understand what is happening within the sw.
These are always on applications that folks don't take kindly to restarting.
Is there any similar type of debugging that can be taken for JVM applications? These are running on customer sites where using a conventional debugger is not an option for a number of reasons.
Please no lectures on how the application is poorly designed for support-ability. That's a given, we're just a couple of guys in support that have to figure it out the best we can.
thanks,
Abe
I've been in similar situation, where stopping application in debugger triggered timeouts on lower layers, and adding instrumentation was annoying because of restarts.
For me the solution was to add more logging statements.
I've used slf4j API, with logback implementation, I've enabled JMX on JVM and used it to enable/disable logging as needed and/or change classes that were logged.
If you use slf4j/logback the right way, there is very little overhead for disabled log statement, so I was able to use them liberally.
This way I could turn on "debug instrumentation", without annoying users with restarts.
Now some code:
This is testbed for experiment
package pl.gov.mofnet.giif.logondemand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogOnDemand {
private static final Logger log = LoggerFactory.getLogger(LogOnDemand.class);
public static void main(String []args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
log.debug("i: {}", i);
Thread.sleep(1000);
}
}
}
This file needs to be put in default package (or anywhere on classpath).
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<jmxConfigurator />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date [%thread] %-5level %logger{25} - %msg%n</Pattern>
</layout>
</appender>
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>
This code has compile time dependencies on org.slf4j:slf4j-api:1.7.7 and runtime dependency on ch.qos.logback:logback-classic:1.1.2
Run this code, in Java 7 you do not have to enable JMX explicitly if you connect to JMX from same machine. You will see that there is no output on console save for initial Logback configuration messages.
Start jconsole, connect to this process, in MBeans tab you will find ch.qos.logback.classic node, deep below it there are operations.
Edit parameters to setLoggerLevel, set p1 to package name of your class, in this case pl.gov.mofnet.giif and p2 to debug. Press setLoggerLevel button to run this operation, you should see log messages on console. To disable logging reset logger level to info or higher.
Related
The Spring logging documentation is available here and describes the different logging possibilities. However, something I have not been able to find is the priority of the different configurations. More concretely, in application.properties I enabled logging for showing basic authentication failures by doing:
logging.level.org.springframework.security.web.authentication.www=DEBUG
This works fines and enabled the logs I expect to see. Then I checked if I could override the configuration above via log4j2.xml by including:
<Logger name="org.springframework.security.web.authentication.www" level="ERROR" additivity="false">
<AppenderRef ref="CONSOLE"/>
</Logger>
The change in log4j2.xml did not cause any effect. So my impression is that the logging configuration included in applications.properties takes priority over log4j2.xml (or it is read after). The only note I can see in the documentation that may be related is:
Since logging is initialized before the ApplicationContext is created,
it is not possible to control logging from #PropertySources in Spring
#Configuration files. The only way to change the logging system or
disable it entirely is through System properties.
So my assumption is that the following will happen:
Logging is configured according to log4j2.xml.
Spring loads the Application context and then the logging configuration in application.properties is taken and "wins".
So my question is, if someone knows how this is supposed to work without guessing (a link will be appreciated). NOTE: I am interested because I want to be sure that no log4j2.xml will disable the spring-security logging.
Thanks in advance!
application.properties/application.yaml take precedence over log4j2.xml - search for precedence here
Environment variables take precedence over application.properties/application.yaml
A fuller precedence list is here
I'm having an issue with some missing logs from GKE container in Cluod Logging.
I have an Spring boot application deployed on GKE with Log4j2. All the logs generated by the application are always writted to Cloud Logging, so if I execute 100 transactions in parallel using Jmeter I can search for all the logs in Cloud logging without problems (Logs at the beginning, middle and end of the rest controller).
Now I am migrating from Log4j2 to Logback to have a full integration with Cloud Logging, I'm following this guide: https://docs.spring.io/spring-cloud-gcp/docs/current/reference/html/logging.html
After the migration, updating only the log dependency from Log4j to Logback I can still see my logs on Cloud Logging but I'm having a weird issue with some missing logs.
For example if I send 30 parallel transactions using Jmeter I can see all the logs generated by the service, basically I'm searching for each message like this:
"This is a message "
"This is the mid of controller"
"End of trx, cleaning MDC context : "
Loggers looks like this:
Logger.info("Starting transaction: ", transactionId).
Logger.info("This is the mid of controller").
Logger.info("End of trx, cleaning MDC context : ", transactionId).
MDC.clear();
return response.
I'm searching for messages generated at the start of the rest controller, some logs at the middle of the controller and logs generated at the end of the controller, just before the "return reponse."
So if I send 30 trx in parallel using Jmeter I can find all the Loggers in Cloud Logging, but if I repeat the same 30 trx 1 min later I can find logs, but not all the logs. For example I can find:
30 of **Starting transaction:**,
22 of "This is the mid of controller"
2 of "End of trx, cleaning MDC context : "
Then if I repeat
20 of **Starting transaction:**,
0 of "This is the mid of controller"
0 of "End of trx, cleaning MDC context : "
If I wait 5 minutes and repeat
30 of **Starting transaction:**,
30 of "This is the mid of controller"
30 of "End of trx, cleaning MDC context : "
Even in some cases I can't literally find 0 logs for an specific transaction.
In all the cases the response of the service is always good, I mean even when I can't see all the logs I know the service is working fine because I can receive a 200 success and the expected response in the body. Also there are no inconsistencies in the response, everything is just working fine.
Sorry for the long intro but now the questions.
1 - Is Cloud Logging skipping similar logs? I'm always sending the same transaction in jmeter for all the cases, so the only difference between transactions is the transactionId (generated at the beginning of the rest controller)
2 - If I send a request manually using postman, I can find all the logs. Could Cloud Logging be skipping similar logs generated almost at the same time with parallel transactions?
I have tested the same cases on my local and everything is working fine, even if I send 100 transactions in parallel each second in a long loop I can find all the logs generated by the service (I'm wirtting the logs to a file), so I'm only having this issue in GKE.
Also I understand that #RestController is thread safe, so I'm not seeing inconsistencies in the logs or responses.
I'm using MDC with the configuration in Logback includeMDC, basically I'm adding the transactionId to the MDC context MDC.put("transactionId", transactionId), if I'm not wrong MDC is also thread safe, so it should not be the problem.
My logback file looks like this.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/cloud/gcp/autoconfigure/logging/logback-appender.xml"/>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<appender name="CONSOLE_JSON_APP" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.springframework.cloud.gcp.logging.StackdriverJsonLayout">
<includeTraceId>true</includeTraceId>
<includeSpanId>true</includeSpanId>
<includeLevel>true</includeLevel>
<includeThreadName>true</includeThreadName>
<includeMDC>true</includeMDC>
<includeLoggerName>true</includeLoggerName>
<includeContextName>true</includeContextName>
<includeMessage>true</includeMessage>
<includeFormattedMessage>true</includeFormattedMessage>
<includeExceptionInMessage>true</includeExceptionInMessage>
<includeException>true</includeException>
<serviceContext>
<service>APP-LOG</service>
</serviceContext>
</layout>
</encoder>
</appender>
<appender name="CONSOLE_JSON_EXT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.springframework.cloud.gcp.logging.StackdriverJsonLayout">
<projectId>${projectId}</projectId>
<includeTraceId>true</includeTraceId>
<includeSpanId>true</includeSpanId>
<includeLevel>true</includeLevel>
<includeThreadName>true</includeThreadName>
<includeMDC>true</includeMDC>
<includeLoggerName>true</includeLoggerName>
<includeContextName>true</includeContextName>
<includeMessage>true</includeMessage>
<includeFormattedMessage>true</includeFormattedMessage>
<includeExceptionInMessage>true</includeExceptionInMessage>
<includeException>true</includeException>
<serviceContext>
<service>EXT-LOG</service>
</serviceContext>
</layout>
</encoder>
</appender>
<!-- Loggers-->
<root level="INFO" name="info-log">
<appender-ref ref="LOCAL_EXTERNAL_DEP"/>
</root>
<logger name="com.example.test.service" level="INFO" additivity="false">
<appender-ref ref="LOCAL_APP" />
</logger>
</configuration>
The restController looks like this.
#RestController
public class TestServiceController {
#PostMapping("/evaluate")
public Response evaluate(#RequestBody Request request) {
UUID transactionId = UUID.randomUUID();
Logger.info("Starting transaction: ", transactionId ).
MDC.put("transactionId", transactionId.toString());
//Some java code here (Only simple things)
Logger.info("This is the mid of controller").
//Some java code here (Only simple things)
Logger.info("End of trx, cleaning MDC context : ", transactionId).
MDC.clear();
return transaction.getResponse();
}
}
At this moment my only guess is that Cloud Logging is skipping similar logs generated in a short period of time (Basically parallels executions).
Try adjusting the flushing settings. For example, set flushLevel to DEBUG. Docs about flushLevel: https://docs.spring.io/spring-cloud-gcp/docs/current/reference/html/logging.html#_log_via_api
I've seen the issue you described when applications aren't configured to flush logs directly to stdout/stderr.
I have a kotlin desktop application with gradle builder.
I added Exposed ORM framework for my sqlite DB.
Then I noticed this framework generates a lot of logs that I don't want to see in console (I want to see only my logs generated io.github.microutils:kotlin-logging).
Is there any way to disable logs from Exposed using gradle properties?
To disable (or change logging level) you have to check your logger framework implementation documentation. Both kotlin-logging and slf4j (which used by kotlin-logging) just provide facades for logging.
For example, if you use logback you could update your configuration to show only warns and errors from an Exposed:
<configuration>
// another code here
<logger name="Exposed" level="warn" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
</configuration>
My application is based on Jdk8, groovy 2.4 as language and on the top of Spring Framework. As a logger I'm using 'logback' dependencies. (group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.8'). Basically, RollingFileAppender is working fine for me, But currently, I have some additional requirement.
For an example,
Suppose the specific logger function is invoked at 2018-05-16 11:08:50, A record entered into error.log and no rolling file is created. Well, when the next execution occurs approximately after 6 minutes at 2018-05-16 11:15:05, A fresh file is created error.2018-05-16-11-08.log and error.log file is refreshed with only the new message. This behavior is as per the documentation. But currently, I'm in need of instant creation of log file as soon as the rolling complete. In that case, the new file error.2018-05-16-11-08.log which is created actually at 2018-05-16 11:15:05, I need to create the same file actually at 2018-05-16 11:09:00 (means as soon as the minute based rolling is complete).
<appender name="ERROR_LOG"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/error_log/error.%d{yyyy-MM-dd-HH-mm}.log
</fileNamePattern>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}\t%msg%n</pattern>
</encoder>
</appender>
I know there is a way, simply eliminating input property. If I omit that property, The problem partially gets resolved for me. But, in that case, error.log will not be created which have to be retained.
Please, feel free to ask me anything regurding this issue.
I need to log to splunk from AWS Lambda using Java8 runtime. It uses spring framework and I added logback splunk appender to the project. There are no errors and the logs don't seem to show up in splunk. The splunk admin mentioned that there are no requests received on splunk server. When I tried to invoke the REST API manually, the log show up in splunk. So the connectivity from AWS Lambda to splunk server is good. The splunk appender seem to be invoking the API in async fashion and I added a 50seconds sleep at the end of the AWS Lambda code to see whether it is an issue with VM exiting before the async step completes. No luck yet. How do I debug further?
Code snippet:-
public class LambdaApp implements RequestHandler<String, Object>
{
private static final Logger LOGGER = LoggerFactory.getLogger(LambdaApp.class);
private static final Logger SPLUNK_LOGGER = LoggerFactory.getLogger("splunk.logger");
#Override
public Object handleRequest(String event, Context context)
{
SPLUNK_LOGGER.info("AWS Lambda start");
try {
Thread.sleep(50000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
return "handled";
}
Maven dependency:-
<dependency>
<groupId>com.splunk.logging</groupId>
<artifactId>splunk-library-javalogging</artifactId>
<version>1.5.2</version>
</dependency>
Logback configuration:-
<appender name="http" class="com.splunk.logging.HttpEventCollectorLogbackAppender">
<url>https://a.b.c.d:8088</url>
<token>valid-token</token>
<disableCertificateValidation>true</disableCertificateValidation>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>{%msg}</pattern>
</layout>
</appender>
<logger name ="splunk.logger" level="DEBUG">
<appender-ref ref="http" />
</logger>
The first step is to add batch_size_count to rule out any issues with the HttpEventCollectorLogbackAppender not flushing to Splunk.
<appender name="http" class="com.splunk.logging.HttpEventCollectorLogbackAppender">
<url>https://a.b.c.d:8088</url>
<token>valid-token</token>
<batch_size_count>1</batch_size_count>
<disableCertificateValidation>true</disableCertificateValidation>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>{%msg}</pattern>
</layout>
</appender>
You should also verify that you are using Splunk 6.3+ on the receiving end since HTTP Event Collector requires a minimum of v6.3
You have to specify a valid / existing Splunk index or the log entry will be dropped silently on the floor.
Setting batch_size_count to 1 will make sure that every log entry gets flushed. Unfortunately everything's written asynchronously no matter how you configure it (even though "sequential" is the default sendMode), so flushing isn't sufficient to make sure it's actually flushed. For that you need to set terminationTimeout to a value (in milliseconds) that's "long enough", but not too long to significantly impact your lambda.
Note that terminationTimeout is applied on each flush and not just during whatever they think is "termination". They also use busy polling to implement it. It was apparently implemented by someone that doesn't really understand how to write multi-threaded code or the implications of a thread spinning every few milliseconds to poll the state of another thread.