Skip to content

Commit

Permalink
Disable info log by default and add infoLogLevel option (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
vweevers committed May 30, 2019
1 parent 5678c16 commit 002d836
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ prebuilds/
yarn.lock
package-lock.json
.nyc_output/
.benchmarks/
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ libleveldb.a
# Benchmarks and tests
bench/
test/
.benchmarks/
*.csv

# Misc
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ If you don't want to use the prebuilt binary for the platform you are installing
Please refer to [`leveldown`](https://github.com/Level/leveldown#api) for API documentation. The `db.open(options, callback)` method of `rocksdb` has a few additional options:

- `readOnly` (boolean, default `false`): open database in read-only mode.
- `infoLogLevel` (string, default `null`): verbosity of info log. One of `'debug'`, `'info'`, `'warn'`, `'error'`, `'fatal'`, `'header'` or `null` (disable).

## Contributing

Expand Down
44 changes: 41 additions & 3 deletions binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <rocksdb/write_batch.h>
#include <rocksdb/cache.h>
#include <rocksdb/filter_policy.h>
#include <rocksdb/utilities/leveldb_options.h>
#include <rocksdb/cache.h>
#include <rocksdb/comparator.h>
#include <rocksdb/env.h>
Expand All @@ -20,6 +19,13 @@ namespace leveldb = rocksdb;
#include <map>
#include <vector>

class NullLogger : public rocksdb::Logger {
public:
using rocksdb::Logger::Logv;
virtual void Logv(const char* format, va_list ap) override {}
virtual size_t GetLogFileSize() const override { return 0; }
};

/**
* Forward declarations.
*/
Expand Down Expand Up @@ -777,6 +783,7 @@ struct OpenWorker final : public BaseWorker {
uint32_t blockRestartInterval,
uint32_t maxFileSize,
uint32_t cacheSize,
const std::string& infoLogLevel,
bool readOnly)
: BaseWorker(env, database, callback, "leveldown.db.open"),
readOnly_(readOnly),
Expand All @@ -790,7 +797,25 @@ struct OpenWorker final : public BaseWorker {
options_.max_open_files = maxOpenFiles;
options_.max_log_file_size = maxFileSize;
options_.paranoid_checks = false;
options_.info_log_level = rocksdb::InfoLogLevel::WARN_LEVEL;

if (infoLogLevel.size() > 0) {
rocksdb::InfoLogLevel lvl;

if (infoLogLevel == "debug") lvl = rocksdb::InfoLogLevel::DEBUG_LEVEL;
else if (infoLogLevel == "info") lvl = rocksdb::InfoLogLevel::INFO_LEVEL;
else if (infoLogLevel == "warn") lvl = rocksdb::InfoLogLevel::WARN_LEVEL;
else if (infoLogLevel == "error") lvl = rocksdb::InfoLogLevel::ERROR_LEVEL;
else if (infoLogLevel == "fatal") lvl = rocksdb::InfoLogLevel::FATAL_LEVEL;
else if (infoLogLevel == "header") lvl = rocksdb::InfoLogLevel::HEADER_LEVEL;
else napi_throw_error(env_, NULL, "invalid log level");

options_.info_log_level = lvl;
} else {
// In some places RocksDB checks this option to see if it should prepare
// debug information (ahead of logging), so set it to the highest level.
options_.info_log_level = rocksdb::InfoLogLevel::HEADER_LEVEL;
options_.info_log.reset(new NullLogger());
}

rocksdb::BlockBasedTableOptions tableOptions;

Expand Down Expand Up @@ -834,6 +859,8 @@ NAPI_METHOD(db_open) {
bool compression = BooleanProperty(env, options, "compression", true);
bool readOnly = BooleanProperty(env, options, "readOnly", false);

std::string infoLogLevel = StringProperty(env, options, "infoLogLevel");

uint32_t cacheSize = Uint32Property(env, options, "cacheSize", 8 << 20);
uint32_t writeBufferSize = Uint32Property(env, options , "writeBufferSize" , 4 << 20);
uint32_t blockSize = Uint32Property(env, options, "blockSize", 4096);
Expand All @@ -847,7 +874,8 @@ NAPI_METHOD(db_open) {
createIfMissing, errorIfExists,
compression, writeBufferSize, blockSize,
maxOpenFiles, blockRestartInterval,
maxFileSize, cacheSize, readOnly);
maxFileSize, cacheSize,
infoLogLevel, readOnly);
worker->Queue();
delete [] location;

Expand Down Expand Up @@ -1192,6 +1220,11 @@ struct DestroyWorker final : public BaseWorker {

void DoExecute () override {
leveldb::Options options;

// TODO: support overriding infoLogLevel the same as db.open(options)
options.info_log_level = rocksdb::InfoLogLevel::HEADER_LEVEL;
options.info_log.reset(new NullLogger());

SetStatus(leveldb::DestroyDB(location_, options));
}

Expand Down Expand Up @@ -1228,6 +1261,11 @@ struct RepairWorker final : public BaseWorker {

void DoExecute () override {
leveldb::Options options;

// TODO: support overriding infoLogLevel the same as db.open(options)
options.info_log_level = rocksdb::InfoLogLevel::HEADER_LEVEL;
options.info_log.reset(new NullLogger());

SetStatus(leveldb::RepairDB(location_, options));
}

Expand Down
24 changes: 1 addition & 23 deletions leveldown.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN
const binding = require('./binding')
const ChainedBatch = require('./chained-batch')
const Iterator = require('./iterator')
const fs = require('fs')

function LevelDOWN (location) {
if (!(this instanceof LevelDOWN)) {
Expand Down Expand Up @@ -122,28 +121,7 @@ LevelDOWN.destroy = function (location, callback) {
throw new Error('destroy() requires a callback function argument')
}

binding.destroy_db(location, function (err) {
if (err) return callback(err)

// On Windows, RocksDB silently fails to remove the directory because its
// Logger, which is instantiated on destroy(), has an open file handle on a
// LOG file. Destroy() removes this file but Windows won't actually delete
// it until the handle is released. This happens when destroy() goes out of
// scope, which disposes the Logger. So back in JS-land, we can again
// attempt to remove the directory. This is merely a workaround because
// arguably RocksDB should not instantiate a Logger or open a file at all.
fs.rmdir(location, function (err) {
if (err) {
// Ignore this error in case there are non-RocksDB files left.
if (err.code === 'ENOTEMPTY') return callback()
if (err.code === 'ENOENT') return callback()

return callback(err)
}

callback()
})
})
binding.destroy_db(location, callback)
}

LevelDOWN.repair = function (location, callback) {
Expand Down
3 changes: 2 additions & 1 deletion test/destroy-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ test('test destroy non-existent directory', function (t) {
t.ifError(err, 'no error from rimraf()')

leveldown.destroy(location, function (err) {
t.error(err, 'no error')
// This behavior differs from LevelDB, which is silent.
t.ok(/.*IO error.*/.test(err), 'got IO error')

// Assert that destroy() didn't inadvertently create the directory.
// Or if it did, that it was at least cleaned up afterwards.
Expand Down

0 comments on commit 002d836

Please sign in to comment.