diff --git a/src/api/calculate_bucket_id.rs b/src/api/calculate_bucket_id.rs
index ebbe9e54f33dcd692ea327b790b4544e879fdc3d..9c077c3388c70d3134c12537957d16c203a2df6f 100644
--- a/src/api/calculate_bucket_id.rs
+++ b/src/api/calculate_bucket_id.rs
@@ -2,7 +2,6 @@ use std::collections::HashMap;
 use std::os::raw::c_int;
 
 use crate::errors::QueryPlannerError;
-use tarantool::error::TarantoolErrorCode;
 use tarantool::tuple::{FunctionArgs, FunctionCtx, Tuple};
 
 use serde::{de::Deserializer, Deserialize, Serialize};
@@ -11,6 +10,7 @@ use crate::api::helper::load_config;
 use crate::api::COORDINATOR_ENGINE;
 use crate::executor::engine::Coordinator;
 use crate::ir::value::Value;
+use crate::log::tarantool_error;
 
 #[derive(Debug, Default, Serialize, PartialEq, Eq)]
 /// Tuple with space name and `key:value` map of values
@@ -114,11 +114,10 @@ pub extern "C" fn calculate_bucket_id(ctx: FunctionCtx, args: FunctionArgs) -> c
         let runtime = match engine.try_borrow() {
             Ok(runtime) => runtime,
             Err(e) => {
-                return tarantool::set_error!(
-                    TarantoolErrorCode::ProcC,
+                return tarantool_error(&format!(
                     "Failed to borrow the runtime while calculating a bucket id: {:?}",
-                    e
-                )
+                    e,
+                ));
             }
         };
 
@@ -155,7 +154,7 @@ pub extern "C" fn calculate_bucket_id(ctx: FunctionCtx, args: FunctionArgs) -> c
                 ctx.return_mp(&bucket_id).unwrap();
                 0
             }
-            Err(e) => tarantool::set_error!(TarantoolErrorCode::ProcC, "{:?}", e),
+            Err(e) => tarantool_error(&format!("{:?}", e)),
         }
     })
 }
diff --git a/src/api/exec_query.rs b/src/api/exec_query.rs
index 2a41d7b7326b9cb9f5f13502ccc8951e1624e971..d0cfbb83bd1c6d94b9b86ffb256e6f778c3b10f0 100644
--- a/src/api/exec_query.rs
+++ b/src/api/exec_query.rs
@@ -1,8 +1,6 @@
 use serde::{Deserialize, Deserializer};
 use std::collections::HashMap;
 use std::os::raw::c_int;
-use tarantool::error::TarantoolErrorCode;
-use tarantool::log::{say, SayLevel};
 use tarantool::tuple::{FunctionArgs, FunctionCtx, Tuple};
 
 use crate::api::helper::load_config;
@@ -11,14 +9,16 @@ use crate::errors::QueryPlannerError;
 use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::executor::Query;
 use crate::ir::value::Value;
+use crate::log::tarantool_error;
 use crate::otm::{child_span, extract_params, query_span};
+use crate::{debug, error};
 
 /// Dispatch parameterized SQL query from coordinator to the segments.
 #[no_mangle]
 pub extern "C" fn dispatch_query(f_ctx: FunctionCtx, args: FunctionArgs) -> c_int {
     let lua_params = match PatternWithParams::try_from(args) {
         Ok(params) => params,
-        Err(e) => return tarantool::set_error!(TarantoolErrorCode::ProcC, "{:?}", e),
+        Err(e) => return tarantool_error(&e.to_string()),
     };
 
     // We initialize the global tracer on every configuration update.
@@ -35,28 +35,17 @@ pub extern "C" fn dispatch_query(f_ctx: FunctionCtx, args: FunctionArgs) -> c_in
             let runtime = match engine.try_borrow() {
                 Ok(runtime) => runtime,
                 Err(e) => {
-                    return tarantool::set_error!(
-                        TarantoolErrorCode::ProcC,
+                    return tarantool_error(&format!(
                         "Failed to borrow the runtime while dispatching the query: {}",
-                        e.to_string()
-                    );
+                        e
+                    ));
                 }
             };
             let mut query = match Query::new(&*runtime, &lua_params.pattern, lua_params.params) {
                 Ok(q) => q,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
-                        None,
-                        &format!("{:?}", e),
-                    );
-                    return tarantool::set_error!(
-                        TarantoolErrorCode::ProcC,
-                        "{}",
-                        format!("{:?}", e)
-                    );
+                    error!(Option::from("query dispatch"), &format!("{:?}", e));
+                    return tarantool_error(&e.to_string());
                 }
             };
 
@@ -66,14 +55,10 @@ pub extern "C" fn dispatch_query(f_ctx: FunctionCtx, args: FunctionArgs) -> c_in
                         f_ctx.return_tuple(tuple).unwrap();
                         0
                     } else {
-                        tarantool::set_error!(
-                            TarantoolErrorCode::ProcC,
-                            "{}",
-                            "Unsupported result type"
-                        )
+                        tarantool_error("Unsupported result type")
                     }
                 }),
-                Err(e) => tarantool::set_error!(TarantoolErrorCode::ProcC, "{}", e.to_string()),
+                Err(e) => tarantool_error(&e.to_string()),
             }
         })
     })
