mirror of https://github.com/facebook/rocksdb.git
Add native logger support to RocksJava (#12213)
Summary: ## Overview In this PR, we introduce support for setting the RocksDB native logger through Java. As mentioned in the discussion on the [Google Group discussion](https://groups.google.com/g/rocksdb/c/xYmbEs4sqRM/m/e73E4whJAQAJ), this work is primarily motivated by the JDK 17 [performance regression in JNI thread attach/detach calls](https://bugs.openjdk.org/browse/JDK-8314859): the only existing RocksJava logging configuration call, `setLogger`, invokes the provided logger over the JNI. ## Changes Specifically, these changes add support for the `devnull` and `stderr` native loggers. For the `stderr` logger, we add the ability to prefix every log with a `logPrefix`, so that it becomes possible know which database a particular log is coming from (if multiple databases are in use). The API looks like the following: ```java Options opts = new Options(); NativeLogger stderrNativeLogger = NativeLogger.newStderrLogger( InfoLogLevel.DEBUG_LEVEL, "[my prefix here]"); options.setLogger(stderrNativeLogger); try (final RocksDB db = RocksDB.open(options, ...)) {...} // Cleanup stderrNativeLogger.close() opts.close(); ``` Note that the API to set the logger is the same, via `Options::setLogger` (or `DBOptions::setLogger`). However, it will set the RocksDB logger to be native when the provided logger is an instance of `NativeLogger`. ## Testing Two tests have been added in `NativeLoggerTest.java`. The first test creates both the `devnull` and `stderr` loggers, and sets them on the associated `Options`. However, to avoid polluting the testing output with logs from `stderr`, only the `devnull` logger is actually used in the test. The second test does the same logic, but for `DBOptions`. It is possible to manually verify the `stderr` logger by modifying the tests slightly, and observing that the console indeed gets cluttered with logs from `stderr`. Pull Request resolved: https://github.com/facebook/rocksdb/pull/12213 Reviewed By: cbi42 Differential Revision: D52772306 Pulled By: ajkr fbshipit-source-id: 4026895f78f9cc250daf6bfa57427957e2d8b053
This commit is contained in:
parent
59ba1d200d
commit
4835c11cce
|
@ -74,6 +74,7 @@ set(JNI_NATIVE_SOURCES
|
|||
rocksjni/sst_partitioner.cc
|
||||
rocksjni/statistics.cc
|
||||
rocksjni/statisticsjni.cc
|
||||
rocksjni/stderr_logger.cc
|
||||
rocksjni/table.cc
|
||||
rocksjni/table_filter.cc
|
||||
rocksjni/table_filter_jnicallback.cc
|
||||
|
@ -188,6 +189,8 @@ set(JAVA_MAIN_CLASSES
|
|||
src/main/java/org/rocksdb/LiveFileMetaData.java
|
||||
src/main/java/org/rocksdb/LogFile.java
|
||||
src/main/java/org/rocksdb/Logger.java
|
||||
src/main/java/org/rocksdb/LoggerInterface.java
|
||||
src/main/java/org/rocksdb/LoggerType.java
|
||||
src/main/java/org/rocksdb/LRUCache.java
|
||||
src/main/java/org/rocksdb/MemoryUsageType.java
|
||||
src/main/java/org/rocksdb/MemoryUtil.java
|
||||
|
@ -302,6 +305,7 @@ set(JAVA_MAIN_CLASSES
|
|||
src/test/java/org/rocksdb/WriteBatchTest.java
|
||||
src/test/java/org/rocksdb/RocksNativeLibraryResource.java
|
||||
src/test/java/org/rocksdb/util/CapturingWriteBatchHandler.java
|
||||
src/main/java/org/rocksdb/util/StdErrLogger.java
|
||||
src/test/java/org/rocksdb/util/WriteBatchGetter.java
|
||||
)
|
||||
|
||||
|
@ -413,6 +417,7 @@ set(JAVA_TEST_CLASSES
|
|||
src/test/java/org/rocksdb/MemoryUtilTest.java
|
||||
src/test/java/org/rocksdb/TableFilterTest.java
|
||||
src/test/java/org/rocksdb/TtlDBTest.java
|
||||
src/test/java/org/rocksdb/util/StdErrLoggerTest.java
|
||||
)
|
||||
|
||||
set(JAVA_TEST_RUNNING_CLASSES
|
||||
|
@ -511,6 +516,7 @@ set(JAVA_TEST_RUNNING_CLASSES
|
|||
org.rocksdb.MemoryUtilTest
|
||||
org.rocksdb.TableFilterTest
|
||||
org.rocksdb.TtlDBTest
|
||||
org.rocksdb.util.StdErrLoggerTest
|
||||
)
|
||||
|
||||
include(FindJava)
|
||||
|
|
|
@ -89,7 +89,8 @@ NATIVE_JAVA_CLASSES = \
|
|||
org.rocksdb.WriteOptions\
|
||||
org.rocksdb.WriteBatchWithIndex\
|
||||
org.rocksdb.WriteBufferManager\
|
||||
org.rocksdb.WBWIRocksIterator
|
||||
org.rocksdb.WBWIRocksIterator\
|
||||
org.rocksdb.util.StdErrLogger
|
||||
|
||||
NATIVE_JAVA_TEST_CLASSES = \
|
||||
org.rocksdb.RocksDBExceptionTest\
|
||||
|
@ -206,7 +207,8 @@ JAVA_TESTS = \
|
|||
org.rocksdb.WriteBatchTest\
|
||||
org.rocksdb.WriteBatchThreadedTest\
|
||||
org.rocksdb.WriteOptionsTest\
|
||||
org.rocksdb.WriteBatchWithIndexTest
|
||||
org.rocksdb.WriteBatchWithIndexTest\
|
||||
org.rocksdb.util.StdErrLoggerTest
|
||||
|
||||
MAIN_SRC = src/main/java
|
||||
TEST_SRC = src/test/java
|
||||
|
|
|
@ -224,37 +224,15 @@ LoggerJniCallback::~LoggerJniCallback() {
|
|||
|
||||
/*
|
||||
* Class: org_rocksdb_Logger
|
||||
* Method: createNewLoggerOptions
|
||||
* Method: newLogger
|
||||
* Signature: (J)J
|
||||
*/
|
||||
jlong Java_org_rocksdb_Logger_createNewLoggerOptions(JNIEnv* env, jobject jobj,
|
||||
jlong joptions) {
|
||||
jlong Java_org_rocksdb_Logger_newLogger(JNIEnv* env, jobject jobj,
|
||||
jlong jlog_level) {
|
||||
auto* sptr_logger = new std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>(
|
||||
new ROCKSDB_NAMESPACE::LoggerJniCallback(env, jobj));
|
||||
|
||||
// set log level
|
||||
auto* options = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(joptions);
|
||||
sptr_logger->get()->SetInfoLogLevel(options->info_log_level);
|
||||
|
||||
return GET_CPLUSPLUS_POINTER(sptr_logger);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Logger
|
||||
* Method: createNewLoggerDbOptions
|
||||
* Signature: (J)J
|
||||
*/
|
||||
jlong Java_org_rocksdb_Logger_createNewLoggerDbOptions(JNIEnv* env,
|
||||
jobject jobj,
|
||||
jlong jdb_options) {
|
||||
auto* sptr_logger = new std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>(
|
||||
new ROCKSDB_NAMESPACE::LoggerJniCallback(env, jobj));
|
||||
|
||||
// set log level
|
||||
auto* db_options =
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::DBOptions*>(jdb_options);
|
||||
sptr_logger->get()->SetInfoLogLevel(db_options->info_log_level);
|
||||
|
||||
auto log_level = static_cast<ROCKSDB_NAMESPACE::InfoLogLevel>(jlog_level);
|
||||
sptr_logger->get()->SetInfoLogLevel(log_level);
|
||||
return GET_CPLUSPLUS_POINTER(sptr_logger);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "rocksjni/statisticsjni.h"
|
||||
#include "rocksjni/table_filter_jnicallback.h"
|
||||
#include "rocksjni/table_properties_collector_factory.h"
|
||||
#include "util/stderr_logger.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
/*
|
||||
|
@ -1084,14 +1085,31 @@ void Java_org_rocksdb_Options_setSstFileManager(
|
|||
/*
|
||||
* Class: org_rocksdb_Options
|
||||
* Method: setLogger
|
||||
* Signature: (JJ)V
|
||||
* Signature: (JJB)V
|
||||
*/
|
||||
void Java_org_rocksdb_Options_setLogger(JNIEnv*, jobject, jlong jhandle,
|
||||
jlong jlogger_handle) {
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>* pLogger =
|
||||
reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>*>(
|
||||
jlogger_handle);
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle)->info_log = *pLogger;
|
||||
void Java_org_rocksdb_Options_setLogger(JNIEnv* env, jobject, jlong jhandle,
|
||||
jlong jlogger_handle,
|
||||
jbyte jlogger_type) {
|
||||
auto* options = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(jhandle);
|
||||
switch (jlogger_type) {
|
||||
case 0x1:
|
||||
// JAVA_IMPLEMENTATION
|
||||
options->info_log =
|
||||
*(reinterpret_cast<
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>*>(
|
||||
jlogger_handle));
|
||||
break;
|
||||
case 0x2:
|
||||
// STDERR_IMPLEMENTATION
|
||||
options->info_log =
|
||||
*(reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::StderrLogger>*>(
|
||||
jlogger_handle));
|
||||
break;
|
||||
default:
|
||||
ROCKSDB_NAMESPACE::IllegalArgumentExceptionJni::ThrowNew(
|
||||
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
|
||||
ROCKSDB_NAMESPACE::Slice("Unknown value for LoggerType")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -6148,14 +6166,31 @@ void Java_org_rocksdb_DBOptions_setSstFileManager(
|
|||
/*
|
||||
* Class: org_rocksdb_DBOptions
|
||||
* Method: setLogger
|
||||
* Signature: (JJ)V
|
||||
* Signature: (JJB)V
|
||||
*/
|
||||
void Java_org_rocksdb_DBOptions_setLogger(JNIEnv*, jobject, jlong jhandle,
|
||||
jlong jlogger_handle) {
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>* pLogger =
|
||||
reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>*>(
|
||||
jlogger_handle);
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::DBOptions*>(jhandle)->info_log = *pLogger;
|
||||
void Java_org_rocksdb_DBOptions_setLogger(JNIEnv* env, jobject, jlong jhandle,
|
||||
jlong jlogger_handle,
|
||||
jbyte jlogger_type) {
|
||||
auto* options = reinterpret_cast<ROCKSDB_NAMESPACE::DBOptions*>(jhandle);
|
||||
switch (jlogger_type) {
|
||||
case 0x1:
|
||||
// JAVA_IMPLEMENTATION
|
||||
options->info_log =
|
||||
*(reinterpret_cast<
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>*>(
|
||||
jlogger_handle));
|
||||
break;
|
||||
case 0x2:
|
||||
// STDERR_IMPLEMENTATION
|
||||
options->info_log =
|
||||
*(reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::StderrLogger>*>(
|
||||
jlogger_handle));
|
||||
break;
|
||||
default:
|
||||
ROCKSDB_NAMESPACE::IllegalArgumentExceptionJni::ThrowNew(
|
||||
env, ROCKSDB_NAMESPACE::Status::InvalidArgument(
|
||||
ROCKSDB_NAMESPACE::Slice("Unknown value for LoggerType")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "util/stderr_logger.h"
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/org_rocksdb_util_StdErrLogger.h"
|
||||
#include "rocksjni/cplusplus_to_java_convert.h"
|
||||
#include "rocksjni/portal.h"
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_util_StdErrLogger
|
||||
* Method: newStdErrLogger
|
||||
* Signature: (BLjava/lang/String;)J
|
||||
*/
|
||||
jlong Java_org_rocksdb_util_StdErrLogger_newStdErrLogger(JNIEnv* env,
|
||||
jclass /*jcls*/,
|
||||
jbyte jlog_level,
|
||||
jstring jlog_prefix) {
|
||||
auto log_level = static_cast<ROCKSDB_NAMESPACE::InfoLogLevel>(jlog_level);
|
||||
std::shared_ptr<ROCKSDB_NAMESPACE::StderrLogger>* sptr_logger = nullptr;
|
||||
if (jlog_prefix == nullptr) {
|
||||
sptr_logger = new std::shared_ptr<ROCKSDB_NAMESPACE::StderrLogger>(
|
||||
new ROCKSDB_NAMESPACE::StderrLogger(log_level));
|
||||
} else {
|
||||
jboolean has_exception = JNI_FALSE;
|
||||
auto log_prefix = ROCKSDB_NAMESPACE::JniUtil::copyStdString(
|
||||
env, jlog_prefix, &has_exception); // also releases jlog_prefix
|
||||
if (has_exception == JNI_TRUE) {
|
||||
return 0;
|
||||
}
|
||||
sptr_logger = new std::shared_ptr<ROCKSDB_NAMESPACE::StderrLogger>(
|
||||
new ROCKSDB_NAMESPACE::StderrLogger(log_level, log_prefix));
|
||||
}
|
||||
return GET_CPLUSPLUS_POINTER(sptr_logger);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_util_StdErrLogger
|
||||
* Method: setInfoLogLevel
|
||||
* Signature: (JB)V
|
||||
*/
|
||||
void Java_org_rocksdb_util_StdErrLogger_setInfoLogLevel(JNIEnv* /*env*/,
|
||||
jclass /*jcls*/,
|
||||
jlong jhandle,
|
||||
jbyte jlog_level) {
|
||||
auto* handle =
|
||||
reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::StderrLogger>*>(
|
||||
jhandle);
|
||||
handle->get()->SetInfoLogLevel(
|
||||
static_cast<ROCKSDB_NAMESPACE::InfoLogLevel>(jlog_level));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_util_StdErrLogger
|
||||
* Method: infoLogLevel
|
||||
* Signature: (J)B
|
||||
*/
|
||||
jbyte Java_org_rocksdb_util_StdErrLogger_infoLogLevel(JNIEnv* /*env*/,
|
||||
jclass /*jcls*/,
|
||||
jlong jhandle) {
|
||||
auto* handle =
|
||||
reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::StderrLogger>*>(
|
||||
jhandle);
|
||||
return static_cast<jbyte>(handle->get()->GetInfoLogLevel());
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_util_StdErrLogger
|
||||
* Method: disposeInternal
|
||||
* Signature: (J)V
|
||||
*/
|
||||
void Java_org_rocksdb_util_StdErrLogger_disposeInternal(JNIEnv* /*env*/,
|
||||
jobject /*jobj*/,
|
||||
jlong jhandle) {
|
||||
auto* handle =
|
||||
reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::StderrLogger>*>(
|
||||
jhandle);
|
||||
delete handle; // delete std::shared_ptr
|
||||
}
|
|
@ -213,9 +213,9 @@ public class DBOptions extends RocksObject
|
|||
}
|
||||
|
||||
@Override
|
||||
public DBOptions setLogger(final Logger logger) {
|
||||
public DBOptions setLogger(final LoggerInterface logger) {
|
||||
assert(isOwningHandle());
|
||||
setLogger(nativeHandle_, logger.nativeHandle_);
|
||||
setLogger(nativeHandle_, logger.getNativeHandle(), logger.getLoggerType().getValue());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -1275,8 +1275,7 @@ public class DBOptions extends RocksObject
|
|||
long rateLimiterHandle);
|
||||
private native void setSstFileManager(final long handle,
|
||||
final long sstFileManagerHandle);
|
||||
private native void setLogger(long handle,
|
||||
long loggerHandle);
|
||||
private native void setLogger(final long handle, final long loggerHandle, final byte loggerType);
|
||||
private native void setInfoLogLevel(long handle, byte logLevel);
|
||||
private native byte infoLogLevel(long handle);
|
||||
private native void setMaxOpenFiles(long handle, int maxOpenFiles);
|
||||
|
|
|
@ -185,10 +185,10 @@ public interface DBOptionsInterface<T extends DBOptionsInterface<T>> {
|
|||
*
|
||||
* <p>Default: nullptr</p>
|
||||
*
|
||||
* @param logger {@link Logger} instance.
|
||||
* @param logger {@link LoggerInterface} instance.
|
||||
* @return the instance of the current object.
|
||||
*/
|
||||
T setLogger(Logger logger);
|
||||
T setLogger(LoggerInterface logger);
|
||||
|
||||
/**
|
||||
* <p>Sets the RocksDB log level. Default level is INFO</p>
|
||||
|
|
|
@ -35,10 +35,7 @@ package org.rocksdb;
|
|||
* {@link org.rocksdb.InfoLogLevel#FATAL_LEVEL}.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class Logger extends RocksCallbackObject {
|
||||
private static final long WITH_OPTIONS = 0;
|
||||
private static final long WITH_DBOPTIONS = 1;
|
||||
|
||||
public abstract class Logger extends RocksCallbackObject implements LoggerInterface {
|
||||
/**
|
||||
* <p>AbstractLogger constructor.</p>
|
||||
*
|
||||
|
@ -47,10 +44,13 @@ public abstract class Logger extends RocksCallbackObject {
|
|||
* maximum log level of RocksDB.</p>
|
||||
*
|
||||
* @param options {@link org.rocksdb.Options} instance.
|
||||
*
|
||||
* @deprecated Use {@link Logger#Logger(InfoLogLevel)} instead, e.g. {@code new
|
||||
* Logger(options.infoLogLevel())}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Logger(final Options options) {
|
||||
super(options.nativeHandle_, WITH_OPTIONS);
|
||||
|
||||
this(options.infoLogLevel());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,56 +61,64 @@ public abstract class Logger extends RocksCallbackObject {
|
|||
* as maximum log level of RocksDB.</p>
|
||||
*
|
||||
* @param dboptions {@link org.rocksdb.DBOptions} instance.
|
||||
*
|
||||
* @deprecated Use {@link Logger#Logger(InfoLogLevel)} instead, e.g. {@code new
|
||||
* Logger(dbOptions.infoLogLevel())}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Logger(final DBOptions dboptions) {
|
||||
super(dboptions.nativeHandle_, WITH_DBOPTIONS);
|
||||
this(dboptions.infoLogLevel());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>AbstractLogger constructor.</p>
|
||||
*
|
||||
* @param logLevel the log level.
|
||||
*/
|
||||
public Logger(final InfoLogLevel logLevel) {
|
||||
super(logLevel.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long initializeNative(final long... nativeParameterHandles) {
|
||||
if(nativeParameterHandles[1] == WITH_OPTIONS) {
|
||||
return createNewLoggerOptions(nativeParameterHandles[0]);
|
||||
} else if(nativeParameterHandles[1] == WITH_DBOPTIONS) {
|
||||
return createNewLoggerDbOptions(nativeParameterHandles[0]);
|
||||
if (nativeParameterHandles.length == 1) {
|
||||
return newLogger(nativeParameterHandles[0]);
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link org.rocksdb.InfoLogLevel} to AbstractLogger.
|
||||
*
|
||||
* @param infoLogLevel {@link org.rocksdb.InfoLogLevel} instance.
|
||||
*/
|
||||
public void setInfoLogLevel(final InfoLogLevel infoLogLevel) {
|
||||
setInfoLogLevel(nativeHandle_, infoLogLevel.getValue());
|
||||
@Override
|
||||
public void setInfoLogLevel(final InfoLogLevel logLevel) {
|
||||
setInfoLogLevel(nativeHandle_, logLevel.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the loggers log level.
|
||||
*
|
||||
* @return {@link org.rocksdb.InfoLogLevel} instance.
|
||||
*/
|
||||
@Override
|
||||
public InfoLogLevel infoLogLevel() {
|
||||
return InfoLogLevel.getInfoLogLevel(
|
||||
infoLogLevel(nativeHandle_));
|
||||
}
|
||||
|
||||
protected abstract void log(InfoLogLevel infoLogLevel,
|
||||
String logMsg);
|
||||
@Override
|
||||
public long getNativeHandle() {
|
||||
return nativeHandle_;
|
||||
}
|
||||
|
||||
protected native long createNewLoggerOptions(
|
||||
long options);
|
||||
protected native long createNewLoggerDbOptions(
|
||||
long dbOptions);
|
||||
protected native void setInfoLogLevel(long handle,
|
||||
byte infoLogLevel);
|
||||
protected native byte infoLogLevel(long handle);
|
||||
@Override
|
||||
public final LoggerType getLoggerType() {
|
||||
return LoggerType.JAVA_IMPLEMENTATION;
|
||||
}
|
||||
|
||||
protected abstract void log(final InfoLogLevel logLevel, final String logMsg);
|
||||
|
||||
protected native long newLogger(final long logLevel);
|
||||
protected native void setInfoLogLevel(final long handle, final byte logLevel);
|
||||
protected native byte infoLogLevel(final long handle);
|
||||
|
||||
/**
|
||||
* We override {@link RocksCallbackObject#disposeInternal()}
|
||||
* as disposing of a rocksdb::LoggerJniCallback requires
|
||||
* a slightly different approach as it is a std::shared_ptr
|
||||
* a slightly different approach as it is a std::shared_ptr.
|
||||
*/
|
||||
@Override
|
||||
protected void disposeInternal() {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2016, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* LoggerInterface is a thin interface that specifies the most basic
|
||||
* functionality for a Java wrapper around a RocksDB Logger.
|
||||
*/
|
||||
public interface LoggerInterface {
|
||||
/**
|
||||
* Set the log level.
|
||||
*
|
||||
* @param logLevel the level at which to log.
|
||||
*/
|
||||
void setInfoLogLevel(final InfoLogLevel logLevel);
|
||||
|
||||
/**
|
||||
* Get the log level
|
||||
*
|
||||
* @return the level at which to log.
|
||||
*/
|
||||
InfoLogLevel infoLogLevel();
|
||||
|
||||
/**
|
||||
* Get the underlying Native Handle.
|
||||
*
|
||||
* @return the native handle.
|
||||
*/
|
||||
long getNativeHandle();
|
||||
|
||||
/**
|
||||
* Get the type of this logger.
|
||||
*
|
||||
* @return the type of this logger.
|
||||
*/
|
||||
LoggerType getLoggerType();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) 2016, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
package org.rocksdb;
|
||||
|
||||
/**
|
||||
* Simple enumeration used for differentiating
|
||||
* the types of loggers when passing via the JNI
|
||||
* boundary.
|
||||
*/
|
||||
public enum LoggerType {
|
||||
JAVA_IMPLEMENTATION((byte) 0x1),
|
||||
STDERR_IMPLEMENTATION((byte) 0x2);
|
||||
|
||||
private final byte value;
|
||||
|
||||
LoggerType(final byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte value of the enumerations value
|
||||
*
|
||||
* @return byte representation
|
||||
*/
|
||||
byte getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LoggerType by byte value.
|
||||
*
|
||||
* @param value byte representation of LoggerType.
|
||||
*
|
||||
* @return {@link org.rocksdb.LoggerType} instance.
|
||||
* @throws java.lang.IllegalArgumentException if an invalid
|
||||
* value is provided.
|
||||
*/
|
||||
static LoggerType getLoggerType(final byte value) {
|
||||
for (final LoggerType loggerType : LoggerType.values()) {
|
||||
if (loggerType.getValue() == value) {
|
||||
return loggerType;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Illegal value provided for LoggerType.");
|
||||
}
|
||||
}
|
|
@ -1246,9 +1246,9 @@ public class Options extends RocksObject
|
|||
}
|
||||
|
||||
@Override
|
||||
public Options setLogger(final Logger logger) {
|
||||
public Options setLogger(final LoggerInterface logger) {
|
||||
assert(isOwningHandle());
|
||||
setLogger(nativeHandle_, logger.nativeHandle_);
|
||||
setLogger(nativeHandle_, logger.getNativeHandle(), logger.getLoggerType().getValue());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -2180,8 +2180,7 @@ public class Options extends RocksObject
|
|||
long rateLimiterHandle);
|
||||
private native void setSstFileManager(final long handle,
|
||||
final long sstFileManagerHandle);
|
||||
private native void setLogger(long handle,
|
||||
long loggerHandle);
|
||||
private native void setLogger(final long handle, final long loggerHandle, final byte loggerType);
|
||||
private native void setInfoLogLevel(long handle, byte logLevel);
|
||||
private native byte infoLogLevel(long handle);
|
||||
private native void setMaxOpenFiles(long handle, int maxOpenFiles);
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
package org.rocksdb.util;
|
||||
|
||||
import org.rocksdb.InfoLogLevel;
|
||||
import org.rocksdb.LoggerInterface;
|
||||
import org.rocksdb.LoggerType;
|
||||
import org.rocksdb.RocksObject;
|
||||
|
||||
/**
|
||||
* Simply redirects all log messages to StdErr.
|
||||
*/
|
||||
public class StdErrLogger extends RocksObject implements LoggerInterface {
|
||||
/**
|
||||
* Constructs a new StdErrLogger.
|
||||
*
|
||||
* @param logLevel the level at which to log.
|
||||
*/
|
||||
public StdErrLogger(final InfoLogLevel logLevel) {
|
||||
this(logLevel, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new StdErrLogger.
|
||||
*
|
||||
* @param logLevel the level at which to log.
|
||||
* @param logPrefix the string with which to prefix all log messages.
|
||||
*/
|
||||
public StdErrLogger(final InfoLogLevel logLevel, /* @Nullable */ final String logPrefix) {
|
||||
super(newStdErrLogger(logLevel.getValue(), logPrefix));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInfoLogLevel(final InfoLogLevel logLevel) {
|
||||
setInfoLogLevel(nativeHandle_, logLevel.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoLogLevel infoLogLevel() {
|
||||
return InfoLogLevel.getInfoLogLevel(infoLogLevel(nativeHandle_));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggerType getLoggerType() {
|
||||
return LoggerType.STDERR_IMPLEMENTATION;
|
||||
}
|
||||
|
||||
private static native long newStdErrLogger(
|
||||
final byte logLevel, /* @Nullable */ final String logPrefix);
|
||||
private static native void setInfoLogLevel(final long handle, final byte logLevel);
|
||||
private static native byte infoLogLevel(final long handle);
|
||||
|
||||
@Override protected native void disposeInternal(final long handle);
|
||||
}
|
|
@ -232,4 +232,39 @@ public class LoggerTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logLevelLogger() throws RocksDBException {
|
||||
final AtomicInteger logMessageCounter = new AtomicInteger();
|
||||
try (final DBOptions options = new DBOptions().setCreateIfMissing(true);
|
||||
final Logger logger = new Logger(InfoLogLevel.FATAL_LEVEL) {
|
||||
// Create new logger with max log level passed by options
|
||||
@Override
|
||||
protected void log(final InfoLogLevel infoLogLevel, final String logMsg) {
|
||||
assertThat(logMsg).isNotNull();
|
||||
assertThat(logMsg.length()).isGreaterThan(0);
|
||||
logMessageCounter.incrementAndGet();
|
||||
}
|
||||
}) {
|
||||
// Set custom logger to options
|
||||
options.setLogger(logger);
|
||||
|
||||
final List<ColumnFamilyDescriptor> cfDescriptors =
|
||||
Collections.singletonList(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY));
|
||||
final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
|
||||
|
||||
try (final RocksDB db = RocksDB.open(
|
||||
options, dbFolder.getRoot().getAbsolutePath(), cfDescriptors, cfHandles)) {
|
||||
try {
|
||||
// there should be zero messages
|
||||
// using fatal level as log level.
|
||||
assertThat(logMessageCounter.get()).isEqualTo(0);
|
||||
} finally {
|
||||
for (final ColumnFamilyHandle columnFamilyHandle : cfHandles) {
|
||||
columnFamilyHandle.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
package org.rocksdb.util;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.rocksdb.DBOptions;
|
||||
import org.rocksdb.InfoLogLevel;
|
||||
import org.rocksdb.Options;
|
||||
import org.rocksdb.RocksNativeLibraryResource;
|
||||
|
||||
public class StdErrLoggerTest {
|
||||
@ClassRule
|
||||
public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE =
|
||||
new RocksNativeLibraryResource();
|
||||
|
||||
// Logging with the stderr logger would pollute the console when tests were run (and
|
||||
// from Java, we can't redirect or close stderr).
|
||||
// So we just test creation of a StdErrLogger and setting it on Options
|
||||
// without opening the DB.
|
||||
@Test
|
||||
public void nativeLoggersWithOptions() {
|
||||
try (final Options options = new Options().setCreateIfMissing(true);
|
||||
final StdErrLogger stdErrLogger =
|
||||
new StdErrLogger(InfoLogLevel.DEBUG_LEVEL, "[Options prefix]")) {
|
||||
options.setLogger(stdErrLogger);
|
||||
}
|
||||
}
|
||||
|
||||
// Logging with the stderr logger would pollute the console when tests were run (and
|
||||
// from Java, we can't redirect or close stderr).
|
||||
// So we just test creation of a StdErrLogger and setting it on DBOptions
|
||||
// without opening the DB.
|
||||
@Test
|
||||
public void nativeLoggersWithDBOptions() {
|
||||
try (final DBOptions options = new DBOptions().setCreateIfMissing(true);
|
||||
final StdErrLogger stdErrLogger =
|
||||
new StdErrLogger(InfoLogLevel.DEBUG_LEVEL, "[DBOptions prefix]")) {
|
||||
options.setLogger(stdErrLogger);
|
||||
}
|
||||
}
|
||||
}
|
1
src.mk
1
src.mk
|
@ -702,6 +702,7 @@ JNI_NATIVE_SOURCES = \
|
|||
java/rocksjni/sst_partitioner.cc \
|
||||
java/rocksjni/statistics.cc \
|
||||
java/rocksjni/statisticsjni.cc \
|
||||
java/rocksjni/stderr_logger.cc \
|
||||
java/rocksjni/table.cc \
|
||||
java/rocksjni/table_filter.cc \
|
||||
java/rocksjni/table_filter_jnicallback.cc \
|
||||
|
|
|
@ -6,10 +6,15 @@
|
|||
|
||||
#include "util/stderr_logger.h"
|
||||
|
||||
#include "port/malloc.h"
|
||||
#include "port/sys_time.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
StderrLogger::~StderrLogger() {}
|
||||
StderrLogger::~StderrLogger() {
|
||||
if (log_prefix != nullptr) {
|
||||
free((void*)log_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
void StderrLogger::Logv(const char* format, va_list ap) {
|
||||
const uint64_t thread_id = Env::Default()->GetThreadID();
|
||||
|
@ -19,12 +24,40 @@ void StderrLogger::Logv(const char* format, va_list ap) {
|
|||
const time_t seconds = now_tv.tv_sec;
|
||||
struct tm t;
|
||||
port::LocalTimeR(&seconds, &t);
|
||||
fprintf(stderr, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", t.tm_year + 1900,
|
||||
t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
|
||||
static_cast<int>(now_tv.tv_usec),
|
||||
static_cast<long long unsigned int>(thread_id));
|
||||
|
||||
vfprintf(stderr, format, ap);
|
||||
fprintf(stderr, "\n");
|
||||
// The string we eventually log has three parts: the context (time, thread),
|
||||
// optional user-supplied prefix, and the actual log message (the "suffix").
|
||||
//
|
||||
// We compute their lengths so that we can allocate a buffer big enough to
|
||||
// print it. The context string (with the date and thread id) is really only
|
||||
// 44 bytes, but we allocate 50 to be safe.
|
||||
//
|
||||
// ctx_len = 44 = ( 4+ 1+ 2+1+2+ 1+2+ 1+2+ 1+ 2+1+6+ 1+16+1)
|
||||
const char* ctx_prefix_fmt = "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx %s";
|
||||
size_t ctx_len = 50;
|
||||
|
||||
va_list ap_copy;
|
||||
va_copy(ap_copy, ap);
|
||||
const size_t log_suffix_len = vsnprintf(nullptr, 0, format, ap_copy);
|
||||
va_end(ap_copy);
|
||||
|
||||
// Allocate space for the context, log_prefix, and log itself
|
||||
// Extra byte for null termination
|
||||
size_t buf_len = ctx_len + log_prefix_len + log_suffix_len + 1;
|
||||
std::unique_ptr<char[]> buf(new char[buf_len]);
|
||||
|
||||
// If the logger was created without a prefix, the prefix is a nullptr
|
||||
const char* prefix = log_prefix == nullptr ? "" : log_prefix;
|
||||
|
||||
// Write out the context and prefix string
|
||||
int written =
|
||||
snprintf(buf.get(), ctx_len + log_prefix_len, ctx_prefix_fmt,
|
||||
t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min,
|
||||
t.tm_sec, static_cast<int>(now_tv.tv_usec),
|
||||
static_cast<long long unsigned int>(thread_id), prefix);
|
||||
written += vsnprintf(buf.get() + written, log_suffix_len, format, ap);
|
||||
buf[written] = '\0';
|
||||
|
||||
fprintf(stderr, "%s%c", buf.get(), '\n');
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
|
|
@ -17,7 +17,11 @@ namespace ROCKSDB_NAMESPACE {
|
|||
class StderrLogger : public Logger {
|
||||
public:
|
||||
explicit StderrLogger(const InfoLogLevel log_level = InfoLogLevel::INFO_LEVEL)
|
||||
: Logger(log_level) {}
|
||||
: Logger(log_level), log_prefix(nullptr) {}
|
||||
explicit StderrLogger(const InfoLogLevel log_level, const std::string prefix)
|
||||
: Logger(log_level),
|
||||
log_prefix(strdup(prefix.c_str())),
|
||||
log_prefix_len(strlen(log_prefix)) {}
|
||||
|
||||
~StderrLogger() override;
|
||||
|
||||
|
@ -26,6 +30,12 @@ class StderrLogger : public Logger {
|
|||
using Logger::Logv;
|
||||
|
||||
virtual void Logv(const char* format, va_list ap) override;
|
||||
|
||||
private:
|
||||
// This prefix will be appended after the time/thread info of every log
|
||||
const char* log_prefix;
|
||||
// The length of the log_prefix
|
||||
size_t log_prefix_len;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
|
Loading…
Reference in New Issue