Skip to content

Commit d756c77

Browse files
feat: introduce setLogger method
1 parent 67c25d3 commit d756c77

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

lib/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ const addon = bindings<{
4848
databaseClose(db: NativeDatabase): void;
4949

5050
signalTokenize(value: string): Array<string>;
51+
52+
setLogger(fn: (code: string, message: string) => void): void;
5153
}>(ROOT_DIR);
5254

5355
export type RunResult = {
@@ -587,4 +589,12 @@ export default class Database {
587589
}
588590
}
589591

590-
export { Database };
592+
function setLogger(fn: (code: string, message: string) => void): void {
593+
if (typeof fn !== 'function') {
594+
throw new TypeError('Invalid value');
595+
}
596+
597+
return addon.setLogger(fn);
598+
}
599+
600+
export { Database, setLogger };

src/addon.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,48 @@ static Napi::Value SignalTokenize(const Napi::CallbackInfo& info) {
7474
return result;
7575
}
7676

77+
// Global Settings
78+
79+
thread_local Napi::Reference<Napi::Function> logger_fn_;
80+
81+
static void LoggerWrapper(void* _ctx, int code, const char* msg) {
82+
if (logger_fn_.IsEmpty()) {
83+
return;
84+
}
85+
86+
auto env = logger_fn_.Env();
87+
Napi::HandleScope scope(env);
88+
89+
#define CODE_STR(NAME) \
90+
case NAME: \
91+
code_name = #NAME; \
92+
break;
93+
94+
const char* code_name;
95+
switch (code) {
96+
SQLITE_ERROR_ENUM(CODE_STR)
97+
default:
98+
code_name = "unknown";
99+
break;
100+
}
101+
102+
#undef CODE_STR
103+
104+
logger_fn_.Value().Call({
105+
Napi::String::New(env, code_name),
106+
Napi::String::New(env, msg),
107+
});
108+
}
109+
110+
static void SetLogger(const Napi::CallbackInfo& info) {
111+
auto callback = info[0].As<Napi::Function>();
112+
assert(callback.IsFunction());
113+
114+
logger_fn_.Reset(callback, 1);
115+
116+
sqlite3_config(SQLITE_CONFIG_LOG, LoggerWrapper);
117+
}
118+
77119
// Utils
78120

79121
Napi::Error FormatError(Napi::Env env, const char* format, ...) {
@@ -856,6 +898,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
856898

857899
Database::Init(env, exports);
858900
Statement::Init(env, exports);
901+
exports["setLogger"] = Napi::Function::New(env, &SetLogger);
859902
exports["signalTokenize"] = Napi::Function::New(env, &SignalTokenize);
860903
return exports;
861904
}

test/disk.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { tmpdir } from 'node:os';
33
import { join } from 'node:path';
44
import { expect, test, beforeEach, afterEach } from 'vitest';
55

6-
import Database from '../lib/index.js';
6+
import Database, { setLogger } from '../lib/index.js';
77

88
let dir: string;
99
let db: Database;
@@ -25,6 +25,22 @@ afterEach(async () => {
2525
}
2626
});
2727

28+
test('call logger function when configured', async () => {
29+
const messages = new Array<{ code: string; message: string }>();
30+
setLogger((code, message) => {
31+
messages.push({ code, message });
32+
});
33+
await rm(join(dir, 'db.sqlite'));
34+
db.close();
35+
36+
expect(messages).toHaveLength(1);
37+
expect(messages[0]?.code).toEqual('SQLITE_WARNING');
38+
expect(messages[0]?.message).toMatch(/file unlinked while open/);
39+
40+
// Re-open the database to let `afterEach` cleanup.
41+
db = new Database(join(dir, 'db.sqlite'));
42+
});
43+
2844
test.each([[false], [true]])('ciphertext=%j', (ciphertext) => {
2945
if (ciphertext) {
3046
db.pragma(`key = 'hello world'`);

test/memory.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, test, beforeEach, afterEach } from 'vitest';
22

3-
import Database from '../lib/index.js';
3+
import Database, { setLogger } from '../lib/index.js';
44

55
const rows = [
66
{

0 commit comments

Comments
 (0)