@@ -92,11 +77,8 @@ impl TryFrom<FunctionArgs> for ExecuteQueryParams {
     type Error = QueryPlannerError;
 
     fn try_from(value: FunctionArgs) -> Result<Self, Self::Error> {
-        say(
-            SayLevel::Debug,
-            file!(),
-            line!().try_into().unwrap_or(0),
-            None,
+        debug!(
+            Option::from("argument parsing"),
             &format!("Execute query parameters: {:?}", value),
         );
         Tuple::from(value)
@@ -138,7 +120,7 @@ impl<'de> Deserialize<'de> for ExecuteQueryParams {
 pub extern "C" fn execute_query(f_ctx: FunctionCtx, args: FunctionArgs) -> c_int {
     let lua_params = match ExecuteQueryParams::try_from(args) {
         Ok(param) => param,
-        Err(e) => return tarantool::set_error!(TarantoolErrorCode::ProcC, "{:?}", e),
+        Err(e) => return tarantool_error(&e.to_string()),
     };
 
     let ret_code = load_config(&SEGMENT_ENGINE);
@@ -158,11 +140,10 @@ pub extern "C" fn execute_query(f_ctx: FunctionCtx, args: FunctionArgs) -> c_int
                 let runtime = match engine.try_borrow() {
                     Ok(runtime) => runtime,
                     Err(e) => {
-                        return tarantool::set_error!(
-                            TarantoolErrorCode::ProcC,
+                        return tarantool_error(&format!(
                             "Failed to borrow the runtime while executing the query: {}",
-                            e.to_string()
-                        );
+                            e
+                        ));
                     }
                 };
                 match runtime.execute(
@@ -175,16 +156,10 @@ pub extern "C" fn execute_query(f_ctx: FunctionCtx, args: FunctionArgs) -> c_int
                             f_ctx.return_tuple(tuple).unwrap();
                             0
                         } else {
-                            tarantool::set_error!(
-                                TarantoolErrorCode::ProcC,
-                                "{}",
-                                "Unsupported result type"
-                            )
+                            tarantool_error("Unsupported result type")
                         }
                     }
-                    Err(e) => {
-                        tarantool::set_error!(TarantoolErrorCode::ProcC, "{}", format!("{:?}", e))
-                    }
+                    Err(e) => tarantool_error(&e.to_string()),
                 }
             })
         },
diff --git a/src/api/explain.rs b/src/api/explain.rs
index 32dd3e395ff69457972b743477205fc90a1dbc64..67b9966e0b8a41342efb13a59f28c23187aed799 100644
--- a/src/api/explain.rs
+++ b/src/api/explain.rs
@@ -1,14 +1,14 @@
 use std::os::raw::c_int;
 
 use serde::{Deserialize, Serialize};
-use tarantool::error::TarantoolErrorCode;
-use tarantool::log::{say, SayLevel};
 use tarantool::tuple::{FunctionArgs, FunctionCtx, Tuple};
 
 use crate::api::helper::load_config;
 use crate::api::COORDINATOR_ENGINE;
+use crate::error;
 use crate::errors::QueryPlannerError;
 use crate::executor::Query;
+use crate::log::tarantool_error;
 
 #[derive(Serialize, Deserialize)]
 /// Lua function params
@@ -32,7 +32,7 @@ impl TryFrom<FunctionArgs> for Args {
 pub extern "C" fn explain(ctx: FunctionCtx, args: FunctionArgs) -> c_int {
     let lua_params = match Args::try_from(args) {
         Ok(param) => param,
-        Err(e) => return tarantool::set_error!(TarantoolErrorCode::ProcC, "{:?}", e),
+        Err(e) => return tarantool_error(&e.to_string()),
     };
 
     let ret_code = load_config(&COORDINATOR_ENGINE);
@@ -43,24 +43,17 @@ pub extern "C" fn explain(ctx: FunctionCtx, args: FunctionArgs) -> c_int {
         let runtime = match engine.try_borrow() {
             Ok(runtime) => runtime,
             Err(e) => {
-                return tarantool::set_error!(
-                    TarantoolErrorCode::ProcC,
+                return tarantool_error(&format!(
                     "Failed to borrow runtime while explaining the query: {}",
-                    e.to_string()
-                );
+                    e
+                ));
             }
         };
         let query = match Query::new(&*runtime, &lua_params.query, vec![]) {
             Ok(q) => q,
             Err(e) => {
-                say(
-                    SayLevel::Error,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
-                    None,
-                    &format!("{:?}", e),
-                );
-                return tarantool::set_error!(TarantoolErrorCode::ProcC, "{}", format!("{:?}", e));
+                error!(Option::from("explain"), &format!("{:?}", e));
+                return tarantool_error(&e.to_string());
             }
         };
 
@@ -69,7 +62,7 @@ pub extern "C" fn explain(ctx: FunctionCtx, args: FunctionArgs) -> c_int {
                 ctx.return_mp(&q).unwrap();
                 0
             }
-            Err(e) => tarantool::set_error!(TarantoolErrorCode::ProcC, "{}", e.to_string()),
+            Err(e) => tarantool_error(&e.to_string()),
         }
     })
 }
