blob: 27d4ba407de4b853d259307077799700b0cdc476 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.phoenix.log;
import java.util.Map;
import java.util.UUID;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.monitoring.MetricType;
import org.apache.phoenix.util.EnvironmentEdgeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import io.netty.util.internal.ThreadLocalRandom;
/*
* Wrapper for query translator
*/
public class QueryLogger {
private final ThreadLocal<RingBufferEventTranslator> threadLocalTranslator = new ThreadLocal<>();
private QueryLoggerDisruptor queryDisruptor;
private String queryId;
private LogLevel logLevel;
private Builder<QueryLogInfo, Object> queryLogBuilder = ImmutableMap.builder();
private boolean isSynced;
private static final Logger LOGGER = LoggerFactory.getLogger(QueryLogger.class);
private QueryLogger(PhoenixConnection connection) {
this.queryId = UUID.randomUUID().toString();
this.queryDisruptor = connection.getQueryServices().getQueryDisruptor();
logLevel = connection.getLogLevel();
log(QueryLogInfo.QUERY_ID_I, queryId);
log(QueryLogInfo.START_TIME_I, EnvironmentEdgeManager.currentTimeMillis());
}
private QueryLogger() {
logLevel = LogLevel.OFF;
}
private RingBufferEventTranslator getCachedTranslator() {
RingBufferEventTranslator result = threadLocalTranslator.get();
if (result == null) {
result = new RingBufferEventTranslator(queryId);
threadLocalTranslator.set(result);
}
return result;
}
public static final QueryLogger NO_OP_INSTANCE = new QueryLogger() {
@Override
public void log(QueryLogInfo queryLogInfo, Object info) {
}
@Override
public boolean isDebugEnabled() {
return false;
}
@Override
public boolean isInfoEnabled() {
return false;
}
@Override
public void sync(
Map<String, Map<MetricType, Long>> readMetrics, Map<MetricType, Long> overAllMetrics) {
}
@Override
public boolean isSynced(){
return true;
}
};
public static QueryLogger getInstance(PhoenixConnection connection, boolean isSystemTable) {
if (connection.getLogLevel() == LogLevel.OFF || isSystemTable || ThreadLocalRandom.current()
.nextDouble() > connection.getLogSamplingRate()) { return NO_OP_INSTANCE; }
return new QueryLogger(connection);
}
/**
* Add query log in the table, columns will be logged depending upon the connection logLevel
*/
public void log(QueryLogInfo queryLogInfo, Object info) {
try {
queryLogBuilder.put(queryLogInfo, info);
} catch (Exception e) {
LOGGER.warn("Unable to add log info because of " + e.getMessage());
}
}
private boolean publishLogs(RingBufferEventTranslator translator) {
if (queryDisruptor == null) { return false; }
boolean isLogged = queryDisruptor.tryPublish(translator);
if (!isLogged && LOGGER.isDebugEnabled()) {
LOGGER.debug("Unable to write query log in table as ring buffer queue is full!!");
}
return isLogged;
}
/**
* Is debug logging currently enabled?
* Call this method to prevent having to perform expensive operations (for example, String concatenation) when the log level is more than debug.
*/
public boolean isDebugEnabled(){
return isLevelEnabled(LogLevel.DEBUG);
}
private boolean isLevelEnabled(LogLevel logLevel){
return this.logLevel != null && logLevel != LogLevel.OFF ? logLevel.ordinal() <= this.logLevel.ordinal()
: false;
}
/**
* Is Info logging currently enabled?
* Call this method to prevent having to perform expensive operations (for example, String concatenation) when the log level is more than info.
* @return
*/
public boolean isInfoEnabled(){
return isLevelEnabled(LogLevel.INFO);
}
/**
* Return queryId of the current query logger , needed by the application
* to correlate with the logging table.
* Eg(usage):-
* StatementContext context = ((PhoenixResultSet)rs).getContext();
* String queryId = context.getQueryLogger().getQueryId();
*
* @return
*/
public String getQueryId() {
return this.queryId;
}
public void sync(Map<String, Map<MetricType, Long>> readMetrics, Map<MetricType, Long> overAllMetrics) {
if (!isSynced) {
isSynced = true;
final RingBufferEventTranslator translator = getCachedTranslator();
translator.setQueryInfo(logLevel, queryLogBuilder.build(), readMetrics, overAllMetrics);
publishLogs(translator);
}
}
/**
* Is Synced already
*/
public boolean isSynced(){
return this.isSynced;
}
}