1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // This file implements the callback "bridge" between Java and C++ for
7 // ROCKSDB_NAMESPACE::Logger.
8
9 #include "include/org_rocksdb_Logger.h"
10
11 #include <cstdarg>
12 #include <cstdio>
13 #include "rocksjni/loggerjnicallback.h"
14 #include "rocksjni/portal.h"
15
16 namespace ROCKSDB_NAMESPACE {
17
LoggerJniCallback(JNIEnv * env,jobject jlogger)18 LoggerJniCallback::LoggerJniCallback(JNIEnv* env, jobject jlogger)
19 : JniCallback(env, jlogger) {
20 m_jLogMethodId = LoggerJni::getLogMethodId(env);
21 if (m_jLogMethodId == nullptr) {
22 // exception thrown: NoSuchMethodException or OutOfMemoryError
23 return;
24 }
25
26 jobject jdebug_level = InfoLogLevelJni::DEBUG_LEVEL(env);
27 if (jdebug_level == nullptr) {
28 // exception thrown: NoSuchFieldError, ExceptionInInitializerError
29 // or OutOfMemoryError
30 return;
31 }
32 m_jdebug_level = env->NewGlobalRef(jdebug_level);
33 if (m_jdebug_level == nullptr) {
34 // exception thrown: OutOfMemoryError
35 return;
36 }
37
38 jobject jinfo_level = InfoLogLevelJni::INFO_LEVEL(env);
39 if (jinfo_level == nullptr) {
40 // exception thrown: NoSuchFieldError, ExceptionInInitializerError
41 // or OutOfMemoryError
42 return;
43 }
44 m_jinfo_level = env->NewGlobalRef(jinfo_level);
45 if (m_jinfo_level == nullptr) {
46 // exception thrown: OutOfMemoryError
47 return;
48 }
49
50 jobject jwarn_level = InfoLogLevelJni::WARN_LEVEL(env);
51 if (jwarn_level == nullptr) {
52 // exception thrown: NoSuchFieldError, ExceptionInInitializerError
53 // or OutOfMemoryError
54 return;
55 }
56 m_jwarn_level = env->NewGlobalRef(jwarn_level);
57 if (m_jwarn_level == nullptr) {
58 // exception thrown: OutOfMemoryError
59 return;
60 }
61
62 jobject jerror_level = InfoLogLevelJni::ERROR_LEVEL(env);
63 if (jerror_level == nullptr) {
64 // exception thrown: NoSuchFieldError, ExceptionInInitializerError
65 // or OutOfMemoryError
66 return;
67 }
68 m_jerror_level = env->NewGlobalRef(jerror_level);
69 if (m_jerror_level == nullptr) {
70 // exception thrown: OutOfMemoryError
71 return;
72 }
73
74 jobject jfatal_level = InfoLogLevelJni::FATAL_LEVEL(env);
75 if (jfatal_level == nullptr) {
76 // exception thrown: NoSuchFieldError, ExceptionInInitializerError
77 // or OutOfMemoryError
78 return;
79 }
80 m_jfatal_level = env->NewGlobalRef(jfatal_level);
81 if (m_jfatal_level == nullptr) {
82 // exception thrown: OutOfMemoryError
83 return;
84 }
85
86 jobject jheader_level = InfoLogLevelJni::HEADER_LEVEL(env);
87 if (jheader_level == nullptr) {
88 // exception thrown: NoSuchFieldError, ExceptionInInitializerError
89 // or OutOfMemoryError
90 return;
91 }
92 m_jheader_level = env->NewGlobalRef(jheader_level);
93 if (m_jheader_level == nullptr) {
94 // exception thrown: OutOfMemoryError
95 return;
96 }
97 }
98
Logv(const char *,va_list)99 void LoggerJniCallback::Logv(const char* /*format*/, va_list /*ap*/) {
100 // We implement this method because it is virtual but we don't
101 // use it because we need to know about the log level.
102 }
103
Logv(const InfoLogLevel log_level,const char * format,va_list ap)104 void LoggerJniCallback::Logv(const InfoLogLevel log_level, const char* format,
105 va_list ap) {
106 if (GetInfoLogLevel() <= log_level) {
107 // determine InfoLogLevel java enum instance
108 jobject jlog_level;
109 switch (log_level) {
110 case ROCKSDB_NAMESPACE::InfoLogLevel::DEBUG_LEVEL:
111 jlog_level = m_jdebug_level;
112 break;
113 case ROCKSDB_NAMESPACE::InfoLogLevel::INFO_LEVEL:
114 jlog_level = m_jinfo_level;
115 break;
116 case ROCKSDB_NAMESPACE::InfoLogLevel::WARN_LEVEL:
117 jlog_level = m_jwarn_level;
118 break;
119 case ROCKSDB_NAMESPACE::InfoLogLevel::ERROR_LEVEL:
120 jlog_level = m_jerror_level;
121 break;
122 case ROCKSDB_NAMESPACE::InfoLogLevel::FATAL_LEVEL:
123 jlog_level = m_jfatal_level;
124 break;
125 case ROCKSDB_NAMESPACE::InfoLogLevel::HEADER_LEVEL:
126 jlog_level = m_jheader_level;
127 break;
128 default:
129 jlog_level = m_jfatal_level;
130 break;
131 }
132
133 assert(format != nullptr);
134 const std::unique_ptr<char[]> msg = format_str(format, ap);
135
136 // pass msg to java callback handler
137 jboolean attached_thread = JNI_FALSE;
138 JNIEnv* env = getJniEnv(&attached_thread);
139 assert(env != nullptr);
140
141 jstring jmsg = env->NewStringUTF(msg.get());
142 if (jmsg == nullptr) {
143 // unable to construct string
144 if (env->ExceptionCheck()) {
145 env->ExceptionDescribe(); // print out exception to stderr
146 }
147 releaseJniEnv(attached_thread);
148 return;
149 }
150 if (env->ExceptionCheck()) {
151 // exception thrown: OutOfMemoryError
152 env->ExceptionDescribe(); // print out exception to stderr
153 env->DeleteLocalRef(jmsg);
154 releaseJniEnv(attached_thread);
155 return;
156 }
157
158 env->CallVoidMethod(m_jcallback_obj, m_jLogMethodId, jlog_level, jmsg);
159 if (env->ExceptionCheck()) {
160 // exception thrown
161 env->ExceptionDescribe(); // print out exception to stderr
162 env->DeleteLocalRef(jmsg);
163 releaseJniEnv(attached_thread);
164 return;
165 }
166
167 env->DeleteLocalRef(jmsg);
168 releaseJniEnv(attached_thread);
169 }
170 }
171
format_str(const char * format,va_list ap) const172 std::unique_ptr<char[]> LoggerJniCallback::format_str(const char* format,
173 va_list ap) const {
174 va_list ap_copy;
175
176 va_copy(ap_copy, ap);
177 const size_t required =
178 vsnprintf(nullptr, 0, format, ap_copy) + 1; // Extra space for '\0'
179 va_end(ap_copy);
180
181 std::unique_ptr<char[]> buf(new char[required]);
182
183 va_copy(ap_copy, ap);
184 vsnprintf(buf.get(), required, format, ap_copy);
185 va_end(ap_copy);
186
187 return buf;
188 }
~LoggerJniCallback()189 LoggerJniCallback::~LoggerJniCallback() {
190 jboolean attached_thread = JNI_FALSE;
191 JNIEnv* env = getJniEnv(&attached_thread);
192 assert(env != nullptr);
193
194 if (m_jdebug_level != nullptr) {
195 env->DeleteGlobalRef(m_jdebug_level);
196 }
197
198 if (m_jinfo_level != nullptr) {
199 env->DeleteGlobalRef(m_jinfo_level);
200 }
201
202 if (m_jwarn_level != nullptr) {
203 env->DeleteGlobalRef(m_jwarn_level);
204 }
205
206 if (m_jerror_level != nullptr) {
207 env->DeleteGlobalRef(m_jerror_level);
208 }
209
210 if (m_jfatal_level != nullptr) {
211 env->DeleteGlobalRef(m_jfatal_level);
212 }
213
214 if (m_jheader_level != nullptr) {
215 env->DeleteGlobalRef(m_jheader_level);
216 }
217
218 releaseJniEnv(attached_thread);
219 }
220
221 } // namespace ROCKSDB_NAMESPACE
222
223 /*
224 * Class: org_rocksdb_Logger
225 * Method: createNewLoggerOptions
226 * Signature: (J)J
227 */
Java_org_rocksdb_Logger_createNewLoggerOptions(JNIEnv * env,jobject jobj,jlong joptions)228 jlong Java_org_rocksdb_Logger_createNewLoggerOptions(JNIEnv* env, jobject jobj,
229 jlong joptions) {
230 auto* sptr_logger = new std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>(
231 new ROCKSDB_NAMESPACE::LoggerJniCallback(env, jobj));
232
233 // set log level
234 auto* options = reinterpret_cast<ROCKSDB_NAMESPACE::Options*>(joptions);
235 sptr_logger->get()->SetInfoLogLevel(options->info_log_level);
236
237 return reinterpret_cast<jlong>(sptr_logger);
238 }
239
240 /*
241 * Class: org_rocksdb_Logger
242 * Method: createNewLoggerDbOptions
243 * Signature: (J)J
244 */
Java_org_rocksdb_Logger_createNewLoggerDbOptions(JNIEnv * env,jobject jobj,jlong jdb_options)245 jlong Java_org_rocksdb_Logger_createNewLoggerDbOptions(JNIEnv* env,
246 jobject jobj,
247 jlong jdb_options) {
248 auto* sptr_logger = new std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>(
249 new ROCKSDB_NAMESPACE::LoggerJniCallback(env, jobj));
250
251 // set log level
252 auto* db_options =
253 reinterpret_cast<ROCKSDB_NAMESPACE::DBOptions*>(jdb_options);
254 sptr_logger->get()->SetInfoLogLevel(db_options->info_log_level);
255
256 return reinterpret_cast<jlong>(sptr_logger);
257 }
258
259 /*
260 * Class: org_rocksdb_Logger
261 * Method: setInfoLogLevel
262 * Signature: (JB)V
263 */
Java_org_rocksdb_Logger_setInfoLogLevel(JNIEnv *,jobject,jlong jhandle,jbyte jlog_level)264 void Java_org_rocksdb_Logger_setInfoLogLevel(JNIEnv* /*env*/, jobject /*jobj*/,
265 jlong jhandle, jbyte jlog_level) {
266 auto* handle =
267 reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>*>(
268 jhandle);
269 handle->get()->SetInfoLogLevel(
270 static_cast<ROCKSDB_NAMESPACE::InfoLogLevel>(jlog_level));
271 }
272
273 /*
274 * Class: org_rocksdb_Logger
275 * Method: infoLogLevel
276 * Signature: (J)B
277 */
Java_org_rocksdb_Logger_infoLogLevel(JNIEnv *,jobject,jlong jhandle)278 jbyte Java_org_rocksdb_Logger_infoLogLevel(JNIEnv* /*env*/, jobject /*jobj*/,
279 jlong jhandle) {
280 auto* handle =
281 reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>*>(
282 jhandle);
283 return static_cast<jbyte>(handle->get()->GetInfoLogLevel());
284 }
285
286 /*
287 * Class: org_rocksdb_Logger
288 * Method: disposeInternal
289 * Signature: (J)V
290 */
Java_org_rocksdb_Logger_disposeInternal(JNIEnv *,jobject,jlong jhandle)291 void Java_org_rocksdb_Logger_disposeInternal(JNIEnv* /*env*/, jobject /*jobj*/,
292 jlong jhandle) {
293 auto* handle =
294 reinterpret_cast<std::shared_ptr<ROCKSDB_NAMESPACE::LoggerJniCallback>*>(
295 jhandle);
296 delete handle; // delete std::shared_ptr
297 }
298