diff --git a/src/api/helper.rs b/src/api/helper.rs
index ba6276fadf6974b0f60aa8766146af679192212d..b7476e009d06480e0176b772fd53b9339dfeb2c8 100644
--- a/src/api/helper.rs
+++ b/src/api/helper.rs
@@ -1,8 +1,8 @@
 use crate::executor::engine::Configuration;
+use crate::log::tarantool_error;
 use std::cell::RefCell;
 use std::os::raw::c_int;
 use std::thread::LocalKey;
-use tarantool::error::TarantoolErrorCode;
 
 pub fn load_config<Runtime>(engine: &'static LocalKey<RefCell<Runtime>>) -> c_int
 where
@@ -15,11 +15,10 @@ where
         let runtime = match engine.try_borrow() {
             Ok(runtime) => runtime,
             Err(e) => {
-                return tarantool::set_error!(
-                    TarantoolErrorCode::ProcC,
+                return tarantool_error(&format!(
                     "Failed to borrow the runtime while loading configuration: {}",
-                    e.to_string()
-                );
+                    e
+                ));
             }
         };
         match runtime.get_config() {
@@ -27,13 +26,7 @@ where
                 config = conf;
                 0
             }
-            Err(e) => {
-                tarantool::set_error!(
-                    TarantoolErrorCode::ProcC,
-                    "Failed to get configuration: {}",
-                    e.to_string()
-                )
-            }
+            Err(e) => tarantool_error(&format!("Failed to get configuration: {}", e)),
         }
     });
 
@@ -44,11 +37,10 @@ where
             let mut runtime = match runtime.try_borrow_mut() {
                 Ok(runtime) => runtime,
                 Err(e) => {
-                    return tarantool::set_error!(
-                        TarantoolErrorCode::ProcC,
+                    return tarantool_error(&format!(
                         "Failed to borrow the runtime while updating configuration: {}",
-                        e.to_string()
-                    );
+                        e
+                    ));
                 }
             };
             runtime.update_config(config);
diff --git a/src/api/invalidate_cached_schema.rs b/src/api/invalidate_cached_schema.rs
index 06175a46cc7bd6a7d4f30eb03648f1e586c06079..30515d7665d6559faece0d1fd55a7dac23c1e8dc 100644
--- a/src/api/invalidate_cached_schema.rs
+++ b/src/api/invalidate_cached_schema.rs
@@ -1,9 +1,9 @@
 use std::os::raw::c_int;
-use tarantool::error::TarantoolErrorCode;
 use tarantool::tuple::{FunctionArgs, FunctionCtx};
 
 use crate::api::{COORDINATOR_ENGINE, SEGMENT_ENGINE};
 use crate::executor::engine::{Configuration, Coordinator};
+use crate::log::tarantool_error;
 
 /// Flush cached configuration in the Rust memory of the coordinator runtime.
 /// This function should be invoked in the Lua cartridge application with `apply_config()`.
@@ -13,22 +13,18 @@ pub extern "C" fn invalidate_coordinator_cache(ctx: FunctionCtx, _: FunctionArgs
         Ok(mut runtime) => {
             runtime.clear_config();
             if let Err(e) = runtime.clear_ir_cache() {
-                return tarantool::set_error!(
-                    TarantoolErrorCode::ProcC,
+                return tarantool_error(&format!(
                     "Failed to clear the IR cache on router: {:?}",
                     e
-                );
+                ));
             }
             ctx.return_mp(&true).unwrap();
             0
         }
-        Err(e) => {
-            tarantool::set_error!(
-                TarantoolErrorCode::ProcC,
-                "Failed to borrow the runtime while clearing cached configuration on router: {}",
-                e.to_string()
-            )
-        }
+        Err(e) => tarantool_error(&format!(
+            "Failed to borrow the runtime while clearing cached configuration on router: {}",
+            e
+        )),
     })
 }
 
@@ -42,12 +38,9 @@ pub extern "C" fn invalidate_segment_cache(ctx: FunctionCtx, _: FunctionArgs) ->
             ctx.return_mp(&true).unwrap();
             0
         }
-        Err(e) => {
-            tarantool::set_error!(
-                TarantoolErrorCode::ProcC,
-                "Failed to borrow the runtime while clearing cached configuration on a storage: {}",
-                e.to_string()
-            )
-        }
+        Err(e) => tarantool_error(&format!(
+            "Failed to borrow the runtime while clearing cached configuration on a storage: {}",
+            e
+        )),
     })
 }
diff --git a/src/executor/engine/cartridge/backend/sql/ir.rs b/src/executor/engine/cartridge/backend/sql/ir.rs
index ff9e2ba3002bb09f88440a26d445d7a7d6847d63..b8c61d0b44a2710855d23c4f4ed97f4e8c43a179 100644
--- a/src/executor/engine/cartridge/backend/sql/ir.rs
+++ b/src/executor/engine/cartridge/backend/sql/ir.rs
@@ -2,10 +2,10 @@ use itertools::Itertools;
 use serde::{Deserialize, Deserializer, Serialize};
 use std::collections::HashMap;
 use std::fmt::Write as _;
-use tarantool::log::{say, SayLevel};
 use tarantool::tlua;
 use tarantool::tuple::{FunctionArgs, Tuple};
 
