use futures::Future;
use lazy_static::lazy_static;
use tokio::runtime::Runtime;
use xCommonLib::base::status::Status;
use xCommonLib::protocol::protocol_v1::ProtocolV1Reader;
use xCommonLib::serial::request_message::RequestMessage;
use protobuf::CodedInputStream;
use tracing::debug;
use xCommonLib::base::dll_api::{
AddChannelIdToRemoteServicesApi, BuildChannelApi, GenIDApi,
GetAllConnIdApi, GetAllLocalServiceApi, GetChannelIdByConnIdApi, GetHttpDataApi,
GetHttpListenAddrApi, GetXPRCPortApi, LoadServiceApi, OutputLogApi,
RemoveHttpClientApi, RemoveRemoteServiceAllChannelIdApi, ResponseApi,
SendHttpRequestApi, SendMessageApi, SetChannelXRPCPortApi, SleepApi, SubscribeApi,
UnLoadServiceApi, UnSubscribeApi, HasServiceApi,
};
use core::slice;
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::MaybeUninit;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};
pub static mut RUNTIME: MaybeUninit<Runtime> = MaybeUninit::uninit();
pub static mut SEND_MESSAGE_API: MaybeUninit<SendMessageApi> = MaybeUninit::uninit();
pub static mut HAS_SERVICE_API: MaybeUninit<HasServiceApi> = MaybeUninit::uninit();
pub(crate) static mut SLEEP_API: MaybeUninit<SleepApi> = MaybeUninit::uninit();
pub(crate) static mut GEN_ID_API: MaybeUninit<GenIDApi> = MaybeUninit::uninit();
pub(crate) static mut RESPONSE_FUNC_API: MaybeUninit<ResponseApi> = MaybeUninit::uninit();
pub(crate) static mut OUTPUT_LOG_API: MaybeUninit<OutputLogApi> = MaybeUninit::uninit();
pub(crate) static mut LOAD_SERVICE_API: MaybeUninit<LoadServiceApi> = MaybeUninit::uninit();
pub(crate) static mut UNLOAD_SERVICE_API: MaybeUninit<UnLoadServiceApi> = MaybeUninit::uninit();
pub(crate) static mut SEND_HTTP_REQUEST_API: MaybeUninit<SendHttpRequestApi> =
MaybeUninit::uninit();
pub(crate) static mut GET_HTTP_DATA_API: MaybeUninit<GetHttpDataApi> = MaybeUninit::uninit();
pub(crate) static mut REMOVE_HTTP_CLIENT_API: MaybeUninit<RemoveHttpClientApi> =
MaybeUninit::uninit();
pub(crate) static mut GET_HTTP_LISTEN_ADDR_API: MaybeUninit<GetHttpListenAddrApi> =
MaybeUninit::uninit();
pub(crate) static mut BUILD_CHANNEL_API: MaybeUninit<BuildChannelApi> = MaybeUninit::uninit();
pub(crate) static mut GET_ALL_LOCAL_SERVICE: MaybeUninit<GetAllLocalServiceApi> =
MaybeUninit::uninit();
pub(crate) static mut ADD_CHANNEL_ID_TO_REMOTE_SERVICE: MaybeUninit<
AddChannelIdToRemoteServicesApi,
> = MaybeUninit::uninit();
pub(crate) static mut REMOVE_REMOTE_SERVICE_ALL_CHANNEL_ID: MaybeUninit<
RemoveRemoteServiceAllChannelIdApi,
> = MaybeUninit::uninit();
pub(crate) static mut SET_CHANNEL_XRPC_PORT: MaybeUninit<SetChannelXRPCPortApi> =
MaybeUninit::uninit();
pub(crate) static mut GET_XRPC_PORT: MaybeUninit<GetXPRCPortApi> = MaybeUninit::uninit();
pub(crate) static mut GET_ALL_CONN_ID: MaybeUninit<GetAllConnIdApi> = MaybeUninit::uninit();
pub(crate) static mut GET_CHANNEL_ID_BY_CONN_ID: MaybeUninit<GetChannelIdByConnIdApi> =
MaybeUninit::uninit();
pub(crate) static mut SUBSCRIBE: MaybeUninit<SubscribeApi> = MaybeUninit::uninit();
pub(crate) static mut UNSUBSCRIBE: MaybeUninit<UnSubscribeApi> = MaybeUninit::uninit();
pub(crate) static mut SERVICE_ID: i64 = 0;
thread_local! {
pub static CUR_REQUEST_ID:RefCell<i64> = RefCell::new(0);
}
pub fn set_request_id(request_id: i64) {
CUR_REQUEST_ID.with(move |id| {
*id.borrow_mut() = request_id;
});
}
pub fn get_request_id() -> i64 {
CUR_REQUEST_ID.with(move |id| *id.borrow())
}
#[no_mangle]
pub extern "C" fn inject_send_message(api: SendMessageApi) {
unsafe {
SEND_MESSAGE_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_has_service(api: HasServiceApi) {
unsafe {
HAS_SERVICE_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_sleep(api: SleepApi) {
unsafe {
SLEEP_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_response_func(api: ResponseApi) {
unsafe {
RESPONSE_FUNC_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_gen_id(api: GenIDApi) {
unsafe {
GEN_ID_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_build_channel(api: BuildChannelApi) {
unsafe {
BUILD_CHANNEL_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_get_all_local_service(api: GetAllLocalServiceApi) {
unsafe {
GET_ALL_LOCAL_SERVICE.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_add_channel_id_to_remote_services(api: AddChannelIdToRemoteServicesApi) {
unsafe {
ADD_CHANNEL_ID_TO_REMOTE_SERVICE.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_remove_remote_services_all_channel_id(
api: RemoveRemoteServiceAllChannelIdApi,
) {
unsafe {
REMOVE_REMOTE_SERVICE_ALL_CHANNEL_ID.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_set_channel_xrpc_port(api: SetChannelXRPCPortApi) {
unsafe {
SET_CHANNEL_XRPC_PORT.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_get_xrpc_port(api: GetXPRCPortApi) {
unsafe {
GET_XRPC_PORT.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn result_in(request_id: i64, buffer: *const u8, buffer_len: u32) {
debug!("result_in 消息 = {}", request_id);
let handler = {
let mut handler_map = REQUEST_ID_MAP.lock().unwrap();
handler_map.remove(&request_id)
};
if handler.is_none() {
debug!("返回消息: ={},无处理....", request_id);
return;
}
let vec_buffer = unsafe { slice::from_raw_parts(buffer as *mut u8, buffer_len as usize) };
let reader = ProtocolV1Reader::new(&vec_buffer);
let msg_body = reader.msg_body();
handler.unwrap()(msg_body);
}
#[no_mangle]
pub extern "C" fn inject_log_output(api: OutputLogApi) {
unsafe {
OUTPUT_LOG_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_load_service(api: LoadServiceApi) {
unsafe {
LOAD_SERVICE_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_unload_service(api: UnLoadServiceApi) {
unsafe {
UNLOAD_SERVICE_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_send_http_request(api: SendHttpRequestApi) {
unsafe {
SEND_HTTP_REQUEST_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_get_http_listen_addr(api: GetHttpListenAddrApi) {
unsafe {
GET_HTTP_LISTEN_ADDR_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_get_http_data(api: GetHttpDataApi) {
unsafe {
GET_HTTP_DATA_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_remove_http_client(api: RemoveHttpClientApi) {
unsafe {
REMOVE_HTTP_CLIENT_API.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_get_all_conn_id(api: GetAllConnIdApi) {
unsafe {
GET_ALL_CONN_ID.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_get_channel_id_by_conn_id(api: GetChannelIdByConnIdApi) {
unsafe {
GET_CHANNEL_ID_BY_CONN_ID.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_subscribe(api: SubscribeApi) {
unsafe {
SUBSCRIBE.as_mut_ptr().write(api);
}
}
#[no_mangle]
pub extern "C" fn inject_unsubscribe(api: UnSubscribeApi) {
unsafe {
UNSUBSCRIBE.as_mut_ptr().write(api);
}
}
pub type Result<T> = std::result::Result<T, Status>;
pub mod xrpc {
use xCommonLib::service::sys_service_api::ServiceKey;
pub struct Context {
pub sender_service_key: ServiceKey,
pub channel_id: i64,
pub request_id: i64,
}
}
pub fn gen_id() -> i64 {
unsafe { GEN_ID_API.assume_init_ref()(get_service_id()) }
}
pub fn get_service_id() -> i64 {
unsafe { SERVICE_ID }
}
lazy_static! {
static ref REQUEST_ID_MAP: Mutex<HashMap<i64, Box<dyn Fn(&[u8]) + Send + Sync>>> =
Mutex::new(HashMap::new());
}
pub struct RequestState<T> {
pub is_finish: bool,
pub status: Status,
pub value: Option<T>,
pub waker: Option<Waker>,
}
pub fn create_request_state<T>() -> Arc<Mutex<RequestState<T>>> {
Arc::new(Mutex::new(RequestState {
is_finish: false,
status: Status::default(),
value: None,
waker: None,
}))
}
pub struct RequestFuture<T> {
pub shared_state: Arc<Mutex<RequestState<T>>>,
}
impl<T> Future for RequestFuture<T> {
type Output = Result<T>;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let mut shared_state = self.shared_state.lock().unwrap();
if shared_state.is_finish {
if shared_state.status.err_code != 0 {
let err_msg = std::mem::take(&mut shared_state.status.err_msg);
return Poll::Ready(Err(Status::new(1, err_msg)));
} else {
let value = shared_state.value.take();
return Poll::Ready(Ok(value.unwrap()));
}
}
shared_state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
pub struct RequestEmptyFuture {
pub shared_state: Arc<Mutex<RequestState<()>>>,
}
impl Future for RequestEmptyFuture {
type Output = Result<()>;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let mut shared_state = self.shared_state.lock().unwrap();
if shared_state.is_finish {
if shared_state.status.err_code != 0 {
let err_msg = std::mem::take(&mut shared_state.status.err_msg);
return Poll::Ready(Err(Status::new(1, err_msg)));
} else {
return Poll::Ready(Ok(()));
}
}
shared_state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
pub fn build_request_future<T>() -> (RequestFuture<T>, i64, bool) {
let request_id = gen_id();
let future = RequestFuture {
shared_state: create_request_state(),
};
return (future, request_id, true);
}
pub fn build_request_empty_future() -> (RequestEmptyFuture, i64, bool) {
let request_id = gen_id();
let future = RequestEmptyFuture {
shared_state: create_request_state(),
};
return (future, request_id, true);
}
pub fn serial_empty_request(message: &str) -> Vec<u8> {
let size = protobuf::rt::string_size(1, message);
let mut buffer: Vec<u8> = Vec::with_capacity(size as usize);
let mut os = protobuf::CodedOutputStream::vec(&mut buffer);
os.write_string(1, message).unwrap();
os.flush().unwrap();
drop(os);
buffer
}
pub fn serial_request<T>(message: &str, param: &T) -> Vec<u8>
where
T: RequestMessage,
{
let mut size = protobuf::rt::string_size(1, message);
size += param.compute_size();
let mut buffer: Vec<u8> = Vec::with_capacity(size as usize);
let mut os = protobuf::CodedOutputStream::vec(&mut buffer);
os.write_string(1, message).unwrap();
param.serial_with_output_stream(&mut os).unwrap();
os.flush().unwrap();
drop(os);
buffer
}
pub fn parse_request_param<T>(is: &mut protobuf::CodedInputStream<'_>) -> T
where
T: RequestMessage + Default,
{
let mut param = T::default();
if is.check_eof().is_ok() {
return param;
}
param.parse_from_input_stream(is).unwrap();
param
}
pub fn parse_empty_response_and_wake(shared_state: &Arc<Mutex<RequestState<()>>>, buffer: &[u8]) {
let waker = {
let mut shared_state = shared_state.lock().unwrap();
let waker = shared_state.waker.take();
if waker.is_none() {
return;
}
let mut is = CodedInputStream::from_bytes(buffer);
is.read_raw_tag_or_eof().unwrap();
let _rsp_message = is.read_string().unwrap();
shared_state
.status
.parse_from_input_stream_with_tag_and_len(&mut is);
shared_state.is_finish = true;
waker
};
waker.unwrap().wake_by_ref();
}
pub fn parse_response_and_wake<T>(shared_state: &Arc<Mutex<RequestState<T>>>, buffer: &[u8])
where
T: RequestMessage + Default,
{
let waker = {
let mut shared_state = shared_state.lock().unwrap();
let waker = shared_state.waker.take();
if waker.is_none() {
return;
}
let mut is = CodedInputStream::from_bytes(buffer);
is.read_raw_tag_or_eof().unwrap();
let _rsp_message = is.read_string().unwrap();
shared_state
.status
.parse_from_input_stream_with_tag_and_len(&mut is);
if is.pos() != buffer.len() as u64 {
let mut value = T::default();
value.parse_from_input_stream_with_tag_and_len(&mut is);
shared_state.value = Some(value);
}
shared_state.is_finish = true;
waker
};
waker.unwrap().wake_by_ref();
}
pub fn add_request_handler(request_id: i64, handler: Box<dyn Fn(&[u8]) + Send + Sync>) {
let mut handler_map = REQUEST_ID_MAP.lock().unwrap();
handler_map.insert(request_id, handler);
}
pub struct Task<F>
where
F: Future + Send + 'static,
{
request_id: i64,
future: F,
}
impl<F> Future for Task<F>
where
F: Future + Send + 'static,
{
type Output = F::Output;
fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
set_request_id(self.request_id);
let future = unsafe { self.as_mut().map_unchecked_mut(|t| &mut t.future) };
let ret = future.poll(cx);
set_request_id(0);
ret
}
}
pub fn spawn(future: impl Future<Output = ()> + 'static + Send) {
let request_id = get_request_id();
let task = Task {
request_id,
future: future,
};
let runtime = get_runtime();
runtime.spawn(task);
}
fn serial_only_status(rsp_message: &str, status: &Status) -> Vec<u8> {
let mut size = protobuf::rt::string_size(1, rsp_message);
let status_size = status.compute_size_with_tag_and_len();
size += status_size;
let mut buffer: Vec<u8> = Vec::with_capacity(size as usize);
let mut os = protobuf::CodedOutputStream::vec(&mut buffer);
os.write_string(1, rsp_message).unwrap();
status.serial_with_tag_and_len(&mut os);
drop(os);
buffer
}
pub fn response_empty_msg(rsp_message: &str, request_id: i64, result: &Result<()>) {
let buffer = match result {
Ok(_) => {
let status = xCommonLib::base::status::Status::default();
serial_only_status(rsp_message, &status)
}
Err(err) => serial_only_status(rsp_message, err),
};
unsafe {
RESPONSE_FUNC_API.assume_init_ref()(
get_service_id(),
request_id,
buffer.as_ptr(),
buffer.len() as u32,
);
}
}
pub fn response_msg<T>(rsp_message: &str, request_id: i64, result: &Result<T>)
where
T: RequestMessage,
{
let buffer = match result {
Ok(val) => {
let mut size = protobuf::rt::string_size(1, rsp_message);
let status = xCommonLib::base::status::Status::default();
size += status.compute_size_with_tag_and_len();
size += val.compute_size_with_tag_and_len();
let mut buffer: Vec<u8> = Vec::with_capacity(size as usize);
let mut os = protobuf::CodedOutputStream::vec(&mut buffer);
os.write_string(1, rsp_message).unwrap();
status.serial_with_tag_and_len(&mut os);
val.serial_with_tag_and_len(&mut os);
drop(os);
buffer
}
Err(err) => serial_only_status(rsp_message, err),
};
unsafe {
RESPONSE_FUNC_API.assume_init_ref()(
get_service_id(),
request_id,
buffer.as_ptr(),
buffer.len() as u32,
);
}
}
pub mod config {
use toml::Table;
pub(crate) static mut CONFIG_TABLE: Option<Table> = None;
pub(super) fn init(config: &str) {
let table: Table = toml::from_str(config).expect("解析配置文件失败!");
unsafe {
CONFIG_TABLE = Some(table);
}
}
pub fn get_str(key: &str) -> Option<&str> {
unsafe {
if let Some(table) = CONFIG_TABLE.as_ref() {
let value = table.get(key);
if value.is_none() {
return None;
}
return value.unwrap().as_str();
}
None
}
}
pub fn get_str_array(key: &str) -> Vec<Option<&str>> {
unsafe {
if let Some(table) = CONFIG_TABLE.as_ref() {
let value = table.get(key);
if value.is_none() {
return Vec::new();
}
let value = value.unwrap();
if value.is_array() {
let mut vec = Vec::new();
let value = value.as_array().unwrap();
for ele in value {
vec.push(ele.as_str())
}
}
return vec![value.as_str()];
}
Vec::new()
}
}
pub fn get_i32(key: &str) -> Option<i32> {
unsafe {
if let Some(table) = CONFIG_TABLE.as_ref() {
let value = table.get(key);
if value.is_none() {
return None;
}
let value = value.unwrap();
return if value.is_integer() {
Some(value.as_integer().unwrap() as i32)
} else {
let str_val = value.as_str().unwrap();
let i32_val = str_val.parse::<i32>();
match i32_val {
Ok(val) => Some(val),
Err(_) => None,
}
};
}
None
}
}
pub fn get_i64(key: &str) -> Option<i64> {
unsafe {
if let Some(table) = CONFIG_TABLE.as_ref() {
let value = table.get(key);
if value.is_none() {
return None;
}
let value = value.unwrap();
return if value.is_integer() {
Some(value.as_integer().unwrap() as i64)
} else {
let str_val = value.as_str().unwrap();
let i32_val = str_val.parse::<i64>();
match i32_val {
Ok(val) => Some(val),
Err(_) => None,
}
};
}
None
}
}
pub fn get_f32(key: &str) -> Option<f32> {
unsafe {
if let Some(table) = CONFIG_TABLE.as_ref() {
let value = table.get(key);
if value.is_none() {
return None;
}
let value = value.unwrap();
return if value.is_integer() {
Some(value.as_integer().unwrap() as f32)
} else {
let str_val = value.as_str().unwrap();
let i32_val = str_val.parse::<f32>();
match i32_val {
Ok(val) => Some(val),
Err(_) => None,
}
};
}
None
}
}
pub fn get_f64(key: &str) -> Option<f64> {
unsafe {
if let Some(table) = CONFIG_TABLE.as_ref() {
let value = table.get(key);
if value.is_none() {
return None;
}
let value = value.unwrap();
return if value.is_integer() {
Some(value.as_integer().unwrap() as f64)
} else {
let str_val = value.as_str().unwrap();
let i32_val = str_val.parse::<f64>();
match i32_val {
Ok(val) => Some(val),
Err(_) => None,
}
};
}
None
}
}
pub fn get_bool(key: &str) -> Option<bool> {
unsafe {
if let Some(table) = CONFIG_TABLE.as_ref() {
let value = table.get(key);
if value.is_none() {
return None;
}
return value.unwrap().as_bool();
}
None
}
}
}
mod logger {
use super::OUTPUT_LOG_API;
use std::fmt::Debug;
use std::io::Write;
use tracing::field::{Field, Visit};
use tracing::span::{self, Attributes, Record};
use tracing::{Event, Id, Level, Metadata, Subscriber};
use xCommonLib::utils::time_utils;
struct FieldVisitor {
buffer: Vec<u8>,
}
pub(super) struct LoggerSubscriber {
pub(super) level: Level,
pub(super) service: String,
}
fn output_log(log_buffer: &Vec<u8>) {
unsafe {
OUTPUT_LOG_API.assume_init_ref()(
super::get_service_id(),
log_buffer.as_ptr(),
log_buffer.len() as u32,
);
}
}
pub(crate) fn init(service: &str, log_level: i32) {
let level = match log_level {
0 => Level::TRACE,
1 => Level::DEBUG,
2 => Level::INFO,
3 => Level::WARN,
4 => Level::ERROR,
_ => Level::INFO,
};
let subscriber = LoggerSubscriber {
service: service.to_string(),
level: level,
};
tracing::subscriber::set_global_default(subscriber).expect("初始化日志库失败!");
}
impl Visit for FieldVisitor {
fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
if field.name() == "message" {
write!(&mut self.buffer, "{:?}", value).expect("打印日志失败!");
}
}
}
impl LoggerSubscriber {
const TRACE_STR: &str = "TRACE";
const DEBUG_STR: &str = "DEBUG";
const INFO_STR: &str = " INFO";
const WARN_STR: &str = " WARN";
const ERROR_STR: &str = "ERROR";
fn level_fmt(level: Level) -> &'static str {
match level {
Level::TRACE => Self::TRACE_STR,
Level::DEBUG => Self::DEBUG_STR,
Level::INFO => Self::INFO_STR,
Level::WARN => Self::WARN_STR,
Level::ERROR => Self::ERROR_STR,
}
}
}
impl Subscriber for LoggerSubscriber {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
*metadata.level() <= self.level
}
fn new_span(&self, _span: &Attributes<'_>) -> Id {
span::Id::from_u64(super::gen_id() as u64)
}
fn record(&self, _span: &Id, _values: &Record<'_>) {}
fn record_follows_from(&self, _span: &Id, _follows: &Id) {}
fn event(&self, event: &Event<'_>) {
let mut visitor = FieldVisitor { buffer: Vec::new() };
write!(
&mut visitor.buffer,
"{} {} {} {} {}: ",
time_utils::cur_time_str(),
Self::level_fmt(*event.metadata().level()),
self.service,
event.metadata().file().unwrap(),
event.metadata().line().unwrap()
)
.expect("打印日志失败!");
event.record(&mut visitor);
write!(&mut visitor.buffer, "\n").expect("打印日志失败!");
output_log(&visitor.buffer);
}
fn enter(&self, _span: &Id) {}
fn exit(&self, _span: &Id) {}
}
}
pub fn init_runtime() {
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4) .enable_all()
.build()
.unwrap();
unsafe {
RUNTIME.as_mut_ptr().write(runtime);
}
}
pub fn get_runtime() -> &'static Runtime {
unsafe { RUNTIME.assume_init_ref() }
}
pub fn init_app(service_id: i64, service: &str, config: &str, log_level: i32) {
unsafe {
SERVICE_ID = service_id;
}
config::init(config);
logger::init(service, log_level);
}