/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.logging.jdk;
import org.elasticsearch.common.logging.support.AbstractESLogger;
import java.util.logging.Level;
import java.util.logging.LogRecord;
/**
* A {@link LogRecord} which is used in conjunction with {@link JdkESLogger}
* with the ability to provide the class name, method name and line number
* information of the code calling the logger
*/
public class ESLogRecord extends LogRecord {
private static final long serialVersionUID = 1107741560233585726L;
private static final String FQCN = AbstractESLogger.class.getName();
private String sourceClassName;
private String sourceMethodName;
private transient boolean needToInferCaller;
public ESLogRecord(Level level, String msg) {
super(level, msg);
needToInferCaller = true;
}
@Override
public String getSourceClassName() {
if (needToInferCaller) {
inferCaller();
}
return sourceClassName;
}
@Override
public void setSourceClassName(String sourceClassName) {
this.sourceClassName = sourceClassName;
needToInferCaller = false;
}
@Override
public String getSourceMethodName() {
if (needToInferCaller) {
inferCaller();
}
return sourceMethodName;
}
@Override
public void setSourceMethodName(String sourceMethodName) {
this.sourceMethodName = sourceMethodName;
needToInferCaller = false;
}
/**
* Determines the source information for the caller of the logger (class
* name, method name, and line number)
*/
private void inferCaller() {
needToInferCaller = false;
Throwable throwable = new Throwable();
boolean lookingForLogger = true;
for (final StackTraceElement frame : throwable.getStackTrace()) {
String cname = frame.getClassName();
boolean isLoggerImpl = isLoggerImplFrame(cname);
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
if (isLoggerImpl) {
lookingForLogger = false;
}
} else {
if (!isLoggerImpl) {
// skip reflection call
if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
// We've found the relevant frame.
setSourceClassName(cname);
setSourceMethodName(frame.getMethodName());
return;
}
}
}
}
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
private boolean isLoggerImplFrame(String cname) {
// the log record could be created for a platform logger
return cname.equals(FQCN);
}
}