+use crate::debug;
 use crate::errors::QueryPlannerError;
 use crate::executor::bucket::Buckets;
 use crate::executor::ir::ExecutionPlan;
@@ -36,11 +36,8 @@ impl TryFrom<FunctionArgs> for PatternWithParams {
     type Error = QueryPlannerError;
 
     fn try_from(value: FunctionArgs) -> Result<Self, Self::Error> {
-        say(
-            SayLevel::Debug,
-            file!(),
-            line!().try_into().unwrap_or(0),
-            None,
+        debug!(
+            Option::from("argument parsing"),
             &format!("Query parameters: {:?}", value),
         );
         Tuple::from(value)
diff --git a/src/executor/engine/cartridge/config.rs b/src/executor/engine/cartridge/config.rs
index cd8616a4a214dfb3b8b06a98d6795bb46eb33c1e..5624e60cf1d1440a40ba2c11c1e172a5811fb989 100644
--- a/src/executor/engine/cartridge/config.rs
+++ b/src/executor/engine/cartridge/config.rs
@@ -10,9 +10,7 @@ use crate::executor::engine::{normalize_name_from_schema, normalize_name_from_sq
 use crate::executor::lru::DEFAULT_CAPACITY;
 use crate::executor::CoordinatorMetadata;
 use crate::ir::relation::{Column, ColumnRole, Table, Type};
-
-#[cfg(not(feature = "mock"))]
-use tarantool::log::{say, SayLevel};
+use crate::{debug, warn};
 
 /// Cluster metadata information
 ///
@@ -104,19 +102,13 @@ impl RouterConfiguration {
                                 None => return Err(QueryPlannerError::TypeNotImplemented),
                             };
                             let qualified_name = normalize_name_from_schema(name);
-                            #[cfg(not(feature = "mock"))]
-                            {
-                                say(
-                                    SayLevel::Debug,
-                                    file!(),
-                                    line!().try_into().unwrap_or(0),
-                                    Option::from("configuration parsing"),
-                                    &format!(
-                                        "Column's original name: {}, qualified name {}",
-                                        name, qualified_name
-                                    ),
-                                );
-                            }
+                            debug!(
+                                Option::from("configuration parsing"),
+                                &format!(
+                                    "Column's original name: {}, qualified name {}",
+                                    name, qualified_name
+                                ),
+                            );
                             let role = if self.get_sharding_column().eq(&qualified_name) {
                                 ColumnRole::Sharding
                             } else {
@@ -128,16 +120,10 @@ impl RouterConfiguration {
                         result
                     }
                     None => {
-                        #[cfg(not(feature = "mock"))]
-                        {
-                            say(
-                                SayLevel::Warn,
-                                file!(),
-                                line!().try_into().unwrap_or(0),
-                                Option::from("configuration parsing"),
-                                &format!("Skip space {}: fields not found.", current_space_name),
-                            );
-                        }
+                        warn!(
+                            Option::from("configuration parsing"),
+                            &format!("Skip space {}: fields not found.", current_space_name),
+                        );
                         continue;
                     }
                 };
@@ -149,19 +135,13 @@ impl RouterConfiguration {
                             let key: &str = match k.as_str() {
                                 Some(k) => k,
                                 None => {
-                                    #[cfg(not(feature = "mock"))]
-                                    {
-                                        say(
-                                            SayLevel::Warn,
-                                            file!(),
-                                            line!().try_into().unwrap_or(0),
-                                            Option::from("configuration parsing"),
-                                            &format!(
-                                                "Skip space {}: failed to convert key {:?} to string.",
-                                                current_space_name, k
-                                            ),
-                                        );
-                                    }
+                                    warn!(
+                                        Option::from("configuration parsing"),
+                                        &format!(
+                                            "Skip space {}: failed to convert key {:?} to string.",
+                                            current_space_name, k
+                                        ),
+                                    );
                                     continue;
                                 }
                             };
@@ -170,34 +150,22 @@ impl RouterConfiguration {
                         result
                     }
                     None => {
-                        #[cfg(not(feature = "mock"))]
-                        {
-                            say(
-                                SayLevel::Warn,
-                                file!(),
-                                line!().try_into().unwrap_or(0),
-                                Option::from("configuration parsing"),
-                                &format!("Skip space {}: keys not found.", current_space_name),
-                            );
-                        }
+                        warn!(
+                            Option::from("configuration parsing"),
+                            &format!("Skip space {}: keys not found.", current_space_name),
+                        );
                         continue;
                     }
                 };
 
                 let table_name: String = normalize_name_from_schema(current_space_name);
-                #[cfg(not(feature = "mock"))]
-                {
-                    say(
-                        SayLevel::Debug,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
-                        Option::from("configuration parsing"),
-                        &format!(
-                            "Table's original name: {}, qualified name {}",
-                            current_space_name, table_name
-                        ),
-                    );
-                }
+                debug!(
+                    Option::from("configuration parsing"),
+                    &format!(
+                        "Table's original name: {}, qualified name {}",
+                        current_space_name, table_name
+                    ),
+                );
                 let keys_str = keys.iter().map(String::as_str).collect::<Vec<&str>>();
                 let t = Table::new_seg(&table_name, fields, keys_str.as_slice())?;
                 self.tables.insert(table_name, t);
diff --git a/src/executor/engine/cartridge/router.rs b/src/executor/engine/cartridge/router.rs
index 66f17c82ea324ea3068eead868bf24695c50e0fa..96afa0657572270a116f3074cde0e5d845ec3418 100644
--- a/src/executor/engine/cartridge/router.rs
+++ b/src/executor/engine/cartridge/router.rs
@@ -7,10 +7,10 @@ use std::cell::RefCell;
 use std::collections::{HashMap, HashSet};
 use std::convert::TryInto;
 
-use tarantool::log::{say, SayLevel};
 use tarantool::tlua::LuaFunction;
 use tarantool::tuple::Tuple;
 
+use crate::error;
 use crate::errors::QueryPlannerError;
 use crate::executor::bucket::Buckets;
 use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
@@ -68,13 +68,7 @@ impl Configuration for RouterRuntime {
             let schema: String = match get_schema.call() {
                 Ok(res) => res,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
-                        Option::from("getting schema"),
-                        &format!("{:?}", e),
-                    );
+                    error!(Option::from("getting schema"), &format!("{:?}", e));
                     return Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)));
                 }
             };
@@ -84,10 +78,7 @@ impl Configuration for RouterRuntime {
             let jaeger_host: String = match jaeger_agent_host.call() {
                 Ok(res) => res,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
+                    error!(
                         Option::from("getting jaeger agent host"),
                         &format!("{:?}", e),
                     );
@@ -100,10 +91,7 @@ impl Configuration for RouterRuntime {
             let jaeger_port: u16 = match jaeger_agent_port.call() {
                 Ok(res) => res,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
+                    error!(
                         Option::from("getting jaeger agent port"),
                         &format!("{:?}", e),
                     );
@@ -115,13 +103,7 @@ impl Configuration for RouterRuntime {
             let timeout: u64 = match waiting_timeout.call() {
                 Ok(res) => res,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
-                        Option::from("getting waiting timeout"),
-                        &format!("{:?}", e),
-                    );
+                    error!(Option::from("getting waiting timeout"), &format!("{:?}", e));
                     return Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)));
                 }
             };
@@ -139,10 +121,7 @@ impl Configuration for RouterRuntime {
                     })?
                 }
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
+                    error!(
                         Option::from("getting router cache capacity"),
                         &format!("{:?}", e),
                     );
@@ -154,13 +133,7 @@ impl Configuration for RouterRuntime {
             let column: String = match sharding_column.call() {
                 Ok(column) => column,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
-                        Option::from("getting sharding column"),
-                        &format!("{:?}", e),
-                    );
+                    error!(Option::from("getting sharding column"), &format!("{:?}", e));
                     return Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)));
                 }
             };
@@ -348,13 +321,7 @@ impl RouterRuntime {
             match lua.eval("return require('vshard').router.bucket_count") {
                 Ok(v) => v,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
-                        Option::from("set_bucket_count"),
-                        &format!("{:?}", e),
-                    );
+                    error!(Option::from("set_bucket_count"), &format!("{:?}", e));
                     return Err(QueryPlannerError::LuaError(format!(
                         "Failed lua function load: {}",
                         e
@@ -365,13 +332,7 @@ impl RouterRuntime {
         let bucket_count: u64 = match bucket_count_fn.call() {
             Ok(r) => r,
             Err(e) => {
-                say(
-                    SayLevel::Error,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
-                    Option::from("set_bucket_count"),
-                    &format!("{:?}", e),
-                );
+                error!(Option::from("set_bucket_count"), &format!("{:?}", e));
                 return Err(QueryPlannerError::LuaError(e.to_string()));
             }
         };
@@ -402,13 +363,7 @@ impl RouterRuntime {
         match exec_sql.call_with_args::<Tuple, _>((rs_query, waiting_timeout)) {
             Ok(v) => Ok(Box::new(v)),
             Err(e) => {
-                say(
-                    SayLevel::Error,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
-                    Option::from("read_on_some"),
-                    &format!("{:?}", e),
-                );
+                error!(Option::from("read_on_some"), &format!("{:?}", e));
                 Err(QueryPlannerError::LuaError(format!(
                     "Lua error: {:?}. Query and parameters: {:?}",
                     e, rs_query
@@ -431,13 +386,7 @@ impl RouterRuntime {
         match exec_sql.call_with_args::<Tuple, _>((rs_query, waiting_timeout)) {
             Ok(v) => Ok(Box::new(v)),
             Err(e) => {
-                say(
-                    SayLevel::Error,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
-                    Option::from("write_on_some"),
-                    &format!("{:?}", e),
-                );
+                error!(Option::from("write_on_some"), &format!("{:?}", e));
                 Err(QueryPlannerError::LuaError(format!(
                     "Lua error: {:?}. Query and parameters: {:?}",
                     e, rs_query
@@ -470,13 +419,7 @@ impl RouterRuntime {
         match exec_sql.call_with_args::<Tuple, _>((query, waiting_timeout)) {
             Ok(v) => Ok(Box::new(v)),
             Err(e) => {
-                say(
-                    SayLevel::Error,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
-                    Option::from("read_on_all"),
-                    &format!("{:?}", e),
-                );
+                error!(Option::from("read_on_all"), &format!("{:?}", e));
                 Err(QueryPlannerError::LuaError(format!(
                     "Lua error: {:?}. Query and parameters: {:?}",
                     e, query
@@ -496,13 +439,7 @@ impl RouterRuntime {
         match exec_sql.call_with_args::<Tuple, _>((query, waiting_timeout)) {
             Ok(v) => Ok(Box::new(v)),
             Err(e) => {
-                say(
-                    SayLevel::Error,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
-                    Option::from("write_on_all"),
-                    &format!("{:?}", e),
-                );
+                error!(Option::from("write_on_all"), &format!("{:?}", e));
                 Err(QueryPlannerError::LuaError(format!(
                     "Lua error: {:?}. Query and parameters: {:?}",
                     e, query
@@ -549,13 +486,7 @@ impl Buckets {
         let res: GroupedBuckets = match fn_group.call_with_args(lua_buckets) {
             Ok(v) => v,
             Err(e) => {
-                say(
-                    SayLevel::Error,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
-                    Option::from("buckets group"),
-                    &format!("{:?}", e),
-                );
+                error!(Option::from("buckets group"), &format!("{:?}", e));
                 return Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)));
             }
         };
diff --git a/src/executor/engine/cartridge/storage.rs b/src/executor/engine/cartridge/storage.rs
index 0f87c523d3d684a8eba1a8e1620d35249f9e6863..1b6208951b1657dba175aac2fa9907e93d0a63b6 100644
--- a/src/executor/engine/cartridge/storage.rs
+++ b/src/executor/engine/cartridge/storage.rs
@@ -5,10 +5,10 @@ use crate::executor::engine::Configuration;
 use crate::executor::lru::{Cache, LRUCache, DEFAULT_CAPACITY};
 use crate::ir::value::Value;
 use crate::otm::child_span;
+use crate::{debug, error, warn};
 use sbroad_proc::otm_child_span;
 use std::any::Any;
 use std::cell::RefCell;
-use tarantool::log::{say, SayLevel};
 use tarantool::tlua::LuaFunction;
 use tarantool::tuple::Tuple;
 
@@ -78,10 +78,7 @@ impl Configuration for StorageRuntime {
             let capacity: u64 = match storage_cache_capacity.call() {
                 Ok(capacity) => capacity,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
+                    error!(
                         Option::from("getting storage cache capacity"),
                         &format!("{:?}", e),
                     );
@@ -96,10 +93,7 @@ impl Configuration for StorageRuntime {
             let cache_size_bytes = match storage_cache_size_bytes.call::<u64>() {
                 Ok(size_bytes) => size_bytes,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
+                    error!(
                         Option::from("getting storage cache size bytes"),
                         &format!("{:?}", e),
                     );
@@ -114,10 +108,7 @@ impl Configuration for StorageRuntime {
             let jaeger_host: String = match jaeger_agent_host.call() {
                 Ok(res) => res,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
+                    error!(
                         Option::from("getting jaeger agent host"),
                         &format!("{:?}", e),
                     );
@@ -130,10 +121,7 @@ impl Configuration for StorageRuntime {
             let jaeger_port: u16 = match jaeger_agent_port.call() {
                 Ok(res) => res,
                 Err(e) => {
-                    say(
-                        SayLevel::Error,
-                        file!(),
-                        line!().try_into().unwrap_or(0),
+                    error!(
                         Option::from("getting jaeger agent port"),
                         &format!("{:?}", e),
                     );
@@ -180,6 +168,7 @@ impl StorageRuntime {
     /// - Failed to prepare the statement (invalid SQL or lack of memory in `sql_cache_size`).
     /// - Failed to put or get a prepared statement from the cache.
     /// - Failed to execute the prepared statement.
+    #[allow(unused_variables)]
     pub fn execute(
         &self,
         pattern: &str,
@@ -191,10 +180,7 @@ impl StorageRuntime {
         let stmt_id = match prepare(pattern) {
             Ok(stmt) => {
                 let stmt_id = stmt.id()?;
-                say(
-                    SayLevel::Debug,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
+                debug!(
                     Option::from("execute"),
                     &format!("Created prepared statement {}", stmt_id),
                 );
@@ -213,10 +199,7 @@ impl StorageRuntime {
                 // Possibly the statement is correct, but doesn't fit into
                 // Tarantool's prepared statements cache (`sql_cache_size`).
                 // So we try to execute it bypassing the cache.
-                say(
-                    SayLevel::Warn,
-                    file!(),
-                    line!().try_into().unwrap_or(0),
+                warn!(
                     Option::from("execute"),
                     &format!("Failed to prepare the statement: {}, error: {}", pattern, e),
                 );
@@ -228,10 +211,7 @@ impl StorageRuntime {
         };
 
         // The statement was found in the cache, so we can execute it.
-        say(
-            SayLevel::Debug,
-            file!(),
-            line!().try_into().unwrap_or(0),
+        debug!(
             Option::from("execute"),
             &format!("Execute prepared statement {}", stmt_id),
         );
@@ -259,13 +239,7 @@ fn prepare(pattern: &str) -> Result<PreparedStmt, QueryPlannerError> {
             Ok(PreparedStmt(Some(stmt)))
         }
         Err(e) => {
-            say(
-                SayLevel::Error,
-                file!(),
-                line!().try_into().unwrap_or(0),
-                Option::from("prepare"),
-                &format!("{:?}", e),
-            );
+            error!(Option::from("prepare"), &format!("{:?}", e));
             Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)))
         }
     }
@@ -282,13 +256,7 @@ fn unprepare(stmt: &mut PreparedStmt) -> Result<(), QueryPlannerError> {
     match unprepare_stmt.call_with_args::<(), _>(stmt.id()?) {
         Ok(_) => Ok(()),
         Err(e) => {
-            say(
-                SayLevel::Error,
-                file!(),
-                line!().try_into().unwrap_or(0),
-                Option::from("unprepare"),
-                &format!("{:?}", e),
-            );
+            error!(Option::from("unprepare"), &format!("{:?}", e));
             Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)))
         }
     }
@@ -309,13 +277,7 @@ fn read_prepared(
     match exec_sql.call_with_args::<Tuple, _>((stmt_id, stmt, params)) {
         Ok(v) => Ok(Box::new(v) as Box<dyn Any>),
         Err(e) => {
-            say(
-                SayLevel::Error,
-                file!(),
-                line!().try_into().unwrap_or(0),
-                Option::from("read_prepared"),
-                &format!("{:?}", e),
-            );
+            error!(Option::from("read_prepared"), &format!("{:?}", e));
             Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)))
         }
     }
@@ -332,13 +294,7 @@ fn read_unprepared(stmt: &str, params: &[Value]) -> Result<Box<dyn Any>, QueryPl
     match exec_sql.call_with_args::<Tuple, _>((0, stmt, params)) {
         Ok(v) => Ok(Box::new(v) as Box<dyn Any>),
         Err(e) => {
-            say(
-                SayLevel::Error,
-                file!(),
-                line!().try_into().unwrap_or(0),
-                Option::from("read_unprepared"),
-                &format!("{:?}", e),
-            );
+            error!(Option::from("read_unprepared"), &format!("{:?}", e));
             Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)))
         }
     }
@@ -359,13 +315,7 @@ fn write_prepared(
     match exec_sql.call_with_args::<Tuple, _>((stmt_id, stmt, params)) {
         Ok(v) => Ok(Box::new(v) as Box<dyn Any>),
         Err(e) => {
-            say(
-                SayLevel::Error,
-                file!(),
-                line!().try_into().unwrap_or(0),
-                Option::from("write_prepared"),
-                &format!("{:?}", e),
-            );
+            error!(Option::from("write_prepared"), &format!("{:?}", e));
             Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)))
         }
     }
@@ -382,13 +332,7 @@ fn write_unprepared(stmt: &str, params: &[Value]) -> Result<Box<dyn Any>, QueryP
     match exec_sql.call_with_args::<Tuple, _>((0, stmt, params)) {
         Ok(v) => Ok(Box::new(v) as Box<dyn Any>),
         Err(e) => {
-            say(
-                SayLevel::Error,
-                file!(),
-                line!().try_into().unwrap_or(0),
-                Option::from("write_unprepared"),
-                &format!("{:?}", e),
-            );
+            error!(Option::from("write_unprepared"), &format!("{:?}", e));
             Err(QueryPlannerError::LuaError(format!("Lua error: {:?}", e)))
         }
     }
diff --git a/src/lib.rs b/src/lib.rs
index 052a4cded5934f0fbea5e01affe7759261ce2e69..ec79e1659d300863e6400fafb20c7c587d19b0a2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,4 +10,5 @@ pub mod errors;
 pub mod executor;
 pub mod frontend;
 pub mod ir;
+pub mod log;
 pub mod otm;
diff --git a/src/log.rs b/src/log.rs
new file mode 100644
index 0000000000000000000000000000000000000000..42b0d2a70c6e1f16b2742ab7d5bc1f12ccb30a26
--- /dev/null
+++ b/src/log.rs
@@ -0,0 +1,91 @@
+//! Logging module.
+
+use std::os::raw::c_int;
+
+#[macro_export]
+#[allow(unused_variables)]
+macro_rules! log {
+    ($level:ident, $error:expr, $($message:tt)*) => {{
+        let _line: i32 = line!().try_into().unwrap_or(0);
+        #[cfg(not(feature = "mock"))]
+        {
+            tarantool::log::say(
+                tarantool::log::SayLevel::$level,
+                file!(),
+                _line,
+                $error,
+                $($message)*
+            );
+        }
+    }};
+}
+
+#[macro_export]
+macro_rules! fatal {
+    ($error:expr, $($message:tt)*) => {
+        $crate::log!(Fatal, $error, $($message)*);
+    };
+}
+
+#[macro_export]
+macro_rules! system {
+    ($error:expr, $($message:tt)*) => {
+        $crate::log!(System, $error, $($message)*)
+    };
+}
+
+#[macro_export]
+macro_rules! error {
+    ($error:expr, $($message:tt)*) => {
+        $crate::log!(Error, $error, $($message)*)
+    };
+}
+
+#[macro_export]
+macro_rules! crit {
+    ($error:expr, $($message:tt)*) => {
+        $crate::log!(Crit, $error, $($message)*)
+    };
+}
+
+#[macro_export]
+macro_rules! warn {
+    ($error:expr, $($message:tt)*) => {
+        $crate::log!(Warn, $error, $($message)*)
+    };
+}
+
+#[macro_export]
+macro_rules! info {
+    ($error:expr, $($message:tt)*) => {
+        $crate::log!(Info, $error, $($message)*)
+    };
+}
+
+#[macro_export]
+macro_rules! verbose {
+    ($error:expr, $($message:tt)*) => {
+        $crate::log!(Verbose, $error, $($message)*)
+    };
+}
+
+#[macro_export]
+macro_rules! debug {
+    ($error:expr, $($message:tt)*) => {
+        $crate::log!(Debug, $error, $($message)*)
+    };
+}
+
+#[inline]
+#[allow(unreachable_code)]
+#[allow(unused_variables)]
+#[must_use]
+pub fn tarantool_error(message: &str) -> c_int {
+    #[cfg(not(feature = "mock"))]
+    {
+        use tarantool::error::TarantoolErrorCode;
+
+        return tarantool::set_error!(TarantoolErrorCode::ProcC, "{:?}", message);
+    }
+    -1
+}
diff --git a/src/otm.rs b/src/otm.rs
index 14aef48c595cce13f8ee74bd0e2f8e8c55585881..6f5b3fa94d979d353bf0ac32e1b97bb76f24202d 100644
--- a/src/otm.rs
+++ b/src/otm.rs
@@ -1,5 +1,7 @@
 //! Opentelemetry module
 
+use crate::debug;
+
 use ahash::AHashMap;
 use opentelemetry::global::{get_text_map_propagator, tracer, BoxedTracer};
 use opentelemetry::propagation::{Extractor, Injector};
@@ -141,16 +143,22 @@ where
             old_ti.context(),
         );
         let ti = TraceInfo::new(old_ti.tracer().clone(), ctx, id);
-        debug(&format!(
-            "fiber {}, child span {}: insert new trace info {:?}",
-            fid, name, ti
-        ));
+        debug!(
+            Option::from("child span"),
+            &format!(
+                "fiber {}, child span {}: insert new trace info {:?}",
+                fid, name, ti
+            ),
+        );
         TRACE_MANAGER.with(|tm| tm.borrow_mut().insert(fid, ti));
         let result = f();
-        debug(&format!(
-            "fiber {}, child span {}: restore old trace info {:?}",
-            fid, name, old_ti
-        ));
+        debug!(
+            Option::from("child span"),
+            &format!(
+                "fiber {}, child span {}: restore old trace info {:?}",
+                fid, name, old_ti
+            ),
+        );
         TRACE_MANAGER.with(|tm| tm.borrow_mut().insert(fid, old_ti));
         return result;
     }
@@ -194,16 +202,19 @@ where
         );
         let ti = TraceInfo::new(tracer, ctx, id.to_string());
 
-        debug(&format!(
-            "fiber {}, query span {}: insert trace info {:?}",
-            fid, name, ti
-        ));
+        debug!(
+            Option::from("query span"),
+            &format!(
+                "fiber {}, query span {}: insert trace info {:?}",
+                fid, name, ti
+            ),
+        );
         TRACE_MANAGER.with(|tm| tm.borrow_mut().insert(fid, ti));
         let result = f();
-        debug(&format!(
-            "fiber {}, query span {}: remove trace info",
-            fid, name
-        ));
+        debug!(
+            Option::from("query span"),
+            &format!("fiber {}, query span {}: remove trace info", fid, name),
+        );
         TRACE_MANAGER.with(|tm| tm.borrow_mut().remove(fid));
         return result;
     }
@@ -243,7 +254,10 @@ pub fn inject_context(carrier: &mut dyn Injector) {
         TRACE_MANAGER.with(|tm| {
             tm.borrow().get(fid).map_or_else(
                 || {
-                    debug(&format!("fiber {}, no trace information found", fid));
+                    debug!(
+                        Option::from("context injection"),
+                        &format!("fiber {}, no trace information found", fid),
+                    );
                 },
                 |ti| {
                     get_text_map_propagator(|propagator| {
@@ -284,29 +298,18 @@ pub fn extract_params<S: ::std::hash::BuildHasher>(
     };
     let id = if let Some(id) = id { id } else { new_id() };
     let ctx = if let Some(mut carrier) = context {
-        debug(&format!("Serialized OTM span context: {:?}", carrier));
+        debug!(
+            Option::from("parameters extraction"),
+            &format!("Serialized OTM span context: {:?}", carrier),
+        );
         let ctx = extract_context(&mut carrier);
-        debug(&format!("Deserialized OTM span context: {:?}", ctx.span()));
+        debug!(
+            Option::from("parameters extraction"),
+            &format!("Deserialized OTM span context: {:?}", ctx.span()),
+        );
         ctx
     } else {
         Context::new()
     };
     (id, ctx, tracer)
 }
-
-#[inline]
-#[allow(unused_variables)]
-fn debug(message: &str) {
-    #[cfg(not(feature = "mock"))]
-    {
-        use tarantool::log::{say, SayLevel};
-
-        say(
-            SayLevel::Debug,
-            file!(),
-            line!().try_into().unwrap_or(0),
-            None,
-            message,
-        );
-    }
-}